算法總結:
參數說明:
穩定性:相同數組中變量a與b如果值相等排序時,a原本在b前面,出現a在b后面的現象。
時間復雜度: 一個算法執行所耗費的時間。
空間復雜度:運行完一個程序(函數)所需內存的大小。
n: 數據規模(數組數據個數)
k: “桶”的個數
In-place(內排序): 占用常數內存,不占用額外內存(所有排序操作都在內存中完成)
Out-place(外排序): 占用額外內存(由於數據太大,因此把數據放在磁盤中,而排序通過磁盤和內存的數據傳輸才能進行)
排序算法案例:
①冒泡排序
本質:通過for循環 利用兩兩比較的方式依次找到相對大值放在右邊,實現每次循環后,將未排序的數組部分最大值放在右邊
代碼說明:
public static void sort(int[] arr) { //外層循環控制比較的次數 arr[arr.leng-1]后面沒有比較的數據 比較數[0,arr.leng-1-1] for (int i = 0; i < arr.length-1; i++){ //內層循環控制到達位置 被比較數據[1,arr.leng-1-i(已經比較過的次數)] for (int j = 1; j < arr.length - 1 - i; j++){ if (arr[j ] < arr[j]) { int temp = arr[j ]; arr[j] = arr[j]; arr[j] = temp; } } } }
②選擇排序
本質:每次循環,找到該次循環的最小值,記錄索引排序

public static void sort(int[] array) { //外層循環控制循環次數 for (int i = 0; i < array.length; i++) { int minIndex = i; //內層循環 找到循環時的最小值得索引 for (int j = i; j < array.length; j++) { if (array[j] < array[minIndex]) //找到最小的數 minIndex = j; //將最小數的索引保存 } int temp = array[minIndex]; array[minIndex] = array[i]; array[i] = temp; } }
③插入排序:
本質:通過循環依次從右到左依次判斷,進入插入對應位置,已形成循環次數對應索引左邊的有序與右邊的無序數據
代碼說明:
public static void sort(int[] array) { int current; //外層循環控制循環次數 for (int i = 0; i < array.length - 1; i++) { current = array[i + 1]; int preIndex = i; //內層循環控制索引數據右移或插入 while (preIndex >= 0 && current < array[preIndex]) { array[preIndex + 1] = array[preIndex]; preIndex--; } array[preIndex + 1] = current; } }
④希爾排序:
希爾排序是把記錄按下表的一定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。
具體算法描述:
選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
按增量序列個數k,對序列進行k 趟排序;
每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。
代碼說明:
public static void sort(int[] array) {
int len = array.length;
int temp, gap = len / 2;
while (gap > 0) {
for (int i = gap; i < len; i++) {
temp = array[i];
int preIndex = i - gap;
while (preIndex >= 0 && array[preIndex] > temp) {
array[preIndex + gap] = array[preIndex];
preIndex -= gap;
}
array[preIndex + gap] = temp;
}
gap /= 2;
}
}
⑤歸並排序
算法描述
把長度為n的輸入序列分成兩個長度為n/2的子序列;
對這兩個子序列分別采用歸並排序;
將兩個排序好的子序列合並成一個最終的排序序列。
代碼說明: 補充:暫時未發布該隨筆引用數據參數傳遞與基本數據參數傳遞
/*** 歸並排序 由於歸並排序並不是在原有數組進行操作,應該返回新數組地址*/ public static int[] MergeSort(int[] array) { if (array.length < 2) return array; int mid = array.length / 2; nt[] left = java.util.Arrays.copyOfRange(array, 0, mid); int[] right = java.util.Arrays.copyOfRange(array, mid, array.length); return merge(MergeSort(left), MergeSort(right)); } /*** 歸並排序——將兩段排序好的數組結合成一個排序數組 */ public static int[] merge(int[] left, int[] right) { int[] result = new int[left.length + right.length]; for (int index = 0, i = 0, j = 0; index < result.length; index++) { if (i >= left.length) result[index] = right[j++]; else if (j >= right.length) result[index] = left[i++]; else if (left[i] > right[j]) result[index] = right[j++]; else result[index] = left[i++]; } return result; }
⑥快速排序(Quick Sort)
算法描述:
快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。
- 從數列中挑出一個元素,稱為 “基准”(pivot);
- 重新排序數列,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分區退出之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作;
- 遞歸地(recursive)把小於基准值元素的子數列和大於基准值元素的子數列排序。
代碼說明
/*** 快速排序方法*/ public static int[] QuickSort(int[] array, int start, int end) { if (array.length < 1 || start < 0 || end >= array.length || start > end) return null; int smallIndex = partition(array, start, end); if (smallIndex > start) QuickSort(array, start, smallIndex - 1); if (smallIndex < end) QuickSort(array, smallIndex + 1, end); return array; } /*** 快速排序算法——partition*/ public static int partition(int[] array, int start, int end) { int pivot = (int) (start + Math.random() * (end - start + 1)); int smallIndex = start - 1; swap(array, pivot, end); for (int i = start; i <= end; i++) if (array[i] <= array[end]) { smallIndex++; if (i > smallIndex) swap(array, i, smallIndex); } return smallIndex; } /**交換數組內兩個元素*/ public static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; }
⑦堆排序(Heap Sort)
堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
算法描述
- 將初始待排序關鍵字序列(R1,R2….Rn)構建成大頂堆,此堆為初始的無序區;
- 將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(R1,R2,……Rn-1)和新的有序區(Rn),且滿足R[1,2…n-1]<=R[n];
- 由於交換后新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,……Rn-1)調整為新堆,然后再次將R[1]與無序區最后一個元素交換,得到新的無序區(R1,R2….Rn-2)和新的有序區(Rn-1,Rn)。不斷重復此過程直到有序區的元素個數為n-1,則整個排序過程完成。
代碼說明
注意:這里用到了完全二叉樹的部分性質:詳情見《數據結構二叉樹知識點總結》
//聲明全局變量,用於記錄數組array的長度; static int len; /*** 堆排序算法*/ public static int[] HeapSort(int[] array) { len = array.length; if (len < 1) return array; //1.構建一個最大堆 buildMaxHeap(array); //2.循環將堆首位(最大值)與末位交換,然后在重新調整最大堆 while (len > 0) { swap(array, 0, len - 1); len--; adjustHeap(array, 0); } return array; } /** * 建立最大堆 * * @param array */ public static void buildMaxHeap(int[] array) { //從最后一個非葉子節點開始向上構造最大堆 for (int i = (len/2 - 1); i >= 0; i--) { //感謝 @讓我發會呆 網友的提醒,此處應該為 i = (len/2 - 1) adjustHeap(array, i); } } /*** 調整使之成為最大堆*/ public static void adjustHeap(int[] array, int i) { int maxIndex = i; //如果有左子樹,且左子樹大於父節點,則將最大指針指向左子樹 if (i * 2 < len && array[i * 2] > array[maxIndex]) maxIndex = i * 2; //如果有右子樹,且右子樹大於父節點,則將最大指針指向右子樹 if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex]) maxIndex = i * 2 + 1; //如果父節點不是最大值,則將父節點與最大值交換,並且遞歸調整與父節點交換的位置。 if (maxIndex != i) { swap(array, maxIndex, i); adjustHeap(array, maxIndex); } }
⑧計數排序(CountingSort)
計數排序的核心在於將輸入的數據值轉化為鍵存儲在額外開辟的數組空間中。 作為一種線性時間復雜度的排序,計數排序要求輸入的數據必須是有確定范圍的整數。
計數排序(Counting sort)是一種穩定的排序算法。計數排序使用一個額外的數組C,其中第i個元素是待排序數組A中值等於i的元素的個數。然后根據數組C來將A中的元素排到正確的位置。它只能對整數進行排序。
算法描述--每個桶只存儲單一鍵值
- 找出待排序的數組中最大和最小的元素;
- 統計數組中每個值為i的元素出現的次數,存入數組C的第i項;
- 對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加);
- 反向填充目標數組:將每個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1。
代碼實現
/*** 計數排序 */ public static int[] CountingSort(int[] array) { if (array.length == 0) return array; int bias, min = array[0], max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] > max) max = array[i]; if (array[i] < min) min = array[i]; } bias = 0 - min; int[] bucket = new int[max - min + 1]; Arrays.fill(bucket, 0); for (int i = 0; i < array.length; i++) { bucket[array[i] + bias]++; } int index = 0, i = 0; while (index < array.length) { if (bucket[i] != 0) { array[index] = i - bias; bucket[i]--; index++; } else i++; } return array; }
⑨桶排序(BucketSort)
桶排序是計數排序的升級版。它利用了函數的映射關系,高效與否的關鍵就在於這個映射函數的確定。
桶排序 (Bucket sort)的工作的原理:假設輸入數據服從均勻分布,將數據分到有限數量的桶里,每個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排
算法描述--每個桶存儲一定范圍的數值
- 人為設置一個BucketSize,作為每個桶所能放置多少個不同數值(例如當BucketSize==5時,該桶可以存放{1,2,3,4,5}這幾種數字,但是容量不限,即可以存放100個3);
- 遍歷輸入數據,並且把數據一個一個放到對應的桶里去;
- 對每個不是空的桶進行排序,可以使用其它排序方法,也可以遞歸使用桶排序;
- 從不是空的桶里把排好序的數據拼接起來。
注意,如果遞歸使用桶排序為各個桶排序,則當桶數量為1時要手動減小BucketSize增加下一循環桶的數量,否則會陷入死循環,導致內存溢出。
代碼說明
/*** 桶排序*/ public static ArrayList<Integer> BucketSort(ArrayList<Integer> array, int bucketSize) { if (array == null || array.size() < 2) return array; int max = array.get(0), min = array.get(0); // 找到最大值最小值 for (int i = 0; i < array.size(); i++) { if (array.get(i) > max) max = array.get(i); if (array.get(i) < min) min = array.get(i); } int bucketCount = (max - min) / bucketSize + 1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount); ArrayList<Integer> resultArr = new ArrayList<>(); for (int i = 0; i < bucketCount; i++) { bucketArr.add(new ArrayList<Integer>()); } for (int i = 0; i < array.size(); i++) { bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i)); } for (int i = 0; i < bucketCount; i++) { if (bucketSize == 1) { // 如果帶排序數組中有重復數字時 for (int j = 0; j < bucketArr.get(i).size(); j++) resultArr.add(bucketArr.get(i).get(j)); } else { if (bucketCount == 1) bucketSize--; ArrayList<Integer> temp = BucketSort(bucketArr.get(i), bucketSize); for (int j = 0; j < temp.size(); j++) resultArr.add(temp.get(j)); } } return resultArr; }
⑩基數排序(RadixSort)
基數排序也是非比較的排序算法,對每一位進行排序,從最低位開始排序,復雜度為O(kn),為數組長度,k為數組中的數的最大的位數;
基數排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最后的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。基數排序基於分別排序,分別收集,所以是穩定的。
算法描述 --根據鍵值的每位數字來分配桶
- 取得數組中的最大數,並取得位數;
- arr為原始數組,從最低位開始取每個位組成radix數組;
- 對radix進行計數排序(利用計數排序適用於小范圍數的特點);
代碼實現
/*** 基數排序/ public static int[] RadixSort(int[] array) { if (array == null || array.length < 2) return array; // 1.先算出最大數的位數; int max = array[0]; for (int i = 1; i < array.length; i++) { max = Math.max(max, array[i]); } int maxDigit = 0; while (max != 0) { max /= 10; maxDigit++; } int mod = 10, div = 1; ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>(); for (int i = 0; i < 10; i++) bucketList.add(new ArrayList<Integer>()); for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) { for (int j = 0; j < array.length; j++) { int num = (array[j] % mod) / div; bucketList.get(num).add(array[j]); } int index = 0; for (int j = 0; j < bucketList.size(); j++) { for (int k = 0; k < bucketList.get(j).size(); k++) array[index++] = bucketList.get(j).get(k); bucketList.get(j).clear(); } } return array; }
參考鏈接:
https://www.cnblogs.com/guoyaohua/p/8600214.html;
https://www.cnblogs.com/morethink/p/8419151.html