八大排序演算法-初學筆記
阿新 • • 發佈:2020-12-01
八大經典排序
1、氣泡排序
基本介紹:氣泡排序是一種交換排序
基本思想:(假定從小到大的順序)從第一個元素開始,相鄰兩個資料進行比較,將小的值放在左邊。第一輪:從第一個元素開始,和與其相鄰的後一個元素進行比較,
若後一個比前一個小,則交換位置,否則不變,然後兩個索引都向後移動一位,繼續判斷至最後一組資料,第二輪:從第二個元素出發,繼續重複上述進行比較、換位,
再進行下一輪比較,直到最後一輪結束。
時間複雜度:O(n2),通過測試,長度為80000的陣列,陣列中的元素從0-800000隨機賦值,執行時間為6s;
程式碼實現:
public2、簡單選擇排序 基本介紹: 選擇排序也屬於內部排序;與堆排序共為選擇排序 基本思想: (也是假定從小到大的順序) 先設定第一個元素為最小,讓其後邊的每個元素都與其判斷,儲存最小的資料和最小資料的索引位置, 在一輪判斷後將最小值與第一個元素做替換,再設定第二個元素為當前最小值,繼續判斷,直到最後一組結束。 時間複雜度:O(n2),通過測試,長度為80000的陣列,陣列中的元素從0-800000隨機賦值,執行時間不到1s; 程式碼實現:static void selectX(int[] arr){ for (int i = 0; i < arr.length - 1; i++) { for (int j = i + 1; j < arr.length; j++) { if(arr[i] > arr[j]){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } }
public static void select2(int[] arr) { // 80000 : 不到一秒3、簡單插入排序 基本介紹:插入排序也屬於內部排序;與希爾排序同為插入排序 基本思路:(小->大)先假定有兩個陣列,無序陣列和有序陣列,將無序陣列中的一個元素與有序中的元素一一比較,找出合適的位置後插入進去。 時間複雜度: O(n2),通過測試,長度為80000的陣列,陣列中的元素從0-800000隨機賦值,執行時間不到1s; 程式碼實現 :// 800000 : 一分鐘 int minIndex = 0; int min = arr[minIndex]; for(int i = 0; i < arr.length - 1; i++) { for(int j = i + 1; j < arr.length;j++) { if(arr[j] < min) { min = arr[j]; minIndex = j; } } if(minIndex != i) { arr[minIndex] = arr[i]; arr[i] = min; } } }
public static void insert(int[] arr) { for(int i = 1; i < arr.length ; i++) { int insertIndex = i - 1; int insertVal = arr[i]; while(insertIndex >= 0 && insertVal < arr[insertIndex]) { //找插入的地方 arr[insertIndex + 1] = arr[insertIndex]; //逐個往後移一位, insertIndex--; } arr[insertIndex + 1] = insertVal; //找到了小的數放的位置 } }4、希爾排序 (插入排序) 基本介紹:希爾排序是插入排序的一種。 基本思路: 希爾排序是將一個數組先分成長度/2組資料,同一組資料相鄰間距為長度/2,根據交換法或位移法將資料進行交換,交換完後再將資料分為間距/2組資料, 每一組之間繼續進行比較交換,直到間距為0,即為結束。 時間複雜度:O(n2) 通過測試, 交換法 8萬資料 4s; 位移法 80萬資料 不到1s 800萬資料2s, 顯然希爾位移法排序效率更高。 程式碼實現:
//交換法 public static void shellSort(int[] arr) { // 八萬 : 4s int count = 0; int temp = 0; for(int gsp = arr.length / 2; gsp > 0; gsp /= 2) { for(int i = gsp; i < arr.length; i++) { for(int j = i - gsp; j > 0; j -= gsp) { if(arr[j] > arr[j + gsp]) { temp = arr[j]; arr[j] = arr[j + gsp]; arr[j + gsp] = temp; } } } } } //移位法 public static void shellSort2(int[] arr) { // 8000000 兩秒 for(int gap = arr.length / 2; gap > 0; gap /= 2) { for(int i = gap; i < arr.length; i++) { int j = i; int temp = arr[j]; while(j - gap > 0 && temp < arr[j - gap]) { arr[j] = arr[j - gap]; j -= gap; } arr[j] = temp; } } }5、快速排序 基本介紹:快速排序是對氣泡排序的一種改進。 基本思想:通過一趟排序,將資料分為以某個值大小為界限的兩部分,再將這兩部分值分別以以上方式繼續分組,直到資料變為有序。此方法可以用遞迴呼叫,用空間換時間, 大大提高了排序所耗費的時間。 時間複雜度:O(n*logn)80萬資料 不到1s 800萬資料2s 程式碼實現:
public static void quick(int[] arr,int lift,int right) { if(lift < right) { int l = lift; int r = right; int key = arr[l]; while(l < r) { while(l < r && arr[r] >= key) { r -= 1; } arr[l] = arr[r]; while(l < r && arr[l] <= key) { l += 1; } arr[r] = arr[l]; //這裡可能已經完成,因此賦值需要移位** if(l < r) { arr[r--] = arr[l]; } } arr[l] = key; //左向遞迴 quick(arr,lift,l - 1); //右向遞迴 quick(arr,l + 1,right); } }6、歸併排序 基本介紹:歸併排序用到了分治策略,先將陣列分解成一個個小的陣列,然後再將陣列進行排序整合,變成一個有序的大陣列。 基本思想:用遞迴的方法將一個長的陣列分解成一個個小的陣列,然後給每個分解過的陣列再進行排序操作,直到完成排序。 時間複雜度:O(nlog2) 800萬個資料只用了不到一秒的時間 程式碼實現:
//分: 提供分解陣列的方法 public static void merge(int[] arr, int left, int right,int[] temp) { if(left < right) { int mid = (left + right) / 2; //左分 merge(arr, left, mid, temp); //右分 merge(arr, mid + 1, right, temp); mergeSort(arr,left,right,mid,temp); } } //治: 提供合數組的方法 public static void mergeSort(int arr[], int left, int right, int mid, int temp[]) { if(left < right) { int i = left; int j = mid + 1; //temp臨時的索引 int t = 0; //三大步驟進行合併 //1.先將兩個部分的陣列比較後放入臨時陣列中,直到其中一個數組放完 while(i <= mid && j <= right) { if(arr[i] <= arr[j]) { temp[t] = arr[i]; i += 1; t += 1; }else { temp[t] = arr[j]; j += 1; t += 1; } } //2.再將另一個沒放完的陣列全部放入臨時陣列中 while(i <= mid) { temp[t] = arr[i]; t += 1; i += 1; } while(j <= right) { temp[t] = arr[j]; j += 1; t += 1; } //3.將臨時陣列放入原陣列中 int index = left; int k = 0; while(k < t) { arr[index] = temp[k]; k += 1; index += 1; } } }7、基數排序 基本介紹:基數排序用到了桶排序的思想,將資料的同一位進行比較,然後放入對應的桶中,再逐個取出資料。 基本思想:先建立十個桶,每個桶代表著一個數位上的數字,將所有陣列資料長度設定為最大數位的值,在陣列中取出同一個數位上的數字進行入桶操作,然後再逐個取出來, 分別從低位到高位進行入桶、出桶,最終就會得到有序的陣列。 時間複雜度:O(n*logn) 800萬 不到1s 程式碼實現:
public static void radixSort(int[] arr) { //獲取最大數的位數 int max = 0; for(int i = 0;i < arr.length; i++) { if(max < arr[i]) { max = arr[i]; } } int maxLength = (max + "").length(); //建立桶子 二維陣列 int [][] tong =new int[10][arr.length]; //建立每個桶子的數的個數 int[] inNumber = new int[10]; //執行操作 for(int i = 0,n = 1; i < maxLength; i++,n*=10) { //入桶,求出每個元素的位值,然後入桶 for(int j = 0 ; j < arr.length; j++) { int digOfElement = (arr[j] / n) % 10; tong[digOfElement][inNumber[digOfElement]] = arr[j]; inNumber[digOfElement]++;//忘了移動索引 } int index = 0; //出桶 for(int k = 0; k < inNumber.length; k++) { if(inNumber[k] != 0) { //放值 for(int l = 0; l < inNumber[k]; l++) { arr[index++] = tong[k][l]; } } inNumber[k] = 0; } }
8、堆排序
基本介紹:堆排序用到的是線性二叉陣列,也屬於選擇排序的一種。
基本思想:先將陣列調整成為一個大頂堆,然後將頂元素與最後一個元素位置交換,陣列長度減一。將交換後的陣列繼續調整成為新的大頂堆,並再次進行交換,以此類推。
時間複雜度: O(n*logn) 800萬資料不到 1s完成。
程式碼實現:
//編寫一個堆排序 public static void heapSort(int[] arr) { System.out.println("堆排序!!"); int temp = 0; //最終程式碼: //1.先弄一個大頂堆,升序 for(int i = arr.length / 2 + 1; i >= 0; i--) { adjustHeap(arr, i, arr.length); } //2,交換大頂堆和最後一個元素,長度減一 //3,在剩下的陣列中調整大頂堆。 for(int j = arr.length - 1; j > 0; j--) { temp = arr[j]; arr[j] = arr[0]; arr[0] = temp; adjustHeap(arr, 0, j); } } //將一個數組(二叉樹),調整成一個大頂堆 /** * * @param arr[] 待調整的陣列 * @param i 表示非葉子節點再陣列中的索引 * @param length 表示當前陣列中還有多少待調整的元素 */ public static void adjustHeap(int arr[], int i, int length) { //先取出當前元素的值 int temp = arr[i]; //開始調整 for(int k = i * 2 + 1; k < length; k = k * 2 + 1) { //從左右子節點中找到最大的那個值的索引 if(k + 1 < length && arr[k] < arr[k + 1]) { k++; } //如果子節點大於父節點,直接交換 if(arr[k] > temp) { arr[i] = arr[k]; i = k;//i 指向最大的元素k,繼續迴圈比較 }else { break;//我們是從左到右,從下到上,不必判斷左下層子節點 } } //當for迴圈結束後,已經將以i為父節點的區域性的樹變成大頂堆。 arr[i] = temp;//將temp值放到調整後的位置。 }相關術語解釋: 1、穩定:如果a原先再b前面,而a=b,排序之後a仍然在b的前面; 不穩定:如果a原先再b前面,而a=b,排序之後a可能會出現在b的後面; 2、內排序:所有排序操作都在記憶體中進行 外排序:由於資料太大,因此把資料放在磁碟中,而排序通過磁碟和記憶體的資料傳輸才能進行 3、時間複雜度:一個演算法執行所耗費的時間。 4、空間複雜度:執行一個程式所需記憶體的大小。 5、n: 資料規模;k:“桶的個數”。 6、In-place: 不佔用額外記憶體; Out-place:佔用額外記憶體。