排序算法(學習整理)
排序的算法有很多,冒泡、選擇、直接插入、雞尾酒、快排、堆排......,下文主將盡可能的介紹本人學過的所有排序(會不斷更新,本人還在學習),以從小到大為最終排序結果,C 為主要實現語言。
一、冒泡排序(Bubble Sort):
冒泡排序是一種簡單的利用交換來完成排序的算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
冒泡算法的步驟:
- 比較相鄰的元素。如果前一個元素數比後一個元素大,就交換它們兩個。
- 對每一對相鄰元素做同樣的工作,從開始0和1到結尾的n-1和n。所以,最後的元素應該會是最大的數。
- 針對所有的元素重復以上的步驟,除了最後一個。
- 持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
下面是代碼:
1 void bubble_sort(int arr[], int len) 2 { 3 int i, j; 4 for (i = 0; i < len - 1; i++) //需要進行比較的輪數 5 { 6 for (j = 0; j < len - 1 - i; j++) //每輪需要進行比較的次數, 7 //因為每一輪都有一個最大的數被放到了最後一個,所以第i輪就有i個數不需要排序(這裏就存在優化的可能)Bubble sort8 if (arr[j] > arr[j + 1]) //相鄰的兩個元素兩兩進行比較 9 { 10 arr[j] = arr[j] ^ arr[j+1]; 11 arr[j+1] = arr[j] ^ arr[j+1]; 12 arr[j] = arr[j] ^ arr[j+1]; // 交換 13 } 14 } 15 }
冒泡排序的問題很明顯,舉個例子
當數組元素為 1 2 3 9 4 5 6 7 8 時:
第一輪:1 2 3 4 5 6 7 8 9;
第二輪:1 2 3 4 5 6 7 8 9;
第三輪:1 2 3 4 5 6 7 8 9;
至此我們可以看出在第一輪的時候數組就已經排好序了,然而冒泡排序仍然在兢兢業業的工作(真是好員工,可惜效率低了點)。
所以我們可嘗試著優化一下,在進行交換的時候標記一下,當發數組遍歷完都沒有交換的情況,也就證明已經排好序了,我們的冒泡排序就可以提前下班了。
優化後的代碼如下:
1 void bubble_sort(int arr[], int len) 2 { 3 int i, j; 4 for (i = 0; i < len - 1; i++) //需要進行比較的輪數 5 { 6 int isSorted = 1; //用isSorted進行標記,默認其為已經完成排序,也可以設置為bool型 7 for (j = 0; j < len - 1 - i; j++) //每輪需要進行比較的次數 8 { 9 //因為每一輪都有一個最大的數被放到了最後一個,所以第i輪就有i個數不需要排序 10 if (arr[j] > arr[j + 1]) //相鄰的兩個元素兩兩進行比較 11 { 12 isSorted = 0; //進入交換後立刻置0, 表示排序未完成 13 arr[j] = arr[j] ^ arr[j+1]; 14 arr[j+1] = arr[j] ^ arr[j+1]; 15 arr[j] = arr[j] ^ arr[j+1]; //交換 16 } 17 } 18 if (isSorted) //如果一直未交換就代表已經完成了排序,也就是說可以提前下班了 19 { 20 break; 21 } 22 } 23 }View Code
當我們繼續測試後會發現冒泡排序的另一個問題
打個比方:
當數組為 33 54 32 21 56 67 78 89 時:
第二個for裏
第一輪比較次數:7
第二輪比較次數:7
第三輪比較次數:7
第四輪比較次數:7
程序結束
第一輪的7次是怎麽來的呢?
33和54比較,33 < 54,所以不變。
54和32比較,54 > 32,所以54和32換。
33 32 54 21 56 67 78 89
54和21比較,54 > 21,所以54和21換。
33 32 21 54 56 67 78 89
54和56比較,54 < 56, 不變。
56和67比較,56 < 67,不變。
67和78比較, 67 < 78,不變。
78和89比較, 78 < 89, 不變。
第一輪至此結束。
第二輪的6次
33和32比較, 33 > 32, 所以33和32交換。
32 33 21 54 56 67 78 89
33和21比較, 33 > 21,所以33和21交換。
32 21 33 54 56 67 78 89
33和54比較,33 < 54,所以不變。
54和56比較,54 < 56, 不變。
56和67比較,56 < 67,不變。
67和78比較, 67 < 78,不變。
第二輪至此結束。
到這裏基本就能看出問題了,這個測試組,從56往後都是有序的,但是冒泡這位員工仍然一絲不茍的執行的他的任務。
從56往後的比較都是沒有意義的,所以我們應該考慮如何讓程序識別已排好序的子列。大家可以思考一下然後再往後看。
解決方法:我們只需將要再最後一次元素進行交換的位置進行記錄,讓第二for循環再這個範圍內進行比較。
最終優化代碼如下(對我來說):
1 void bubble_sort(int arr[], int len) 2 { 3 int i, j; 4 int lastIndex, sortBorder; //用來記錄最後一次交換的下標和交換邊界 5 sortBorder = len -1; //因為第一輪的時候i = 0,所以第一輪的sortBorder == len - i - 1 6 for (i = 0; i < len - 1; i++) //需要進行比較的輪數 7 { 8 int isSorted = 1; //用isSorted進行標記,默認其為已經完成排序,也可以設置為bool型 9 for (j = 0; j < sortBorder; j++) //每輪需要進行比較的次數 10 { 11 //這裏不在由i確定循環邊界了,這也是這次優化的核心 12 if (arr[j] > arr[j + 1]) //相鄰的兩個元素兩兩進行比較 13 { 14 isSorted = 0; //進入交換後立刻置0, 表示排序未完成 15 lastIndex = j; //記錄下最後一次交換的位置 16 arr[j] = arr[j] ^ arr[j+1]; 17 arr[j+1] = arr[j] ^ arr[j+1]; 18 arr[j] = arr[j] ^ arr[j+1]; //交換 19 } 20 } 21 sortBorder = lastIndex; //講最後一次交換的位置作為下次比較的邊界 22 if (isSorted) //如果一直未交換就代表已經完成了排序,也就是說可以提前下班了 23 { 24 break; 25 } 26 } 27 }View Code
當然這也不最優的冒泡優化,但是繼續優化就涉及到另一個算法了,所以目前對冒泡這個員工的整改就到這裏
排序算法(學習整理)