因为希尔排序是建立在插入排序之上的,所以我将这两个算法放在一起进行整理归纳。
插入排序是最简单最直观的排序算法了,它的依据是:遍历到第N个元素的时候前面的N-1个元素已经是排序好的了,那么就查找前面的N-1个元素把这第N个元素放在合适的位置,如此下去直到遍历完序列的元素为止.算法的复杂度也是简单的,排序第一个需要1的复杂度,排序第二个需要2的复杂度,因此整个的复杂度就是1 + 2 + 3 + ... + N = O(N ^ 2)的复杂度.
插入排序有许多种类其中最简单的是直接插入排序(Straight Insertion Sort),直接插入排序的作法是:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。直接插入排序属于稳定的排序,时间复杂性为o(n^2),空间复杂度为O(1)。
void InsertSort(int array[],int nLength) { int i,j; int temp; for(i=1;i<nLength;i++) { temp = array[i]; //存放原始数组在temp中 for(j=i ; j>0 && temp < array[j-1] ; j--) //新的数组和temp比较 { array[j]=array[j-1]; //元素右移 } array[j]=temp; //此时的j已经是空位的j } }
i=1开始做这个函数即是从第二个元素开始做外循环,第一趟循环:array[1]的值 一旦当前扫描到的array[1]的值小于array[0]那么把array[0]的值给array[1],即保证较小角标的值比较小,否则不改变值,第二趟:(此时array[0]<array[1])i=2即是第三个值存放到缓存temp中如果它比array[1]值小而比array[0]大的话内循环只做一次此时可以容易看出array[2]=array[1];同时array[1]=temp就相当于交换了array[1]和array[2]这和前提是吻合的,接下来如果它比array[0]都要小的话那它就是三个数中最小的容易看出内循环做两次那么就是array[2] = array[1] array[1] = array[0] (这就使得元素都像右移动一位)||再把最小的数temp放入array[0],同样的以此类推下面的循环中可以肯定的是上一次循环完成之后形成了一个有序的截止到i的数列,而内循环就是比较应该在这个数列中哪里插入这个数即是插在最后一个比它小的数后面,同时右移比它大的元素,因为插入前是有序的。
下面我给出一段完整的代码:
// 插入排序.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <IOSTREAM.H> void InsertSort(int array[],int n) { int i,j; int temp; for(i=1;i<n;i++) { temp = array[i]; //存放原始数组在temp中 for(j=i ; j>0 && temp < array[j-1] ; j--) //新的数组和temp比较 { array[j]=array[j-1]; //所有较大的元素都移动到了右边 } array[j]=temp; //此时的j已经是空位的j } } int main(int argc, char* argv[]) { int arr[]={1,5,2,4,3,8,6,7,9}; int count=sizeof(arr)/sizeof(int); InsertSort(arr,count); int k; for(k=0;k<count;k++) { cout<<arr[k]; } cout<<endl; return 0; }
那么明白了这一点希尔排序也就不再神奇了,
其实希尔排序主要是相当于一个辅助工具优化插入排序的算法,我本人觉得严蔚敏版的数据结构已经讲得很清楚了如图
令数列为a[n],它就是首先选取一个增量,在严蔚敏的书中选取的第一个增量为5那么第一个元素a[0]和第六个元素a[5]之间隔着五个角标,同样a[1]和a[6]也隔着五个角标以此类推,我们就能按照这个规律找到对应的元素直至全部匹配,接下来我们就将两两对应的数进行比较。这个过程是异常简单的,因为知道角标小的不比角标大的小那么交换值就行了。接下来增量取第一次增量的一半以此类推知道增量为1这样希尔的辅助排序工作就完成了。而最后一次排序就是我们的插入排序。
那么我现在展示一下希尔排序的辅助排序函数:
void ShellInsert(SqList &L,int dk) //对顺序表L进行一趟希尔插入排序dk为增量 { int i,j; for(i = dk + 1;i <= L.length;i++) { if LT(L.r[i].key,L.r[i-dk].key) { L.r[0] = L.r[i]; //当前记录暂存到L.r[0]中 for(j = i - dk;j > 0&& LT(L.r[0].key,L.r[j].key);j -= dk) { L.r[j+dk] = L.r[j]; } L.r[j+dk] = L.r[0]; //插入到相应位置 } } }
接下来给出一个完整实例代码 其中也有整个希尔排序过程的展示:
// 希尔排序.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream.h> #define LT(a,b)((a) < (b)) //对两个数值型关键字比较的约定 #define MAX_SIZE 20 //用作示例的小顺序表的最大长度 #define N 10 //记录数组长度 #define T 3 //增量序列数组长度 typedef int KeyType; typedef int InfoType; typedef struct //记录结构类型 { KeyType key; InfoType otherinfo; }RedType; typedef struct //顺序表结构类型 { RedType r[MAX_SIZE+1]; int length; }SqList; void PrintSqList(SqList L) //打印顺序表 { int i; for(i = 1;i <= L.length;i++) { cout<<L.r[i].key<<","<<L.r[i].otherinfo<<" "; } cout<<endl; } void PrintSqListKey(SqList L) //输出顺序表L的关键字 { int i; for(i = 1;i <= L.length;i++) { cout<<L.r[i].key; cout<<" "; } cout<<endl; } void ShellInsert(SqList &L,int dk) //对顺序表L进行一趟希尔插入排序 { int i,j; for(i = dk + 1;i <= L.length;i++) { if LT(L.r[i].key,L.r[i-dk].key) { L.r[0] = L.r[i]; //当前记录暂存到L.r[0]中 for(j = i - dk;j > 0&& LT(L.r[0].key,L.r[j].key);j -= dk) { L.r[j+dk] = L.r[j]; } L.r[j+dk] = L.r[0]; //插入到相应位置 } } } void ShellSort(SqList &L,int dlta[],int t) //按增量序列dlta[0,...,t-1]对顺序表L进行希尔排序 { int k; for(k=0;k<t;k++) //对所有的增量序列 { ShellInsert(L,dlta[k]); //一趟增量为dlat[k]的希尔插入排序 cout<<"data["<<k<<"]"<<"="<<dlta[k]<<",第"<<k+1<<"趟排序结果(仅输出关键字):"; PrintSqListKey(L); } } void main() { RedType d[N] = {{49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8},{55,9},{4,10}}; //记录数组 SqList m; //顺序表 int i,dt[T] = {5,3,1}; //增量序列数组(从大到小,最后一项的值必为1) for(i=0;i<N;i++) { m.r[i+1] = d[i]; } m.length = N; cout<<"希尔排序前:"<<endl; PrintSqList(m); cout<<endl; ShellSort(m,dt,T); //按增量序列dt[0,...,T-1]对顺序表作希尔排序 cout<<"希尔排序后:"<<endl; PrintSqList(m); cout<<endl; return; }