一、 排序的基本概念和分類
- 1. 排序的定義
排序:排序是將一批無序的記錄(數據)重新排列成按關鍵字有序的記錄序列的過程。
排序通常是對於記錄來說的,將一組記錄按照某個關鍵字排成遞增有序(遞減有序)。在現實生活照中排序也經常用到,比如站隊的時候按照身高由低到高排序。
- 2. 排序的分類
排序的分類:排序分為插入排序、選擇排序、交換排序、歸並排序四大類,詳細分類如下圖16-1所示。
- 1. 排序的穩定性
穩定排序:對於關鍵字相等的記錄,排序前后相對位置不變。
不穩定排序:對於關鍵字相等的記錄,排序前后相對位置可能發生變化。
一、 內排序與外排序
待排序的記錄數量不同,排序過程中涉及的存儲器的不同,有不同的排序分類。
待排序的記錄數不太多:所有的記錄都能存放在內存中進行排序,稱為內部排序;
待排序的記錄數太多:所有的記錄不可能存放在內存中, 排序過程中必須在內、外存之間進行數據交換,這樣的排序稱為外部排序。
二、 直接插入排序
- 1. 直接插入排序思想:
將待排序的記錄Ri,插入到已排好序的記錄表R1, R2 ,…., Ri-1中,得到一個新的、記錄數增加1的有序表。 直到所有的記錄都插入完為止。
設待排序的記錄順序存放在數組R[1…n]中,在排序的某一時刻,將記錄序列分成兩部分:
◆ R[1…i-1]:已排好序的有序部分;
◆ R[i…n]:未排好序的無序部分。
顯然,在剛開始排序時,R[1]是已經排好序的。
- 2. 直接插入排序算法
例題: 關鍵字序列T=(13,6,3,31,9,27,5,11),其直接插入排序的排序過程如下:
【13】, 6, 3, 31, 9, 27, 5, 11
第1趟排序: 【6, 13】, 3, 31, 9, 27, 5, 11
第2趟排序: 【3, 6, 13】, 31, 9, 27, 5, 11
第3趟排序: 【3, 6, 13,31】, 9, 27, 5, 11
第4趟排序: 【3, 6, 9, 13,31】, 27, 5, 11
第5趟排序: 【3, 6, 9, 13,27, 31】, 5, 11
第6趟排序: 【3, 5, 6, 9, 13,27, 31】, 11
第7趟排序: 【3, 5, 6, 9, 11,13,27, 31】
算法:
#define N 8 int i,j,t,a[N]={ 13,6,3,31,9,27,5,11}; for(i=1;i<N;i++) //從第2個記錄到最后一個逐一前插 { t=a[i]; if(t<a[i-1]) { for(j=i-1;j>=0&&a[j]>t;j--) //只要有序序列比t大,記錄后移 { a[j+1]=a[j]; } a[j+1]=t; //將t插入在比它小的元素的后面 } }
- 1. 直接插入排序復雜度分析
(1) 最好情況:若待排序記錄按關鍵字從小到大排列(正序),算法中的內循環無須執行,則一趟排序時:關鍵字比較次數1次,每趟排序都要移動將近1個記錄,這樣n個記錄最終的時間復雜度是O(n)。
(2) 最壞情況:若待排序記錄按關鍵字從大到小排列(逆序),n個記錄需要n-1趟排序,最壞的情況就是完全逆序的情況,每趟排序都要移動將近n個記錄,這樣最終的時間復雜度是O(n2)。
(3) 穩定性:穩定排序
一、 希爾排序的原理
希爾排序(Shell Sort)又稱“縮小增量排序”(Diminishing Increment Sort),它也是一種屬於插入排序類的方法,但在時間效率上較前幾種排序方法有較大的改進。
從對直接插入排序的時間復雜度的分析得知,其時間復雜度是O(n2),待排記錄是正序時,可提高到O(n)。當記錄基本有序時,會大大提高效率。而希爾排序正是對直接插入排序進行改進得到的一種插入排序方法。
基本思想是:先將整個待排記錄分割成若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。
二、 希爾排序的過程
以(49 38 65 97 76 13 27 49 55 04)關鍵字為例,先看一下希爾排序的過程。初始關鍵字如圖的第一行所示。首先將該序列分成五個子序列{R1,R6},{R2,R7},...{R5,R10},如圖的第二行至第六行所示,分別對每個子序列進行直接插入排序,排序結果如圖第七行所示,從第一行的初始序列得到第七列的序列的過程稱為一趟希爾排序。然后進行第二趟希爾排序,即對下列三個子序列:{R1,R4,R7,R10},{R2,R5,R8},{R3,R6,R9}進行直接插入排序,其結果如圖第十一行所示,最后對整個序列進行一趟直接插入排序。至此,希爾排序結束,整個序列的記錄已按關鍵字非遞減有序排列
一、 希爾排序的算法
從上述排序過程可見,希爾排序的一個特點是:子序列的構成不是簡單地“逐段分割”而是將相隔某個“增量”的記錄組成子序列。如上例中,第一趟排序時的增量為5,第二趟排序時的增量為3,由於在前兩趟的插入排序中記錄的關鍵字是和同一子序列中的前一個記錄的關鍵字進行比較,因此關鍵字較小的記錄就不是一步一步的往前挪動,而是跳躍式的往前移。,從而使得在最后一趟增量為1時的插入排序時,序列已基本有序,只要做記錄的少量比較和移動即可完成排序,因此希爾排序比直接插入排序的時間復雜度低。算法如下:
void shell(int a[],int n)//希爾排序 { int i,j,k,t; k=n/2; //增量k,並逐步縮小增量 while(k>=1) { for(i=k;i<n;i++) //從第gap個元素,逐個對其所在組進行直接插入排序操作 { t=a[i]; j=i-k; while((a[j]>t)&&(j>=0)) //移動法 { a[j+k]=a[j]; j=j-k; } a[j+k]=t; //插入 } k=k/2; } printf("輸出希爾排序的結果:\n"); for(i=0;i<n;i++) { printf("%d\t",a[i]); } } void main() { int a[]={15,14,13,12,11,9,8,6,7,5}; shell(a,sizeof(a)/sizeof(int)); }
一、 希爾排序的時間復雜度分析
希爾排序的分析是一個復雜的問題,因為它的時間是所取“增量”序列的函數,這涉及一些數學上尚未解決的難題。因此到目前為止尚未有人求得一種最好的增量序列,但大量的研究已得出一些局部的結論。有人指出,當增量序列為dlta[k]=2t-k+1-1時,希爾排序的時間復雜度為O(n3/2),其中t為排序趟數,
代碼希爾排序
/* 希爾排序 縮小增量排序----->通俗的講就是改進后的直接插入排序 增加了k 增量序列 分組的組數 k=MAX/2 增量k的值是越來越小 先分小組,分別對每個組內進行直接插入排序 然后在k=k/2 分組 直到組數為1截止 進行最終的一趟直接插入排序結束 */ #include "stdio.h" #define MAX 11 void main() { int a[MAX]={6,3,8,1,7,4,9,12,52,54,2}; int i;//控制循環趟數 以及 待排序元素的下標 int j;//控制有序數組的下標 int temp;//存放 待排序元素 temp數據類型 與數組類型一致 int k;//增量 k代表把元素分為幾組 //希爾排序開始 for(k=MAX/2;k>=1;k=k/2) // 縮小增量排序 繼續分組 繼續進行直接插入排序 { //直接插入排序開始 for(i=k;i<MAX;i++) { temp=a[i];//待排序元素 if(temp<a[i-k]) { for(j=i-k;a[j]>temp&&j>=0;j=j-k)//i-k有序數組最后一個元素的下標 { a[j+k]=a[j]; } //當我們結束第二層for循環時候,結束時j=j-k a[j+k]=temp; } } //直接插入排序結束 } //希爾排序結束 printf("希爾排序結果:\n"); for(i=0;i<MAX;i++) { printf("%d\t",a[i]); } }
代碼直接插入法
/* Note:Your choice is C IDE */
#include "stdio.h"
#define N 8
void main()
{
int i,j,tem,a[N]={ 13,6,3,31,9,27,5,11};
for(i=1;i<N;i++){
tem=a[i];
if(tem<a[i-1]){
j=i-1;
while(tem<a[j]&&j>=0){
a[j+1]=a[j];
j--;
}
a[j+1]=tem;
}
}
for(i=0;i<N;i++){
printf("%d\t",a[i]);
}
}