在玩扑克牌时,我们在抓取下一张牌时,总会插入到我们的手牌中合适的位置,让我们的手牌保持从大到小的顺序,这就是插入排序.
每步将一个待排序的对象,按其关键码的大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止.
即边插入边排序,保证子序列中随时都是排好序的.
根据找到插入位置的不同方法,我们可以将插入排序分为:
顺序法定位插入位置------ 直接插入排序
二分法定位插入位置------ 二分插入排序
缩小增量多遍插入排序------ 希尔排序
x为记录需要插入的元素,而需要插入的元素存储在a[i], 则:
x=a[i];
j指针指向a[i]的前一个元素,进行循环查找适合x插入的位置:
for (j = i-1; j>=0 && x<a[j]; j--)
a[j+1] = a[j];
a[j+1] = x;
1. 复制为哨兵
L.r[0] = L.r[i];
2. 记录后移,查找插入位置
for (j = i-1; L.r[0].key<L.r[j].key; j--)
L.r[j+1] = L.r[j];
3. 将哨兵位置上记录的关键字插入到正确位置:
L.r[j+1] = L.r[0];
若在执行第1步之前先判断:需要插入的元素是否比排好序的序列中最后一个元素大;如果大的话就不用移动需要插入的元素了.
/*
* 直接插入排序
*/
void InsertSort(SqList L) {
int i,j;
for (i = 2; i < L.length; i++) {
if (L.r[i].key < L.r[i-1].key) { // 若"<",需要将L.r[i]插入到有序表中
L.r[0] = L.r[i]; // 复制为哨兵
}
for (j = i-1; L.r[0] < L.r[j]; j--) {
L.r[j+1] = L.r[j]; // 记录后移
}
L.r[j+1] = L.r[0]; // 插入到正确位置
}
}
实现排序的基本操作有两个:
1. "比较"序列中两个关键字的大小;
2. "移动"记录
最好的情况:关键字在记录中顺序有序
比较的次数: n-1
移动的次数: 0
最坏的情况: 关键字在记录中逆序有序
比较的次数: (n+2)(n-1)/2
移动的次数: (n+4)(n-1)/2
平均情况
比较次数: (n+2)(n-1)/4
移动次数: (n+6)(n-1)/4
原始数据越有序,排序速度越快
最坏情况下Tw(n) = O(n^2)
平均情况下Te(n) = O(n^2)
空间复杂度,需要一个哨兵位: O(1)
要提高查找速度:
减少元素的比较次数
减少元素的移动次数
/*
* 折半插入排序
*/
void BInsertSort(SqList L) {
for (int i = 2; i <= L.length; i++) { // 依次插入第2~第n个元素
L.r[0] = L.r[i]; // 当前插入元素存到哨兵位
int low = 1;
int high = i-1;
while (low < high) {
int mid = (low + high) / 2;
if (L.r[0].key < L.r[mid].key)
high = mid -1;
else low = mid + 1;
} // 循环结束,high+1则为插入位置
for (int j = i-1; j >= high+1; j--) { // 移动元素
L.r[j+1] = L.r[j];
}
L.r[high+1] = L.r[0]; // 插入到正确位置
}
}
基本思想:
先将整个待排记录序列分割成若干子序列,分别进行直接插入排序;待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序.
1. 定义增量序列D: Dm>Dm-1>...>D1; 如D3=5, D2=3, D1=1;
2. 对每个Dk进行"Dk-间隔"插入排序(k=m,m-1,...,1);
希尔排序的特点就是:
1. 一次移动,移动位置较大,跳跃式地接近排序后的最终位置;
2. 最好一次只需少量移动;
3. 增量序列必须是递减的,最后一个必须是1;
4. 增量序列应该是互质的;
void ShellSort(SqList &L, int dlta[], int t) { // Dk值依次存在dlta[t]中
// 按增量序列dlta[0...t-1]对顺序表L作希尔排序.
for (int k = 0; k < t; k++)
ShellInsert(L, dlta[k]); //一趟增量为dlta[k]的插入排序
}// ShellSort
void ShellInsert(SqList &L, int Dk) {
int i,j;
// 对顺序表L进行一趟增量为Dk的Shell排序,Dk为步长因子
for (i = Dk+1; i <= L.length; i++)
if (L.r[i].key < L.r[i-Dk].key) {
L.r[0] = L.r[i];
for (j = i-Dk; j>0 && (L.r[0].key < L.r[j].key); j = j-Dk)
L.r[j+dk] = L.r[j];
L.r[j+Dk] = L.r[0];
}
}
希尔排序算法效率与增量序列的取值有关
Hibbard增量序列:Dk=2^k- 1 ------ 相邻元素互质
Sedgewick增量序列: {1, 5, 19, 41, 109...} ------ 9*4^i-9*2^i+1或4^i-3*2^i+1
希尔排序算法是一种不稳定的排序算法;
时间复杂度是n和d的函数:
O(n^1.25)~O(1.6n^1.25) ------ 经验公式;
空间复杂度为O(1);
是一种不稳定的排序;
如何选择最佳d增量序列,目前尚未解决,但最后一个增量序列值必须为1,无除了1之外的公因子;
不适合在链式存储结构上实现;