希尔排序
我对希尔排序进行学习的博客:希尔排序–简单易懂图解
前言
可以说是一个加强版的插入排序。在插入排序中,最好的时间复杂度是可以说是极其舒服的线性时间复杂度,而这最好的情况,就是当数组中所有的数都是有序的时候,这是的复杂度。或者稍微比较好的情况,就是数组中大部分数都是有序的时候,那么,插入排序的时间复杂度,将趋近于,也就是线性的复杂度。而希尔排序的思想本质就是让数组中的数在尽可能少的次数内,变得有序起来,从而缩短整体时间复杂度,当然这思想没有脱离插入排序的基本做法,所以希尔排序的最坏时间复杂度,依然是,但在平均的情况下,希尔排序的时间复杂度将会是挺可观的。
基本做法
对数组进行分组(逻辑上的分组),然后对每个分组进行插入排序,使分组中的数变得有序,因为被分组后,每个分组中的数都不是很多,所以单次分组后的插入排序相对来说不会有过高的时间复杂度。然后经过多次分组然后执行插入排序后,数组中的数在整体上的有序程度会逐步拔高,直到最后一次对整体的插入排序,时间复杂度基本趋近于线性。而这个分组的依据就很关键了,在希尔排序中使用的则是与计算机紧密相关的数2
。
例:长度为8的数组
每次对数组(整除2
所得到的间隔值(gap))进行分组,然后对每个分组进行插入排序,因为最后间距(gap)必定是1,所以,前面的可以说是利用数字2
的特性,进行的数组有序程度的提高,然后最后进行一个时间复杂度相对低的整体插入排序。最终达到整体时间复杂度的降低。
第一次分组:
第二次分组:
第三次分组:
代码构造思路
注:下面所说的分组
和有序数组
都是逻辑上的。
首先最外层的循环,自然是要套上每次的分组依据,也就是gap
值的循环for (int gap = len >> 1; gap > 0; gap >>= 1)
。数组中的每个数相隔gap
个位置为一组。其次的第二层循环则是要循环上当前需要排序的整个数组for (int i = gap; i < len; ++i)
,而第二层循环从gap
开始,则是因为,数组中gap
值前面位置的数在各自分组中都是第一个数,而gap
值的位置开始到最后的数,一开始都还没有插入到各自的有序分组之中,所以每个分组,当前只有一个数,而分组中只有一个数,自然这个分组当前是有序的。最后的insertI(nums, gap, i)
,其实就跟插入排序差不了多少,把当前数插入到有序数组中正确的位置即可。
代码
class Solution {
public:
Solution() {}
~Solution() {}
void insertI(vector<int>& nums, int gap, int i) {
int insertNum = nums[i];
int j;
for (j = i - gap; j >= 0 && insertNum < nums[j]; j -= gap) {
nums[j + gap] = nums[j];
}
nums[j + gap] = insertNum;
}
vector<int> sortArray(vector<int>& nums) {
int len = nums.size();
for (int gap = len >> 1; gap > 0; gap >>= 1) {
for (int i = gap; i < len; ++i) {
insertI(nums, gap, i);
}
}
return nums;
}
};
时间复杂度
平均情况 | 最好情况 | 最坏情况 |
---|---|---|
注:这平均情况的时间复杂度不太好算。
空间复杂度
辅助存储 |
---|
PS:这个算法很明显是不稳定的
来源:CSDN
作者:GenmCai
链接:https://blog.csdn.net/qq_36296888/article/details/103751407