十種常見排序算法能夠分爲兩大類:
非線性時間比較類排序:經過比較來決定元素間的相對次序,因爲其時間複雜度不能突破O(nlogn),所以稱爲非線性時間比較類排序。java
線性時間非比較類排序:不經過比較來決定元素間的相對次序,它能夠突破基於比較排序的時間下界,以線性時間運行,所以稱爲線性時間非比較類排序。git
穩定:若是a本來在b前面,而a=b,排序以後a仍然在b的前面。算法
不穩定:若是a本來在b的前面,而a=b,排序以後 a 可能會出如今 b 的後面。shell
時間複雜度:對排序數據的總的操做次數。反映當n變化時,操做次數呈現什麼規律。數組
空間複雜度:是指算法在計算機內執行時所需存儲空間的度量,它也是數據規模n的函數。 數據結構
/** * 冒泡排序 * 分類 -------------- 內部比較排序 * 數據結構 ---------- 數組 * 最差時間複雜度 ---- O(n^2) * 最優時間複雜度 ---- 若是能在內部循環第一次運行時,使用一個旗標來表示有無須要交換的可能,能夠把最優時間複雜度下降到O(n) * 平均時間複雜度 ---- O(n^2) * 所需輔助空間 ------ O(1) * 穩定性 ------------ 穩定 */ public void bubble (int []array){ int temp;//交換數據 System.out.println("冒泡排序:"); for(int i=0;i<array.length-1;i++) { //最後一個元素不須要比,因此總次數減小1 for(int j=0;j<array.length-1-i;j++) { //每次少比一回 if(array[j]>array[j+1]) { //大於時交換升降,反之降序 temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } }
n個記錄的直接選擇排序可通過n-1趟直接選擇排序獲得有序結果。具體算法描述以下:ide
/** * 選擇排序 * 分類 -------------- 內部比較排序 * 數據結構 ---------- 數組 * 最差時間複雜度 ---- O(n^2) * 最優時間複雜度 ---- O(n^2) * 平均時間複雜度 ---- O(n^2) * 所需輔助空間 ------ O(1) * 穩定性 ------------ 不穩定 */ public void selection (int []arr){ int len = arr.length; int minIndex, temp; for (int i = 0; i < len - 1; i++) { minIndex = i; //用來記住數組元素的下標 for (int j = i + 1; j < len; j++) { if (arr[j] < arr[minIndex]) { // 尋找最小的數 minIndex = j; // 將最小數的索引保存 } } //一輪排序進行一次數組位置交換 if(i!=minIndex) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } }
/** * 插入排序 * 分類 ------------- 內部比較排序 * 數據結構 ---------- 數組 * 最差時間複雜度 ---- 最壞狀況爲輸入序列是降序排列的,此時時間複雜度O(n^2) * 最優時間複雜度 ---- 最好狀況爲輸入序列是升序排列的,此時時間複雜度O(n) * 平均時間複雜度 ---- O(n^2) * 所需輔助空間 ------ O(1) * 穩定性 ------------ 穩定 */ public void insertion (int []arr){ int len = arr.length; int preIndex, current; for (int i = 1; i < len; i++) { preIndex = i - 1; current = arr[i]; while (preIndex >= 0 && arr[preIndex] > current) { arr[preIndex + 1] = arr[preIndex]; preIndex--; } arr[preIndex + 1] = current; } }
/** * 希爾排序 * 分類 -------------- 內部比較排序 * 數據結構 ---------- 數組 * 最差時間複雜度 ---- 根據步長序列的不一樣而不一樣。已知最好的爲O(n(logn)^2) * 最優時間複雜度 ---- O(n) * 平均時間複雜度 ---- 根據步長序列的不一樣而不一樣。 * 所需輔助空間 ------ O(1) * 穩定性 ------------ 不穩定 */ public void shellSort(int[] arrays) { if (arrays == null || arrays.length <= 1) { return; } //增量 int incrementNum = arrays.length / 2; while (incrementNum >= 1) { for (int i = 0; i < arrays.length; i++) { //進行插入排序 for (int j = i; j < arrays.length - incrementNum; j = j + incrementNum) { if (arrays[j] > arrays[j + incrementNum]) { int temple = arrays[j]; arrays[j] = arrays[j + incrementNum]; arrays[j + incrementNum] = temple; } } } //設置新的增量 incrementNum = incrementNum / 2; } System.out.println(Arrays.toString(arrays)); }
動圖來源
函數
實現步驟(分而治之)ui
/** * 歸併排序 * 分類 -------------- 內部比較排序 * 數據結構 ---------- 數組 * 最差時間複雜度 ---- O(nlogn) * 最優時間複雜度 ---- O(nlogn) * 平均時間複雜度 ---- O(nlogn) * 所需輔助空間 ------ O(n) * 穩定性 ------------ 穩定 */ public class MergeSort { public static void main(String[] args) { int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1}; sort(arr); System.out.println(Arrays.toString(arr)); } public static void sort(int[] arr) { int[] temp = new int[arr.length];//在排序前,先建好一個長度等於原數組長度的臨時數組,避免遞歸中頻繁開闢空間 sort(arr, 0, arr.length - 1, temp); } private static void sort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; sort(arr, left, mid, temp);//左邊歸併排序,使得左子序列有序 sort(arr, mid + 1, right, temp);//右邊歸併排序,使得右子序列有序 merge(arr, left, mid, right, temp);//將兩個有序子數組合並操做 } } private static void merge(int[] arr, int left, int mid, int right, int[] temp) { int i = left;//左序列指針 int j = mid + 1;//右序列指針 int t = 0;//臨時數組指針 while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[t++] = arr[i++]; } else { temp[t++] = arr[j++]; } } while (i <= mid) {//將左邊剩餘元素填充進temp中 temp[t++] = arr[i++]; } while (j <= right) {//將右序列剩餘元素填充進temp中 temp[t++] = arr[j++]; } t = 0; //將temp中的元素所有拷貝到原數組中 while (left <= right) { arr[left++] = temp[t++]; } } }
/** * 快速排序 * 分類 ------------ 內部比較排序 * 數據結構 --------- 數組 * 最差時間複雜度 ---- 每次選取的基準都是最大(或最小)的元素,致使每次只劃分出了一個分區,須要進行n-1次劃分才能結束遞歸,時間複雜度爲O(n^2) * 最優時間複雜度 ---- 每次選取的基準都是中位數,這樣每次都均勻的劃分出兩個分區,只須要logn次劃分就能結束遞歸,時間複雜度爲O(nlogn) * 平均時間複雜度 ---- O(nlogn) * 所需輔助空間 ------ 主要是遞歸形成的棧空間的使用(用來保存left和right等局部變量),取決於遞歸樹的深度,通常爲O(logn),最差爲O(n) * 穩定性 ---------- 不穩定 */ public class QuickSort { public static void main(String[] args) { int[] a = {1, 2, 4, 5, 7, 4, 5, 3, 9, 0}; System.out.println(Arrays.toString(a)); quickSort(a); System.out.println(Arrays.toString(a)); } public static void quickSort(int[] a) { if (a.length > 0) { quickSort(a, 0, a.length - 1); } } private static void quickSort(int[] a, int low, int high) { //1,找到遞歸算法的出口 if (low > high) { return; } //2, 存 int i = low; int j = high; //3,key int key = a[low]; //4,完成一趟排序 while (i < j) { //4.1 ,從右往左找到第一個小於key的數 while (i < j && a[j] > key) { j--; } // 4.2 從左往右找到第一個大於key的數 while (i < j && a[i] <= key) { i++; } //4.3 交換 if (i < j) { int p = a[i]; a[i] = a[j]; a[j] = p; } } // 4.4,調整key的位置 int p = a[i]; a[i] = a[low]; a[low] = p; //5, 對key左邊的數快排 quickSort(a, low, i - 1); //6, 對key右邊的數快排 quickSort(a, i + 1, high); } }
import java.util.Arrays; /** * 堆排序 * 分類 -------------- 內部比較排序 * 數據結構 ---------- 數組 * 最差時間複雜度 ---- O(nlogn) * 最優時間複雜度 ---- O(nlogn) * 平均時間複雜度 ---- O(nlogn) * 所需輔助空間 ------ O(1) * 穩定性 ------------ 不穩定 */ public class HeapSort { public static void main(String[] args) { int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 }; heapSort(a); System.out.println(Arrays.toString(a)); } /** * 構建大頂堆 */ public static void adjustHeap(int[] a, int i, int len) { int temp, j; temp = a[i]; for (j = 2 * i; j < len; j *= 2) {// 沿關鍵字較大的孩子結點向下篩選 if (j < len && a[j] < a[j + 1]) ++j; // j爲關鍵字中較大記錄的下標 if (temp >= a[j]) break; a[i] = a[j]; i = j; } a[i] = temp; } public static void heapSort(int[] a) { int i; for (i = a.length / 2 - 1; i >= 0; i--) {// 構建一個大頂堆 adjustHeap(a, i, a.length - 1); } for (i = a.length - 1; i >= 0; i--) {// 將堆頂記錄和當前未經排序子序列的最後一個記錄交換 int temp = a[0]; a[0] = a[i]; a[i] = temp; adjustHeap(a, 0, i - 1);// 將a中前i-1個記錄從新調整爲大頂堆 } } }
/** * 計數排序 * 分類 ------------ 內部非比較排序 * 數據結構 --------- 數組 * 最差時間複雜度 ---- O(n + k) * 最優時間複雜度 ---- O(n + k) * 平均時間複雜度 ---- O(n + k) * 所需輔助空間 ------ O(n + k) * 穩定性 ----------- 穩定 */ public class CountSort { private static int[] countSort(int[] array,int k) { int[] C=new int[k+1];//構造C數組 int length=array.length,sum=0;//獲取A數組大小用於構造B數組 int[] B=new int[length];//構造B數組 for(int i=0;i<length;i++) { C[array[i]]+=1;// 統計A中各元素個數,存入C數組 } for(int i=0;i<k+1;i++)//修改C數組 { sum+=C[i]; C[i]=sum; } for(int i=length-1;i>=0;i--)//遍歷A數組,構造B數組 { B[C[array[i]]-1]=array[i];//將A中該元素放到排序後數組B中指定的位置 C[array[i]]--;//將C中該元素-1,方便存放下一個一樣大小的元素 } return B;//將排序好的數組返回,完成排序 } public static void main(String[] args) { int[] A=new int[]{2,5,3,0,2,3,0,3}; int[] B=countSort(A, 5); System.out.println(Arrays.toString(B)); } }
/** * 桶排序 * * 分類 ------------- 內部非比較排序 * 數據結構 --------- 數組 * 最差時間複雜度 ---- O(nlogn)或O(n^2),只有一個桶,取決於桶內排序方式 * 最優時間複雜度 ---- O(n),每一個元素佔一個桶 * 平均時間複雜度 ---- O(n),保證各個桶內元素個數均勻便可 * 所需輔助空間 ------ O(n + bn) * 穩定性 ----------- 穩定 */ public static void bucketSort(int[] arr){ int max = Integer.MIN_VALUE; int min = Integer.MAX_VALUE; for(int i = 0; i < arr.length; i++){ max = Math.max(max, arr[i]); min = Math.min(min, arr[i]); } //桶數 int bucketNum = (max - min) / arr.length + 1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); for(int i = 0; i < bucketNum; i++){ bucketArr.add(new ArrayList<Integer>()); } //將每一個元素放入桶 for(int i = 0; i < arr.length; i++){ int num = (arr[i] - min) / (arr.length); bucketArr.get(num).add(arr[i]); } //對每一個桶進行排序 for(int i = 0; i < bucketArr.size(); i++){ Collections.sort(bucketArr.get(i)); } System.out.println(bucketArr.toString()); }
/** * 基數排序 * 分類 ------------- 內部非比較排序 * 數據結構 ---------- 數組 * 最差時間複雜度 ---- O(n * dn) * 最優時間複雜度 ---- O(n * dn) * 平均時間複雜度 ---- O(n * dn) * 所需輔助空間 ------ O(n * dn) * 穩定性 ----------- 穩定 */ private static void radixSort(int[] array,int d) { int n=1;//表明位數對應的數:1,10,100... int k=0;//保存每一位排序後的結果用於下一位的排序輸入 int length=array.length; int[][] bucket=new int[10][length];//排序桶用於保存每次排序後的結果,這一位上排序結果相同的數字放在同一個桶裏 int[] order=new int[length];//用於保存每一個桶裏有多少個數字 while(n<d) { for(int num:array) //將數組array裏的每一個數字放在相應的桶裏 { int digit=(num/n)%10; bucket[digit][order[digit]]=num; order[digit]++; } for(int i=0;i<length;i++)//將前一個循環生成的桶裏的數據覆蓋到原數組中用於保存這一位的排序結果 { if(order[i]!=0)//這個桶裏有數據,從上到下遍歷這個桶並將數據保存到原數組中 { for(int j=0;j<order[i];j++) { array[k]=bucket[i][j]; k++; } } order[i]=0;//將桶裏計數器置0,用於下一次位排序 } n*=10; k=0;//將k置0,用於下一輪保存位排序結果 } }