赞
踩
基本思想:将数据分成若干个序列,然后对每个序列分别进行直接插入排序,每个序列由两两相邻x位置组成,该x称为每次切割的增量。接着减少增量x,再对每个序列进行直接插入排序,当增量x足够小时,则说明此时序列基本有序,此时再对整个序列进行最后一次插入排序,由于此时序列基本有序,所以插入排序的效率是非常高的。所以希尔排序在时间效率上比直接插入排序有较大提高。
通过上面分析我们发现希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1)插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。(希尔排序每次排序后,整个序列比之前变得更有序)
2)插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。(希尔排序每次对若干序列进行排序)。
增量的选择:在每趟的排序过程都有一个增量,至少满足 增量关系 dx[1] > dx[2] > dx[3] >…> dx[t] = 1 (t趟排序);根据增量序列的选取其时间复杂度也会有变化,本文采用增量为n/2,以此递推,每次增量为原先的1/2,直到增量为1。
对序列【3,1,5,6,4,8,2】进行排序。
(1)第一步,dx=length/2=3时,
得到三组序列
1)3,6,2
2)1,4
3)5,8
对每组序列分别进行插入排序
1)2,3,6
2)1,4
3)5,8
(1)第二步,dx=length/2=1时,
得到一组序列
2,1,5,3,4,8,6,
对该序列进行排序:
1,2,3,4,5,6,8。
排序结束。
希尔排序中对于增量序列的选择十分重要,直接影响到希尔排序的性能。我们上面选择的增量序列{n/2,(n/2)/2…1},其最坏时间复杂度依然为O(n2),一些经过优化的增量序列如Hibbard经过复杂证明可使得最坏时间复杂度为O(n^(3/2)),希尔排序没有快速排序算法快 O(n(logn)),因此中等大小规模表现良好,对规模非常大的数据排序不是最优选择。但是比O(n^2)复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。 此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。
对于希尔排序以及其它高效算法之所以高效得原因,我觉得有一位博主会打的很好,这里引用一下:
希尔能突破O(N2)的界线,可以用逆序数来理解,假设我们要从小到大排序,一个数组中取两个元素如果前面比后面大,则为一个逆序,容易看出排序的本质就是消除逆序数,可以证明对于随机数组,逆序数是O(N2)的,而如果采用“交换相邻元素”的办法来消除逆序,每次正好只消除一个,因此必须执行O(N^2)的交换次数,这就是为啥冒泡、插入等算法只能到平方级别的原因,反过来,基于交换元素的排序要想突破这个下界,必须执行一些比较,交换相隔比较远的元素,使得一次交换能消除一个以上的逆序,希尔、快排、堆排等等算法都是交换比较远的元素,只不过规则各不同罢了。
#include <iostream> #include <vector> using namespace std; void XiEr(vector<int> arr) { int len = arr.size(); for (int bu = len/2; bu >= 1; bu/=2) { for (int i = bu; i < len; i++) { int target = arr[i]; int k = i-bu; //插入的值小于前面的值 while (k>0&& target<arr[k]) { arr[k + bu] = arr[k];//移位 k -= bu; } arr[k + bu] = target;//放入插入值 } } for (int i = 0; i < len; i++) { cout << arr[i] << " "; } } int main() { vector<int> arr = { 1,3,5,4,3 }; XiEr(arr); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。