数据结构:从插入排序到希尔排序
插入排序
算法思路
每次从无序表中取出第一个元素,将其插入到有序表中的适当位置,使有序表的长度不断加长,完成排序过程。
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),是一种不稳定的排序算法!