0


数据结构(初阶)—— 排序算法(上)

前言

   ** 排序在我们日常生活中很常见,如:打扑克牌、商品的销量、价格、好评度等都需要用到排序;排序目的在于让我们跟容易对事物一目了然;所以掌握好排序也是非常有必要的;接下来我们介绍排序算法的前两个:插入排序和选择排序;**

一、直接插入排序

**什么是直接插入排序? **

** 相信大家都完过扑克牌,摸得一手好牌(如果全是炸弹^-^)牌堪比人上人呀;我们从开始摸第一张牌起其后摸得所有牌,都需要插入到相应的位置,让自己手中的牌按升序或降序的排列方式组合起来;**

1.动图演示

对于一个随机组合的数组[ 6, 2, 10, 3, 9, 4, 8, 5, 1, 7 ] ,我们如何利用直接插入排序,将其排列成升序或降序的排列方式呢?

看过了动图,那么如何操作实现呢?

2.代码实现

// 插入排序
void InsertSort(int* a, int n)
{
    assert(a);
    for (int i = 0; i < n - 1; i++)
    {
        //将x插入【0,end】有序区间
        int end = i;
        int x = a[end + 1];
        while (end >= 0)
        {
            if (a[end] > x)
            {
                a[end + 1] = a[end];//将前一个数向后挪动
                --end;
            }
            else
            {
                break;
            }
        }
        a[end + 1] = x;
    }
}

二、希尔排序

    **希尔排序,顾名思义就是有一个叫希尔的大佬想出的排序算法;也被称为"****缩小增量排序****"它的本质还是插入排序,是在直接插入排序算法的基础上进行改进;他是如何一步一步改进的呢?**

1.希尔大佬来告诉你他是怎么想的?

2.如何实现单趟的分组预排(含动态演示)

*我们以间距(gap*)为3为例;来对这10个数据进行第一组的与排序; **

从上面的动图可以看出,本质思想还是直接插入;

void ShellSort(int* a, int n)
{ 
int gap = 3;
    for (int i = 0; i < n - gap; i+=gap)
    {
        int end = i;
        int x = a[end + gap];
        while (end >= 0)
        {
            if (a[end] > x)
            {
                a[end + gap] = a[end];
                end -= gap;
            }
            else
            {
                break;
            }
        }
        a[end + gap] = x;
    }
}

3. 多组预排序的方法(含动态演示)

方法1:

在单趟与排序的基础上外面套一层循环,一共gap组,那就循环gap次;

动图演示:

方法2:

在方法1的基础上做了一些改进,它不再是先排完第一组再排第二组,再排第三组;它让三组同时开始预排;

**动图演示: **

上述两种方法都是不错的,方法2并没有质的提升,但是在量上略微提升了一些;

代码实现

// 希尔排序
void ShellSort(int* a, int n)
{ 
    //多组一锅炖1
    int gap = 3;
    for (int j = 0; j < gap; j++)
    {
        for (int i = 0; i < n - gap; i+=gap)
        {
            int end = i;
            int x = a[end + gap];
            while (end >= 0)
            {
                if (a[end] > x)
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end + gap] = x;
        }
    }
    //多组一锅炖2
    int gap = 3;
    for (int i = 0; i < n - gap; ++i)
    {
        int end = i;
        int x = a[end + gap];
        while (end >= 0)
        {
            if (a[end] < x)
            {
                a[end + gap] = a[end];
                end -= gap;
            }
            else
            {
                break;
            }
        }
        a[end + gap] = x;
    }
}

4. 希尔排序实现

    **从本段第一点的介绍,希尔大佬提出先将数组以间距为3分为3组数据进行预排,再以间距为2将数组分为2组进行预排;最后再以间距为1(即直接插入排序)进行最后的一次排序;达到升序或降序的排列组合;** 
// 希尔排序
void ShellSort(int* a, int n)
{
    int gap = n;
    while (gap > 1)
    {
        //gap = gap / 2;
        gap = gap / 3 + 1;
        for (int i = 0; i < n - gap; i++)
        {
            int end = i;
            int x = a[end + gap];
            while (end >= 0)
            {
                if (a[end] > x)
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end + gap] = x;
        }
    }
}

阅读本段至此,我们可以发现一些问题?

1.gap越大,预排越快,预排后越不接近有序;

** **间距越大,意味着越小的数字能够很快的跳的前面来,越大的数字能很快的跳到后面去;虽然预排比较快,但是整体看不出来有序;
2.gap越小,预排越慢,预排后越接近有序;

** **** 间距越小,意味着它每部分的数据都接近于有序,整体相对有序,但是预排相对比较慢;**
3.gap == 1 ,就是直接插入排序;

** **当间距在发生变化时,不断进行预排,每次都在向有序靠近,知道最后间距变化为1时,就能将整个数组排列完成;

一般gap是如何确定呢?

** 起初,希尔大佬认为gap=n/2、gap=gap/2直到gap=1;后来Knuth提出取gap=gap/3+1.还有人认为取奇数为好,也有人提出取gap互质为好。无论哪一种主张都没有得到证明;小伙伴就可以以 gap=gap/2gap=gap/3+1 来取;需要强调的是 gap=gap/3+1**

这里为什么需要加1呢?当有N个数据时且N为偶数时,除到最后是,gap就为0了,我们需要最后一次达到gap=1,去进行直接插入排序,所以这里加1的目的就在于此;

三、选择排序

思路1:

** 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的 数据元素排完 。**

//选择排序1
void SelectSort(int* a, int n)
{
    int begin = 0;
    while (begin < n - 1)
    {
        int min = begin;
        for (int i = begin; i <= n - 1; i++)
        {
            if (a[i] < a[min])
            {
                min = i;//找最小值的下标
            }
        }
        Swap(&a[begin], &a[min]);
        ++begin;
    }
}

思路2:

** 思路1是每次选一个较小数(以升序为例)放到左边,直到全部排序完成;思路二的思想就是一次选两个数,小的放左边,大的放右边;**

** 注意点:**

如何解决呢?

此时最大值被换走了,我们可以修正一下max和min的位置;

// 选择排序2
void Swap(int* px, int* py)
{
    int tmp = *px;
    *px = *py;
    *py = tmp;
}
void SelectSort(int* a, int n)
{
    int begin = 0, end = n - 1;
    while (begin < end)
    {
        int mini = begin, maxi = begin;
        for (int i = begin; i <= end; i++)
        {
            if (a[i] < a[mini])
            {
                mini = i;//找最小值的下标
            }
            if (a[i] > a[maxi])
            {
                maxi = i;//找最大值的下标
            }
        }
        Swap(&a[begin], &a[mini]);
        //begin == maxi时,最大值被换走了,修正一下maxi的位置
        if (begin == maxi)
        {
            maxi = mini;
        }
        Swap(&a[end], &a[maxi]);
        ++begin;
        --end;
    }
}

四、堆排序

** 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。**

向下调整算法:

堆排序:

如果对堆的应用还不清楚的小伙伴可以去看这里:

链接:https://blog.csdn.net/sjsjnsjnn/article/details/124201028?spm=1001.2014.3001.5501

//向下调整函数(大堆(升序)用大于,小堆(降序)用小于)
void AdjustDown(int* a, int n, int parent)
{
    assert(a);
    int child = parent * 2 + 1;
    while (child < n)
    {
        //选出左右孩子中小的那个
        if (child + 1 < n && a[child + 1] > a[child])
        {
            ++child;
        }

        //如果小的孩子小于父亲,则交换,并继续向下调整
        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

// 堆排序
void HeapSort(int* a, int n)
{
    //倒着建堆
    for (int i = (n - 1 - 1) / 2; i >= 0; --i)
    {
        AdjustDown(a, n, i);
    }
    //O(N*logN)
    int end = n - 1;
    while (end > 0)
    {
        Swap(&a[0], &a[end]);
        AdjustDown(a, end, 0);
        --end;
    }
}
标签: c++ 数据结构 算法

本文转载自: https://blog.csdn.net/sjsjnsjnn/article/details/124300911
版权归原作者 霄沫凡 所有, 如有侵权,请联系我们删除。

“数据结构(初阶)&mdash;&mdash; 排序算法(上)”的评论:

还没有评论