數據結構之排序
1. 插入排序
1.1 直接插入排序
直接插入排序是將未排序的數據插入至已排好序序列的合適位置。 具體流程如下: 1、首先比較數組的前兩個數據,並排序; 2、比較第三個元素與前兩個排好序的數據,並將第三個元素放入適當的位置; 3、比較第四個元素與前三個排好序的數據,並將第四個元素放入適當的位置; ...... 4、直至把最後一個元素放入適當的位置。 假如有初始數據:25 11 45 26 12 78。 1、首先比較25和11的大小,11小,位置互換,第一輪排序後,順序為:[11, 25, 45, 26, 12, 78]。 2、對於第三個數據45,其大於11、25,所以位置不變,順序依舊為:[11, 25, 45, 26, 12, 78]。 3、對於第四個數據26,其大於11、25,小於45,所以將其插入25和45之間,順序為:[11, 25, 26, 45, 12, 78]。 ....... 4、最終順序為:[11, 12, 25, 26, 45, 78]。 直接插入排序是穩定的。直接插入排序的平均時間復雜度為O(n2)。 Java 代碼實現如下:1 public void sort(int[] arr) { 2 int tmp; 3 for(int i = 1; i < arr.length; i++) { 4 // 待插入數據 5 tmp = arr[i]; 6 int j; 7 for(j = i - 1; j >= 0; j--) { 8 // 判斷是否大於tmp,大於則後移一位 9 if(arr[j] > tmp) { 10 arr[j+1] = arr[j]; 11 }else{ 12 break; 13 } 14 } 15 arr[j+1] = tmp; 16 System.out.println(i + ":" + Arrays.toString(arr)); 17 } 18 }
1.2 希爾排序(最小增量排序)
希爾排序嚴格來說是基於插入排序的思想,又被稱為縮小增量排序。
具體流程如下: 1、將包含n個元素的數組,分成n/2個數組序列,第一個數據和第n/2+1個數據為一對... 2、對每對數據進行比較和交換,排好順序; 3、然後分成n/4個數組序列,再次排序; 4、不斷重復以上過程,隨著序列減少並直至為1,排序完成。 假如有初始數據:25 11 45 26 12 78。 1、第一輪排序,將該數組分成 6/2=3 個數組序列,第1個數據和第4個數據為一對,第2個數據和第5個數據為一對,第3個數據和第6個數據為一對,每對數據進行比較排序,排序後順序為:[25, 11, 45, 26, 12, 78]。 2、第二輪排序 ,將上輪排序後的數組分成6/4=1個數組序列,此時逐個對數據比較,按照插入排序對該數組進行排序,排序後的順序為:[11, 12, 25, 26, 45, 78]。 對於插入排序而言,如果原數組是基本有序的,那排序效率就可大大提高。另外,對於數量較小的序列使用直接插入排序,會因需要移動的數據量少,其效率也會提高。因此,希爾排序具有較高的執行效率。 希爾排序並不穩定,O(1)的額外空間,時間復雜度為O(N*(logN)^2)。1 public void sort(int[] arr) { 2 // i表示希爾排序中的第n/2+1個元素(或者n/4+1) 3 // j表示希爾排序中從0到n/2的元素(n/4) 4 // r表示希爾排序中n/2+1或者n/4+1的值 5 int i, j, r, tmp; 6 // 劃組排序 7 for(r = arr.length / 2; r >= 1; r = r / 2) { 8 for(i = r; i < arr.length; i++) { 9 tmp = arr[i]; 10 j = i - r; 11 // 一輪排序 12 while(j >= 0 && tmp < arr[j]) { 13 arr[j+r] = arr[j]; 14 j -= r; 15 } 16 arr[j+r] = tmp; 17 } 18 System.out.println(i + ":" + Arrays.toString(arr)); 19 } 20 }
2. 交換排序
2.1 冒泡排序
原理:比較兩個相鄰的元素,將值大的元素交換至右端。
思路:依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。重復第一趟步驟,直至全部排序完成。
舉例說明:要排序數組:int[] arr={6,3,8,2,9,1};
第一趟排序:
第一次排序:6和3比較,6大於3,交換位置: 3 6 8 2 9 1
第二次排序:6和8比較,6小於8,不交換位置:3 6 8 2 9 1
第三次排序:8和2比較,8大於2,交換位置: 3 6 2 8 9 1
第四次排序:8和9比較,8小於9,不交換位置:3 6 2 8 9 1
第五次排序:9和1比較:9大於1,交換位置: 3 6 2 8 1 9
第一趟總共進行了5次比較, 排序結果: 3 6 2 8 1 9
---------------------------------------------------------------------
第二趟排序:
第一次排序:3和6比較,3小於6,不交換位置:3 6 2 8 1 9
第二次排序:6和2比較,6大於2,交換位置: 3 2 6 8 1 9
第三次排序:6和8比較,6大於8,不交換位置:3 2 6 8 1 9
第四次排序:8和1比較,8大於1,交換位置: 3 2 6 1 8 9
第二趟總共進行了4次比較, 排序結果: 3 2 6 1 8 9
---------------------------------------------------------------------
第三趟排序:
第一次排序:3和2比較,3大於2,交換位置: 2 3 6 1 8 9
第二次排序:3和6比較,3小於6,不交換位置:2 3 6 1 8 9
第三次排序:6和1比較,6大於1,交換位置: 2 3 1 6 8 9
第二趟總共進行了3次比較, 排序結果: 2 3 1 6 8 9
---------------------------------------------------------------------
第四趟排序:
第一次排序:2和3比較,2小於3,不交換位置:2 3 1 6 8 9
第二次排序:3和1比較,3大於1,交換位置: 2 1 3 6 8 9
第二趟總共進行了2次比較, 排序結果: 2 1 3 6 8 9
---------------------------------------------------------------------
第五趟排序:
第一次排序:2和1比較,2大於1,交換位置: 1 2 3 6 8 9
第二趟總共進行了1次比較, 排序結果: 1 2 3 6 8 9
---------------------------------------------------------------------
最終結果:1 2 3 6 8 9
---------------------------------------------------------------------
由此可見:N個數字要排序完成,總共進行N-1趟排序,每i趟的排序次數為(N-i)次,所以可以用雙重循環語句,外層控制循環多少趟,內層控制每一趟的循環次數,即
public static void BubbleSort(int[] arr) { int temp;//定義一個臨時變量 for(int i=0;i<arr.length-1;i++){//冒泡趟數 for(int j=0;j<arr.length-i-1;j++){ if(arr[j+1]<arr[j]){ temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } }
2.2 快速排序
單單看以上解釋還是有些模糊,可以通過實例來理解它,下面通過一組數據來進行排序過程的解析:
原數組:{3,7,2,9,1,4,6,8,10,5}
期望結果:{1,2,3,4,5,6,7,8,9,10}
花了點時間擼了下面這張快速排序示意圖:
Demo步驟解析:
1.一開始選定數組的最後一個元素5作為基準值,也就是最終排序結果應該是以5為界限劃分為左右兩邊。
2.從左邊開始,尋找比5大的值,然後與5進行調換(因為如果比5小的值本來就應該排在5前面,比5大的值調換之後就去到了5的後面),一路過來找到了7,將7與5調換,結束此次遍歷。
3.從右邊開始,由於7已經是上一輪排好序的便不再動它,從10開始,一路向左遍歷,尋找比5小的值,然後與5進行調換(因為如果比5大的值本來就應該排在5後面,比5小的值調換之後就去到了5的後前面),一路過來找到了4,將4與5調換,結束此次遍歷。
4.從左邊開始,由於3和4都是前兩輪已經排好序的便不再動它,從2開始,一路向右遍歷,尋找比5大的值,然後與5進行調換(道理同步驟2),一路過來找到了9,將9與5調換,結束此次遍歷。
5.從右邊開始,由於排在9後面的那幾個數字都是上幾輪排好序的便不再動它,從1開始,一路向右遍歷,尋找比5小的值,然後與5進行調換(道理同步驟3),一下子就找到了1,將1與5調換,結束此次遍歷。
6.這個時候,發現5的左右兩邊都是排好序了的,所以結束此輪排序,5的左右兩邊抽出來各自進行下一輪的排序,規則同上,直到無法再拆分下去,即完成了整體的快速排序。
Java代碼- /**
- * 快速排序
- * @author IT_ZJYANG
- */
- public class QuickSort {
- /**
- * 將數組的某一段元素進行劃分,小的在左邊,大的在右邊
- * @param a
- * @param start
- * @param end
- * @return
- */
- public static int divide(int[] a, int start, int end){
- //每次都以最右邊的元素作為基準值
- int base = a[end];
- //start一旦等於end,就說明左右兩個指針合並到了同一位置,可以結束此輪循環。
- while(start < end){
- while(start < end && a[start] <= base)
- //從左邊開始遍歷,如果比基準值小,就繼續向右走
- start++;
- //上面的while循環結束時,就說明當前的a[start]的值比基準值大,應與基準值進行交換
- if(start < end){
- //交換
- int temp = a[start];
- a[start] = a[end];
- a[end] = temp;
- //交換後,此時的那個被調換的值也同時調到了正確的位置(基準值右邊),因此右邊也要同時向前移動一位
- end--;
- }
- while(start < end && a[end] >= base)
- //從右邊開始遍歷,如果比基準值大,就繼續向左走
- end--;
- //上面的while循環結束時,就說明當前的a[end]的值比基準值小,應與基準值進行交換
- if(start < end){
- //交換
- int temp = a[start];
- a[start] = a[end];
- a[end] = temp;
- //交換後,此時的那個被調換的值也同時調到了正確的位置(基準值左邊),因此左邊也要同時向後移動一位
- start++;
- }
- }
- //這裏返回start或者end皆可,此時的start和end都為基準值所在的位置
- return end;
- }
- /**
- * 排序
- * @param a
- * @param start
- * @param end
- */
- public static void sort(int[] a, int start, int end){
- if(start > end){
- //如果只有一個元素,就不用再排下去了
- return;
- }
- else{
- //如果不止一個元素,繼續劃分兩邊遞歸排序下去
- int partition = divide(a, start, end);
- sort(a, start, partition-1);
- sort(a, partition+1, end);
- }
- }
- }
數據結構之排序