数据结构:从插入排序到希尔排序
插入排序
算法思路
每次从无序表中取出第一个元素,将其插入到有序表中的适当位置,使有序表的长度不断加长,完成排序过程。
n个待排序的元素由一个有序表和一个无序表组成,开始时有序表中只包含一个元素。
流程演示
蓝色表示由有序表,黑色表示无序表!
分析
元素基本有序时,直接插入排序的时间复杂度接近于O(n)
元素数目n较少时,直接插入排序的效率较高
数据结构定义
首先我们要构建一个顺序表来存放待排序的元素!这是我排序的基础,我的所有的排序算法都会依赖于这个简单的顺序表!
typedef int KeyType; // 定义关键字类型为整型 typedef int InfoType; typedef struct { KeyType key; // 关键字项 InfoType otherinfo; // 其他数据项 }RedType; // 记录类型 typedef struct { RedType r[MAXSIZE+1]; // r[0]闲置或用作哨兵 int length; // 顺序表长度 }SqList;
编写了两个方法可以对顺序表进行初始化和遍历
void initSqList(SqList & S) { int t,count=0; while(scanf("%d",&t)!=0) { count++; S.r[count].key=t; } S.length=count; } void traverseSqList(SqList S) { for(int i=1;i<=S.length;i++) printf("%d",S.r[i].key); }
优化的直接插入排序
我们在这里采用了优化,我们进行的是元素的移动和覆盖,而不仅仅是简单的交换。
void sortByDirectlyInsert(SqList &L) { for(int i = 2; i <= L.length; i++) //第一个元素默认有序,所以直接从2开始 if (L.r[i].key < L.r[i-1].key) { //这里进行了优化,如果i >i-1,说明从1~i这一段都是有序的,我们不需要进行后续操作 L.r[0] = L.r[i]; L.r[i] = L.r[i-1]; int j; for(j = i - 2;L.r[0].key < L.r[j].key; j--) L.r[j+1] = L.r[j]; L.r[j+1] = L.r[0]; }//if }
思路举例:
[ ]5 9 6 3 8 //待排序元素
[ ]5 9 //到这里是有序的
[ ]5 9 6 //到这里发现6小于9
[6 ]5 9 空 //将6移到监视哨,把它的位置空出来
[6 ]5 空 9 //让9来到原来6的位置,把9的位置空出来,看看6能不能填在那里
[6 ]5 6 9 //6可以填在那里,让空的位置等于监视哨
[]5 6 9 3
[3 ]5 6 9 空
[3 ]5 6 空 9
[3 ]5 空 6 9
[3 ]空 5 6 9
[3 ]3 5 6 9
[]3 5 6 9 8
[8 ]3 5 6 9 空
[8 ]3 5 6 空 9
[8 ]3 5 6 8 9
[]3 5 6 8 9
进一步优化版的插入排序
我们这样想,既然左半部分是是有序的,我们在有序的数组中找到插入位置,最好的方法非二分查找莫属。
void sortByBinaryDirectlyInsert(SqList &L) { for(int i=2;i <= L.length; i++) { L.r[0] = L.r[i]; /* 保存待插入元素 */ int low = 1,high = i-1; /* 设置初始区间 */ while(low <= high) /* 确定插入位置 */ { int mid = (low+high)/2; if (L.r[0].key > L.r[mid].key) low = mid+1; /* 插入位置在高半区中 */ else high = mid-1; /* 插入位置在低半区中 */ }/* while */ for(int j = i - 1; j >= high +1; j--) /* high+1为插入位置 */ L.r[j+1] = L.r[j]; /* 后移元素,留出插入空位 */ L.r[high+1] = L.r[0]; /* 将元素插入 */ }/* for */ }
希尔排序
算法思想
先将整个待排序元素序列分割成若干个小序列,在小序列内插入排序;
逐渐扩大小序列的长度(序列个数减少),使得整个序列基本有序;
最后再对全体元素进行一次直接插入排序。
代码实现
void ShellInsert(SqList &L,int dk) {//对顺序表L作一趟希尔排序 for(int i =dk+1; i <= L.length; i++) if (L.r[i].key < L.r[i-dk].key) { L.r[0] = L.r[i]; int j; for(j = i - dk; j>0 &&L.r[0].key < L.r[j].key; j -= dk) L.r[j+dk] = L.r[j]; L.r[j+dk] = L.r[0]; }//if } //ShellInsert void ShellSort(SqList &L,int dlta[],int t) { //按增量序列dlta[0..t-1]进行希尔排序 for(int k = 0; k < t; k++) ShellInsert(L,dlta[k]); //一趟增量为dlta[k]的希尔排序 } //ShellSort
说明:
1.希尔排序的实质是插入排序!希尔排序的分析是一个复杂的问题,增量序列的设置是关键,尚没有正式的依据说明何种设置最为合理!
2.空间复杂度为O(1),是一种不稳定的排序算法!