Day Day Up
插入排序
这么多插入排序,由直接插入排序进行变化得来!
直接插入排序 [稳定]
- 将序列划分为两块:有序区,无序区
- 初始态序列 有序区R[0],无序区R[1] ~ R[n-1]
步骤
i 指向无序区的头
,j 指向有序区的尾
,tmp 用于临时储存待插入的值
- 无序区中每次拿出第一个元素插入到有序区,一共可以执行
n - 1
次(1 ~ n - 1) - 拿出R[i]
存入tmp临时变量
,j = i - 1
从有序区的尾部开始扫描,大于tmp
的元素向后移位一个(R[i]已经用tmp临时储存,可以直接覆盖) - 直到扫描到一个比tmp小的,将tmp插入到该位置的后面,即
j+ 1
的位置 - 整个过程中,
j
满足j >= 0
实现
void insertSort(int *R,int n){
//param: R 序列
//param: n 序列长度
int i,j,tmp;
for(i=1;i<n;i++){
if(!(R[i] < R[i-1]))
continue;//此元素不需要排序
tmp = R[i];
for(j=i-1;j>=0;j--){
if(R[j]<tmp)
break; //找到该元素
else
R[j + 1] = R[j];
}
R[j + 1] = tmp;//插入到J后,这个放在外面是因为,有可能插入在第一个位置,即j = -1的时候;
}
}
分析
当原序列为
正序
:
每一轮:只进行一次比较,即 R[i - 1] < R[i]的比较,不发生元素的移动。
Cmin = n - 1
Mmin = 0
当原序列为
逆序
:
每一轮:有序区的所有元素都需要进行一次,即需要 i
次比较,元素发生移动的个数 (i - 1 - 0) + 1
,另外对于临时变量 tmp
有 两次
额外的移动,总计 i + 2
次移动
Cmax = n(n-1)/2 = O(n2)
Mmax = (n-1)*(n + 4) / 2 = O(n2)
平均时间复杂度
每一轮:平均比较次数 (1+i )/ 2
,平均移动次数 (0 + i) / 2 + 2
总的比较次数和移动次数 = (n - 1)(n + 4)/2 = O(n2)
折半插入排序 [稳定]
步骤
- 其步骤和直接插入相似,只是先用二分法找到插入的位置,再进行元素移动
实现
void binInsertSort(int *R,int n){
int i,j,tmp;
int low,high,mid;
for(i=1;i<n;i++){
if(!(R[i] < R[i - 1]))
continue;
tmp = R[i];
low = 0;high = i - 1;
while(low <= high){
mid = (low + high)/2;
if(tmp < R[mid])
high = mid - 1;
else
low = mid + 1;
}
for(j = i - 1; j >= high + 1;j--){
R[j + 1] = R[j];
}
R[high + 1] = tmp;
}
}
分析
平均比较:log2(i+1)
平均移动:i/2 + 2
平均时间复杂度:
(log2(i+1) + i/2 + 2) = O(n2)
希尔排序 [不稳定]
- 增量分块,增量递减
- 分组的直接插入排序
与直接插入排序想比,比较每次跳跃d,初始值从d开始。
扫描二维码关注公众号,回复:
9056298 查看本文章
步骤
取一个d,分为d组,d递减至1
实现
void shellSort(int * R,int n){
int i,j,tmp,d;
d = n / 2;
while(d > 0){
for(i = d; i < n;i++) //R[d]是第一组的第二个元素(直接插入法中将R[0]作为初始有序区)
{
tmp = R[i];
for(j = i-d;j>=0&&tmp<R[j]; j-=d){
R[j+d] = R[j];
}
R[j+d] = tmp; //插入在后面
}
d = d/2;
}
}
分析
每走一趟,d 变为 floor(d/2)
走了 t = floor(log2n) - 1 次(-1 是因为d的初始值就为 n/2
),d = 1,再走一次进入直接插入排序,完成所有的排序
平均时间复杂度:O(n1.3)
不稳定性
希尔排序是分组的,移动跨越很大,过程中不一定是有序的,当 d=1 且执行完
的时候为有序。