排序(一)--插入排序:直接插入排序、希尔排序

排序是一种常用的数据结构,当有人问我具体有哪些排序呢?我说有选择、冒泡、快排、希尔……说着说着自己就觉得乱乱的,还是来一个系统的总结吧!

一、排序分类
  • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
  • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
    这里写图片描述

注:由于排序种类很多,从本篇博客开始,下面几篇博客都将总结排序

二、直接插入排序(以升序为例)
1.算法思想

将要排序的优先序列分为两个区间,第一个区间为:有序序列区间;第二个区间为:无序序列区间。从无序序列中取一个元素,将它放入有序区间的合适的位置,然后重复此过程,直到第二个区间里面没有数据为止。

2.算法具体的步骤

(1)从第一个元素开始,该元素可以认为已经被排序
(2)取出下一个元素,在已经排序的元素序列中从后向前扫描

  • 如果已排序好的元素大于新元素,将该已排序好的元素移到下一位置
  • 重复此过程,直到找到已排序的元素小于或者等于新元素的位置
  • 将新元素插入到该位置中
    (3)重复上述步骤,直到第二个区间里面没有元素为止
3.图解举例

这里写图片描述

4.代码实现
//直接插入排序
void Insert(int* arr, int size)
{
    if (arr == NULL || size <= 0)
        return;

    for (int i = 1; i < size; ++i)
    {
        int end = arr[i];
        int j = i;
        while (j>0 && arr[j - 1] > end)
        {
            arr[j] = arr[j - 1];
            j--;
        }
        arr[j] = end;
    }
}
5.其他

(1)时间复杂度:O(n^2)

注:O(n^2)的这种情况是要排序的序列为降序时,这种情况是最差的一种情况。
当排序的序列是升序序列时,时间复杂度为O(1),这种情况是最好的一种情况。
我们在说时间复杂度时,要求考虑最差的一种情况!!!

(2)空间复杂度:O(1)
(3)稳定性:稳定
(4)使用场景:当要排序的序列数据较少,并且数据接近有序或者已经有序时。

6.动态图
三、二分插入排序

我们可以用二分查找来提高查找插入位置的位置,来对插入排序进行优化,简称二分插入排序。
代码实现如下:

//二分查找排序
void BinaryInsertSort(int* arr, int size)
{
    if (arr == NULL || size <= 0)
        return;

    for (int i = 1; i < size; ++i)
    {
        int left = 0;
        int right = i - 1;
        int end = arr[i];
        int mid = left + ((right - left) / 2);
        while (left <= right)
        {
            if (arr[i]>mid)
            {
                left = mid + 1;
            }
            else
            {
                right = mid - 1;
            }
        }

        for (int j = i - 1; j >= left; --j)
        {
            arr[j + 1] = arr[j];
        }
        arr[left] = end;
    }
}
四、希尔排序(递减增量排序算法)
1.算法思想

在对数据进行直接排序之前,先对数据进行预排序,使之接近有序,然后再利用直接插入排序对数据进行快速排序。
说明:从本质上来说,希尔排序是直接插入排序的一种优化,因为在直接排序算法中有可能出现最坏的一种情况,希尔排序就是为了防止这种情况。

总的来说,希尔排序是基于插入排序的以下两点性质而提出了改进方法:
(1)插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
(2)但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

2.算法具体的步骤

基于直接插入排序,步骤如下:
(1)现将待排序的序列分成若干个子序列(该子序列由相隔某个增量的元素组成),对每一个子序列进行直接排序;
(2)依次缩减增量,再进行直接排序;
(3)待整个序列基本有序时(即增量足够小),对整体再次进行直接插入排序。

说明:
上面我们说到了增量这个概念,通过大佬们的实践,该增量(gap)一般情况下是这样计算的:第一次以排序序列中的元素除以3加1得到,之后每一次的增量都是前面增量除以3加1,直到增量为1结束

3.图解举例

这里写图片描述

4.代码实现
//希尔排序

void ShellSort(int* arr, int size)
{
    if (arr == NULL || size <= 0)
        return;

    int gap = size;
    while (gap >= 0)
    {
        gap = gap / 3 + 1;

        for (int i = gap; i < size; i++)
        {
            int j = i - gap;
            int end = arr[i];

            if (arr[j]>arr[j+gap])
            {
                arr[j + gap] = arr[j];
            }
            else
            {
                break;
            }
            j -= gap;
            arr[j + gap] = end;
        }
    }
}
5.其他

(1)时间复杂度:希尔排序介于O(N)~O(N^2),希尔排序,当N大时,平均的时间复杂度,大约在N^1.25–1.6N^1.25之间。
(2)空间复杂度:O(1)。
(3)稳定性:不稳定。
(4)使用场景:希尔排序是对插入排序的一种优化,当数据元素比较多时,适合希尔排序。

6.动态图

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值