希尔排序(缩小增量排序)(插入排序的优化版) C++代码实现及算法分析 恋上数据结构笔记

复习概要

  1. 算法思想与流程
  2. 分这么多组分别插入排序有什么用?为什么不直接整个插入排序?
  3. 时间复杂度
  4. 优化
  5. 要眼熟插入排序那一段的代码,和原本的插入排序对比着看!
  6. 插入排序那一段多了一层循环是干什么的,怎么是三层循环?
  7. 希尔的步长序列生成方式?

算法思想

  1. 我觉得完全没必要按照行和列的思想去理解希尔排序
  2. 因为一句话概括希尔排序就是,每隔几个元素一组,分组进行插入排序,然后重新分组,直到每个元素都是一组
  3. 内层先标记这是第几组,然后就是最简单的插入排序思想,只不过是对同一组(列)里的元素的插入排序,请想象元素之间的距离变成stepSequence了,不是1了,而且组的第一个元素索引是col(第几组,初始为0)不是0了,第一个无序元素的位置就是col+stepSequence了,把所有组分别进行插入排序就结束了
  4. 外层就是不断缩小分组所需的步长(元素增量),然后循环内层
  5. 步长:就是隔几个一组
  6. 建议和普通插入排序的代码结合着看,很容易就理解了
  7. 话说希尔排序居然是用发明者名字命名的
  8. 希尔排序比之间插入排序强的地方是,总共处理的逆序对数量比直接插入排序少了,每次分组排序同时会解决一些额外的逆序对。

算法流程

输入数组:
29 15 12 10 9 8 7 6 5 3 2 1 
希尔排序正常版
(每隔几个元素就取出一个看作一组)(也就是列)步长为6,每组有length/步长 个元素
[29] 15 12 10 9 8 [7] 6 5 3 2 1 
7 [15] 12 10 9 8 29 [6] 5 3 2 1
7 6 [12] 10 9 8 29 15 [5] 3 2 1
7 6 5 [10] 9 8 29 15 12 [3] 2 1
7 6 5 3 [9] 8 29 15 12 10 [2] 1
7 6 5 3 2 [8] 29 15 12 10 9 [1]
7 6 5 3 2 1 29 15 12 10 9 8
(每隔几个元素就取出一个看作一组)(也就是列)步长为3
[7] 6 5 [3] 2 1 [29] 15 12 [10] 9 8
3 [15] 12 7 [9] 8 10 [6] 5 29 [2] 1
3 2 [12] 7 6 [8] 10 9 [5] 29 15 [1]
3 2 1 7 6 5 10 9 8 29 15 12
(每隔几个元素就取出一个看作一组)(也就是列)步长为1
[3] [2] [1] [7] [6] [5] [10] [9] [8] [29] [15] [12]
1 2 3 5 6 7 8 9 10 12 15 29

算法复杂度分析及稳定性

  1. 希尔排序最好时间复杂度和插入排序相同为O(n)

  2. 最坏时间复杂度平均时间复杂度取决于步长序列

  3. 希尔本人的步长序列生成法(length/2的k次幂)最坏时间复杂度为O(n2),目前最好的最坏时间复杂度的步长序列生成法可以做到O(n的4/3次方),如下,k=1,2,3.。。。注意是最坏时间复杂度(even代表奇数,odd代表偶数)
    4.

  4. 希尔排序不稳定,空间复杂度O(1),为原地算法

希尔排序代码正常版

//产生步长序列(按照希尔本人的生成方式)
void stepSequenceGenerator(vector<int> array, vector<int> &stepSequence)
{
    for (int i = 2; i < array.size(); i = i * 2)
    {
        stepSequence.push_back(array.size() / i);
    }
}

void sort(vector<int> array, int stepSequence)
{
    //col:当前是第几组(列)
    for (int col = 0; col < stepSequence; col++)
    {
        //下面其实就是最简单的插入排序的代码,只不过是对同一组(列)里的元素的插入排序
        //请想象元素之间的距离变成stepSequence了,不是1了,而且组的第一个元素索引是col不是0了,第一个无序元素的位置就是col+stepSequence了
        for (int chaosBeginIndex = col + stepSequence; chaosBeginIndex < array.size(); chaosBeginIndex = chaosBeginIndex + stepSequence)
        {
            for (int insertEIndex = chaosBeginIndex; insertEIndex > col; insertEIndex = insertEIndex - stepSequence)
            {
                if (array[insertEIndex] < array[insertEIndex - stepSequence])
                {
                    int temp = array[insertEIndex];
                    array[insertEIndex] = array[insertEIndex - stepSequence];
                    array[insertEIndex - stepSequence] = temp;
                }
            }
        }
        vectorPrint(array);
    }
}

void shellSort(vector<int> &array)
{
    vector<int> stepSequence;
    stepSequenceGenerator(array, stepSequence);
    for (int i = 0; i < stepSequence.size(); i++)
    {
        cout << "(每隔几个元素就取出一个看作一组)(也就是列)步长为" << stepSequence[i] << endl;
        sort(array, stepSequence[i]);
    }
}
输入数组:
29 15 12 10 9 8 7 6 5 3 2 1 
希尔排序正常版
(每隔几个元素就取出一个看作一组)(也就是列)步长为6
7 15 12 10 9 8 29 6 5 3 2 1
7 6 12 10 9 8 29 15 5 3 2 1
7 6 5 10 9 8 29 15 12 3 2 1
7 6 5 3 9 8 29 15 12 10 2 1
7 6 5 3 2 8 29 15 12 10 9 1
7 6 5 3 2 1 29 15 12 10 9 8
(每隔几个元素就取出一个看作一组)(也就是列)步长为3
3 15 12 7 9 8 10 6 5 29 2 1
3 2 12 7 6 8 10 9 5 29 15 1
3 2 1 7 6 5 10 9 8 29 15 12
(每隔几个元素就取出一个看作一组)(也就是列)步长为1
1 2 3 5 6 7 8 9 10 12 15 29
算法用时:(微秒)
[AlgoTime: 12002 us]

希尔排序与插入排序代码对比

void sort2nd(vector<int> array, int stepSequence)
{
    //col:当前是第几组(列)
    for (int col = 0; col < stepSequence; col++)
    {
        //下面其实就是最简单的插入排序的代码,只不过是对同一组(列)里的元素的插入排序
        //请想象元素之间的距离变成stepSequence了,不是1了,而且组的第一个元素索引是col不是0了,第一个无序元素的位置就是col+stepSequence了
        for (int chaosBeginIndex = col + stepSequence; chaosBeginIndex < array.size(); chaosBeginIndex = chaosBeginIndex + stepSequence)
        {
            for (int insertEIndex = chaosBeginIndex; insertEIndex > col; insertEIndex = insertEIndex - stepSequence)
            {
                if (array[insertEIndex] < array[insertEIndex - stepSequence])
                {
                    int temp = array[insertEIndex];
                    array[insertEIndex] = array[insertEIndex - stepSequence];
                    array[insertEIndex - stepSequence] = temp;
                }
            }
        }
        vectorPrint(array);
    }
}

void insertionSort1th(vector<int> &array) //这里以数组左侧作为有序的那一边
{
    // chaosBeginIndex:未排序序列的起始元素下标
    // insertEIndex:拿去插入有序序列的那个元素的下标,就是未排序序列的起始元素,但随着交换位置,下标会改变
    for (int chaosBeginIndex = 1; chaosBeginIndex < array.size(); chaosBeginIndex++)
    //外循环控制从第二个元素开始插入到有序序列里,直到最后一个元素
    {
        for (int insertEIndex = chaosBeginIndex; insertEIndex > 0; insertEIndex--)
        //内循环控制从未排序序列的首元素开始,逐渐与有序序列元素比较和交换位置,找到有序序列中的合适位置
        {
            if (array[insertEIndex] < array[insertEIndex - 1])
            {
                int temp = array[insertEIndex];
                array[insertEIndex] = array[insertEIndex - 1];
                array[insertEIndex - 1] = temp;
            }
            else
            {
                break;
            }
        }
        vectorPrint(array);
    }
}

希尔排序个人青春版(别看以免走上歧途)

看输出就能看出来,我改写了插入排序,可以自由选择一段进行插入排序,然后又建了一个空容器,把元素按组排序压入容器,然后覆盖主数组,但是毫无意义,看输出就知道,把一组元素放到一起又会导致不同组之间产生新的逆序对

void insertionSort1th(vector<int> &array, int begin, int end) //这里以数组左侧作为有序的那一边
{
    // chaosBeginIndex:未排序序列的起始元素下标
    // insertEIndex:拿去插入有序序列的那个元素的下标,就是未排序序列的起始元素,但随着交换位置,下标会改变
    for (int chaosBeginIndex = begin + 1; chaosBeginIndex <= end; chaosBeginIndex++)
    //外循环控制从第二个元素开始插入到有序序列里,直到最后一个元素
    {
        for (int insertEIndex = chaosBeginIndex; insertEIndex > begin; insertEIndex--)
        //内循环控制从未排序序列的首元素开始,逐渐与有序序列元素比较和交换位置,找到有序序列中的合适位置
        {
            if (array[insertEIndex] < array[insertEIndex - 1])
            {
                int temp = array[insertEIndex];
                array[insertEIndex] = array[insertEIndex - 1];
                array[insertEIndex - 1] = temp;
            }
            else
            {
                break;
            }
        }
    }
}

void sort(vector<int> &array, int stepSequence)
{
    vector<int> temp;
    for (int j = 0; j < stepSequence; j++)
    {
        int i = 0;
        for (int index = j; index < array.size(); index = index + stepSequence)
        {
            temp.push_back(array[index]);
            i = i + 1;
        }
        insertionSort1th(temp, temp.size() - i, temp.size() - 1);
        vectorPrint(temp);
    }
    array = temp;
}

void shellSort(vector<int> &array)
{
    vector<int> stepSequence;
    stepSequenceGenerator(array, stepSequence);
    for (int i = 0; i < stepSequence.size(); i++)
    {
        cout << "(每隔几个元素就取出一个看作一组)(也就是列)步长为" << stepSequence[i] << endl;
        sort(array, stepSequence[i]);
    }
}
输入数组:
29 15 12 10 9 8 7 6 5 3 2 1
希尔排序个人青春版
(每隔几个元素就取出一个看作一组)(也就是列)步长为6
7 29
7 29 6 15
7 29 6 15 5 12
7 29 6 15 5 12 3 10
7 29 6 15 5 12 3 10 2 9
7 29 6 15 5 12 3 10 2 9 1 8
(每隔几个元素就取出一个看作一组)(也就是列)步长为3
3 7 9 15
3 7 9 15 1 5 10 29
3 7 9 15 1 5 10 29 2 6 8 12
(每隔几个元素就取出一个看作一组)(也就是列)步长为1
1 2 3 5 6 7 8 9 10 12 15 29
算法用时:(微秒)
[AlgoTime: 9002 us]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值