插入排序可分為五類:
1. 直接插入排序演算法 基本操作:將一個記錄插入到已排好序的有序表中,從而得到一個新的、記錄增1的有序表。 時間複雜度:O(n^2) 代碼:java表示
/**<br /> * 直接插入排序,升序排列。<br /> *<br /> * @param data<br /> */<br />public void dirInsertSor(int[] data) {<br />for (int i = 1; i < data.length; i++) {<br />if (data[i] < data[i - 1]) {<br />int temp = data[i];<br />int j;<br />for (j = i - 1; j >= 0 && temp < data[j]; j--) {<br />data[j + 1] = data[j];<br />}<br />data[j + 1] = temp;<br />}<br />}<br />}
分析:
直接插入排序的好處就是算簡單明了,程式容易實現.但是效率就不可避免的比較低下.程式除了需要一個記錄資料的數組以外還要有一個臨時的輔助空間.
從程式中可以看出,即使假設數組已經是順序的,程式仍舊需要進行n - 1次的比較,但是不用進行數組元素的移動.當數組是逆序的時候,程式需要進行(n + 2)(n - 1) / 2次的比較,另加(n + 4)(n - 1) / 2次的數組元素移動.綜上平均計算一下的話,差不多要進行(n ^ 2) / 4次的比較和移動,所以複雜度應該是O(n ^ 2).
2. 折半插入排序演算法
基本操作:由於直接插入排序的基本操作是在一個有序表中進行尋找的和插入的,所以這個“尋找”操作可以利用“折半尋找”來實現,這樣可以減少尋找的時間複雜度。
時間複雜度:O(n^2)
代碼:java表示
/**<br /> * 折半插入排序,升序排列。<br /> *<br /> * @param data<br /> */<br />public void halfInsertSor(int[] data) {<br />for (int i = 1; i < data.length; i++) {<br />if (data[i] < data[i - 1]) {<br />int temp = data[i];<br />int index = halfSearch(data, 0, i - 1, temp); // 折半尋找<br />for (int j = i; j > index; j--) {<br />data[j] = data[j - 1];<br />}<br />data[index] = temp;<br />}<br />}<br />}<br />/**<br /> * 折半尋找的數組。如果數組data中沒有value,則返回比value大的元素中最小的元素所在的位置。<br /> *<br /> * @param data<br /> * @param low<br /> * @param high<br /> * @param value<br /> * 要尋找的值<br /> * @return<br /> */<br />public int halfSearch(int[] data, int low, int high, int value) {<br />int index = 0;<br />while (low <= high) {<br />index = (low + high) / 2;<br />if (value == data[index]) {<br />break;<br />} else if (value < data[index]) {<br />high = index - 1;<br />} else {<br />low = index + 1;<br />}<br />}<br />return index;<br />}
分析:這個方法可以減少資料比較的次數,但是資料移動還是要一步一步的進行(仍舊需要移動大約i / 2個已排好序的元素),所以時間複雜度沒有什麼實質的改善.治標不治本的方法.
3. 2-路插入排序演算法 上面的二分插入減少的是資料比較的次數,而二路插入的方法可以減少資料移動的次數.
/**<br /> * 2-路插入排序,升序排列。<br /> *<br /> * @param data<br /> */<br />public void twoWayInsertSort(int[] data) {<br />int low, high, keyIndex, index;<br />int length = data.length;<br />int[] b = new int[length]; // 輔助記錄空間<br />low = high = 0;<br />b[0] = data[0];<br />for (int i = 1; i < length; i++) {<br />if (data[i] <= b[low]) { // 當資料小於或等於最小值時<br />low = (low - 1 + length) % length;<br />b[low] = data[i];<br />} else if (data[i] > b[high]) { // 當資料大於最大值時<br />high = (high + 1 + length) % length;<br />b[high] = data[i];<br />} else { // 當資料在最大值和最小值之間時<br />keyIndex = (high - 1 + length) % length;<br />while (true) {<br />if (b[keyIndex] <= data[i]) {<br />index = high;<br />high = (high + 1 + length) % length;<br />while (index != keyIndex) {<br />b[(index + 1 + length) % length] = b[index];<br />index = (index - 1 + length) % length;<br />}<br />b[(keyIndex + 1 + length) % length] = data[i];<br />break;<br />}<br />keyIndex = (keyIndex - 1 + length) % length;<br />}<br />}<br />}<br />for (int i = 0; i < length; i++) {<br />data[i] = b[(low + i) % length];<br />}<br />}
這個二路插入的演算法可以比二分插入演算法節約一半的已耗用時間,但是程式比較複雜.
上面的程式另外使用了一個大小為n的輔助空間,並且把b看成了迴圈向量.low是數組中最小的元素,high是數組中最大的元素,如果待排序的元素小於low則放在first的前面,如果比high小則放在high的後面.
在<<電腦程式設計藝術>>中作者說到過一種方法,把輸入地區當作一個迴圈表,而且位置N同1相鄰.根據上一次插入的元素是落在已排序元素的中心的左面還是右面,而從當前未排序元素段的右面或是左面來取新的元素.過後通常需要"轉動"這個地區.通過這個方法可以僅用N + 1的空間同時進行輸入,輸出和排序.
4. 表插入排序演算法
基本操作:和直接插入排序相比,不同之處僅是以修改2n次指標代替了移動記錄,排序過程中所需進行的關鍵字間比較次數相同。
時間複雜度:O(n^2)
代碼:略
5. 希爾排序(縮小增量排序)演算法
基本思想:
不斷把待排序的對象分成若干個小組,對同一小組內的對象採用直接插入法排序,當完成了所有對象都分在一個組內的排序後,排序過程結束。每次比較指定間距的兩個資料項目,若左邊的值小於右邊的值,則交換它們的位置。間距d按給定公式減少: di+1 =(di +1)/2 ,直到d等於1為止。D可以選取{9,5,3,2,1}。
演算法步驟:
Step1 將n個元素個數列分為5個小組,在每個小組內按直接插入法排序;
step2 在第i步,分組個數取 di+1 =(di +1)/2 {9,5,3,2,1};相臨兩組之間的對應元素進行比較,如果ai>aj,則交換它們的位置;
Step3 當dK = 1的迴圈過程完成後,排序過程結束。
基本操作:先將整個待排記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。
希爾排序的一個特點是:子序列的構成不是簡單地“逐段分割”,而是將相隔某個“增量” 的記錄組成一個子序列。希爾排序的分析是一個複雜的問題,因為它的時間是所取“增量”序列的函數,這涉及一些數學上尚未解決的難題。因此,到目前為止尚未有人求得最好的增量序列。增量序列可以有各種方法,但需要注意:應使增量序列中的值沒有除1之外的公因子;並且最後一個增量值必須等於1。
c 代碼:
void shellsort(int v[], int n)<br />{<br />int gap, i, j, temp;<br />for(gap=n/2;gap>0;gap/=2) //設定步長<br /> for(i=gap;i<n;++i) //在元素間移動為止<br /> for(j=i-gap; j>=0&&v[j]>v[j+gap]; j-=gap){ //比較相距gap的元素,逆序互換<br /> temp=v[j];<br /> v[j]=v[j+gap];<br /> v[j+gap]=temp;<br /> }<br />}<br />
分析:Shell排序的時間複雜度不是O(n ^ 2),而是根據所選的增量序列而定的.
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/lixiang040330624/archive/2009/10/09/4647512.aspx