插入排序(I)
直接插入排序
直接插入排序是一種最簡單的排序方法,它的基本操作是將一個記錄插入到已排好的有序的表中,從而得到一個新的、記錄數增1的有序表。
當前元素的前面元素均為有序,要插入時,從當前元素的左邊開始往前找(從后往前找),比當前元素大的元素均往右移一個位置,最后把當前元素放在它應該呆的位置就行了。
直接插入排序過程實例
比如對 21、25、49、25、16、8這些數排序:


直接插入排序分析
移動、比較的次數可作為衡量時間復雜性的標准。
最優情況:如果原始的輸入序列為正序:
比較次數:n-1
移動次數:0
最差情況:如果原始的輸入序列為逆序:
比較次數:(n+2)(n-1)/2
移動次數:(n+4)(n-1)/2
所以直接插入排序的時間復雜度為O(n2)。
直接插入排序代碼
直接插入排序
typedef int ElemType; //假設記錄類型為int,並且記錄關鍵字就是其本身 void InsertSort(ElemType A[], int n) { ElemType x; for(int i=1;i<n;++i) { x=A[i]; int j=i-1; while(A[j]>x) { A[j+1]=A[j]; j--; } A[j+1]=x; } }
其他插入排序
直接插入排序算法在n比較小的時候很好,但是當n增大了以后就不宜采用直接插入排序。
在直接插入排序的基礎上,從減少“比較”和“移動”這兩種操作的次數着眼,可得到下列插入排序的算法。
折半插入排序
由於插入的基本操作是在一個有序的表中進行查找和插入,所以其中這個“查找”操作可以利用折半查找來實現。
折半插入排序僅減少了關鍵字間的比較次數,而記錄的移動次數不變。因此,折半插入排序的時間復雜度仍為O(n2)。
2-路插入排序
2-路插入排序是在折半插入排序的基礎上再改進之,目的是減少排序過程中移動記錄的次數,但為此需要n個記錄的輔助空間。
具體做法是:另設一個同類型的數組d,將第一個元素復制過去,將這個第一個元素看做在排好序的序列中處於中間位置的記錄(並且一直把它作為此標准),然后把d看做一個循環向量,設置兩個指針first和final分別指示排序過程中得到的有序序列中的第一個記錄和最后一個記錄在d中的位置。插入元素時,先將當前元素和d[0]比較,如果該元素比d[0]小,則插入到d[0]之前的有序表中,如果比d[0]大,則插入到d[0]之后的有序表中。
在2-路插入排序中,移動記錄的次數約為n2/8,並且當第一個元素是待排序記錄中關鍵字最小或最大的記錄時,2-路插入排序就完全失去了它的優越性。
表插入排序
#define SIZE 100 //靜態鏈表容量 typedef struct { Rcd rc; //記錄項 int next; //指針項 }SLNode; //表結點類型 typedef struct { SLNode r[SIZE]; //0號元素為表頭結點 int length; //鏈表當前長度 }SLinkListType; //靜態鏈表類型
以上述靜態鏈表類型作為待排序記錄序列的存儲結構,並且,為了插入方便起見,設數組中下標為0的結點為表頭結點,並令表頭結點記錄的關鍵字為最大整數。
則表插入排序的過程描述如下:首先將靜態鏈表中數組下標為1的結點和表頭結點構成一個循環鏈表,然后依次將下標為2至n的結點按記錄關鍵字非遞減有序插入到循環鏈表中。
表插入排序的時間復雜度仍是O(n2)。
表插入排序的結果只是求得一個有序鏈表,只能對它進行順序查找,不能進行隨機查找。為了能實現有序表的折半查找,需要對記錄進行重新排列。
重排記錄的做法:順序掃描有序鏈表,將鏈表中第i個結點移動至數組的第i個分量中。
