插入排序
這麼多插入排序,由直接插入排序進行變化得來!
直接插入排序 [穩定]
- 將序列劃分爲兩塊:有序區,無序區
- 初始態序列 有序區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開始。
步驟
取一個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 且執行完
的時候爲有序。