1、希尔排序:(Shell`s Sort)
思想:
- 将待排序的表分成若干组
- 每组内进行直接插排,使整个序列基本有序
- 然后再对整个表进行更加细化的分组直接插排,始之基本有序
- 直至无法细化分组最终有序
分组方法:
- 对给定的一个步长d(d>0),将下标相差为d的倍数的元素分在一组。
- 希尔排序算法经典的d的取值依次为:
d1=n/2, d2=d1/2, ……,dk=1
(直至步长为1,不可再分组,其中n为表长,)
—— 即不同步长进行分组,各个分组内进行”直接插入排序”,直至不能再分
语言很苍白,用图例来解决
例:数组Arr[] = {100, 8, 20, 16, 14, 7, 105,50, 78, 9}
- Step1 : 步长d1为5,相同颜色的为一组,比如第一组为”100 & 7”,每个组进行直接插排,局部有序
- Step2 : 第一轮结束,取步长为d2 = d1/2 , Arr[0] , Arr[2], Arr[4],Arr[6],Arr[7]相同颜色为一组;每组进行直接插排,局部有序
Step3 : 取步长为 d3 = d2 /2 ,为1,无法再分,对整个数组进行直接插排,整体有序
[注] 当步长 dk=1时,其实就是对最后局部有序的数组进行直接插入排序。
希尔排序算法及分析
- 稳定性:不稳定排序
- 空间性能:1个辅助空间。
- 时间性能: 与数据表初始状态关系不大:
需要循环log2n趟,每趟O(n)
因而时间复杂度为O(nlog2n)
代码:
#include <iostream>
using namespace std;
void Shell_sort(int *pArr, int len)
{
int d = len / 2;
while(d > 0)
{
for(int k = 0; k < d; k++) // k : 每一个步长对应的分组数,依次对每个分组操作
{
//对每一组进行 Straight_insert_Sort
for(int i = d + k; i<len; i=i+d)
{
int temp = pArr[i];
int j = i - d;
while( j >= 0 && (pArr[j] > temp))
{
pArr[j+d] = pArr[j];
j-=d;
}
pArr[j+d] = temp;
}
}
d = d / 2;
}
return;
}
/*
Shell_sort2 的代码实现与上图中的示例图分析理解上可能有差别,但是实现的结果是相同的;
Shell_sort 是先根据步长将组分好,然后将第一个组中进行直接插排,再将第二个组进行直接插排,依次类推.
Shell_sort2 是:
组1的第二个元素与组1第一个元素直接插排,组2的第二个元素与组2第一个元素直接插排...组k的第二个元素与组k第一个元素直接插排;
组1的第三个元素与组1前两个元素直接插排,组2的第三个元素与组2前两个元素直接插排...组k的第二个元素与组k的前两个元素直接插排
依次类推,可以看出并没有严格先将某一组按照直接插排排好,再进行下一组的整体直接插排!!!
单步一下你就知道。2代码更简洁
*/
void Shell_sort2(int *pArr, int len)
{
int d = len / 2;
while(d>0)
{
for(int i = d; i < len; i++) //单步走一下就很清楚
{
int temp = pArr[i];
int j = i-d;
while(j>=0 && (pArr[j] > temp))
{
pArr[j+d] = pArr[j];
j = j-d;
}
pArr[j+d] = temp;
} //对给定的d,在每一组内插入排序
d = d/2;
}
return;
}
int main()
{
int a[]={100,8,20,16,14,7,105,50,78,9};
int len = 10;
Shell_sort(a, len);
for(int i = 0; i < len; i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
最后的大白话:
Shell Sort 根据步长 d(分组)进行直接插入排序。