標籤:選擇 堆排 import 分析 需要 特殊 ref public href
目錄
- 排序演算法
- 一、時間複雜度為O(n^2)的排序演算法
- 1.1 冒泡排序(bubbleSort)
- 1.2 選擇排序(selectionSort)
- 1.3 插入排序(insertionSort)
- 二、時間複雜度為O(log(n))的排序演算法
- 2.1 歸併排序(mergeSort)
- 2.2 快速排序(quickSort)
- 2.3 堆排序
- 2.4 希爾排序(shellSort)
- 三、 桶排序(時間複雜度O(n))
排序演算法
排序演算法之間的比較:
| 排序演算法 |
最差時間分析 |
平均時間複雜度 |
穩定度 |
空間複雜度 |
| 冒泡排序 |
O(n^2) |
O(n^2) |
穩定 |
O(1) |
| 選擇排序 |
O(n^2) |
O(n^2) |
不穩定 |
O(1) |
| 插入排序 |
O(n^2) |
O(n^2) |
穩定 |
O(1) |
| 歸併排序 |
O(nlogn) |
O(nlogn) |
穩定 |
O(n) |
| 快速排序 |
O(n^2) |
O(nlogn) |
不穩定 |
O(1) |
| 堆排序 |
O(n^2) |
O(n*logn) |
不穩定 |
O(1) |
| 希爾排序 |
O(n^2) |
O(n^1.3) |
不穩定 |
O(1) |
| 桶排序 |
O(n) |
O(n) |
穩定 |
O(n) |
一、時間複雜度為O(n^2)的排序演算法1.1 冒泡排序(bubbleSort)
- 進行n-1輪排序,每輪排序數組中元素依次兩兩交換, 擷取最大的元素;
- 時間複雜度 O(n^2), 空間複雜度O(1);
public class BubbleSort { public int[] bubbleSort(int[] A) { // write code here if(A == null || A.length < 2) return A; for(int i = 0; i < A.length - 1; i++){ for(int j = 0; j < A.length - i - 1; j++){ if(A[j] > A[j+1]){ swap(A, j, j+1); } } } return A; } public void swap(int[] A, int i1, int i2){ int tmp = A[i1]; A[i1] = A[i2]; A[i2] = tmp; }}
1.2 選擇排序(selectionSort)
- 進行n-1輪交換, 每輪交換擷取最小的元素
- 時間複雜度 O(n^2), 空間複雜度O(1);
public class SelectionSort { public int[] selectionSort(int[] A) { // write code here if(A == null || A.length < 2) return A; for(int i = 0; i < A.length - 1; i++){ for(int j = i + 1; j < A.length; j ++){ if(A[i] > A[j]){ swap(A, i, j); } } } return A; } public void swap(int[] A, int i1, int i2){ int tmp = A[i1]; A[i1] = A[i2]; A[i2] = tmp; }}
1.3 插入排序(insertionSort)
- 時間複雜度 O(n^2), 空間複雜度O(1);
- 當數組元素有序, 時間複雜度O(n)
public class InsertionSort { public int[] insertionSort(int[] A, int n) { // write code here for(int i = 1; i < A.length; i++){ int key = A[i]; int pre = i - 1; while(pre >= 0 && A[pre] > key){ A[pre+1] = A[pre--]; } A[pre + 1] = key; } return A; }}
二、時間複雜度為O(log(n))的排序演算法2.1 歸併排序(mergeSort)
- 時間複雜度O(log(n)), 空間複雜度O(n)
- 歸併排序的時間複雜度與排序數組元素順序無關;
public class MergeSort { public int[] mergeSort(int[] A, int n) { // write code here if(A == null || A.length < 2) return A; int[] tmp = new int[A.length]; divide(A, 0, A.length - 1, tmp); return A; } // 數組合并 public void merge(int[] A, int left, int right, int mid, int[] tmp){ for(int i = left; i <= right; i++){ tmp[i] = A[i]; } int l1 = left, l2 = mid+1, index = left; for(int i = left; i <= right; i++){ if(l1 > mid){ A[index++] = tmp[l2++]; }else if(l2 > right){ A[index++] = tmp[l1++]; }else if(tmp[l1] < tmp[l2]){ A[index++] = tmp[l1++]; }else{ A[index++] = tmp[l2++]; } } } public void divide(int[] A, int left, int right, int[] tmp){ if(left < right){ // 與 mid = (left + right)/2 相比, 可以防止left+right超出int範圍 int mid = left + (right-left)/2; divide(A, left, mid, tmp); divide(A, mid+1, right, tmp); merge(A, left, right, mid, tmp); } }}
2.2 快速排序(quickSort)
最優情況: 元素分布均勻, 每次都能取到中間位置
- 時間複雜度O(log(n)), 空間複雜度O(logn);
最壞情況: 時間複雜度O(n^2), 空間複雜度O(n), 以下三種最壞情況
- 數組已經是正序(same order)排過序的。
- 數組已經是倒序排過序的。
- 所有的元素都相同(1、2的特殊情況)
public class QuickSort { public int[] quickSort(int[] A, int n) { // write code here quick(A, 0, A.length - 1); return A; } public void quick(int[] A, int left, int right){ if(left >= right) return ; int key = A[left]; int head = left, tail = right+1; while(true){ while(A[++head] <= key) if(head == right) break; while(A[--tail] > key) if(tail == left) break; if(head >= tail) break; int tmp = A[head]; A[head] = A[tail]; A[tail] = tmp; } A[left] = A[tail]; A[tail] = key; quick(A, left, tail-1); quick(A, tail + 1,right); }}
2.3 堆排序
時間複雜度O(nlog(n)), 空間複雜度O(1)
最差情況, 數組元素已經是從小到大有序的, 時間複雜度O(n^2)
public class HeapSort { public int[] heapSort(int[] A, int n) { // 建堆 for(int i = A.length/2; i >= 0; i--){ heapAdjust(A, i, A.length); } for(int i = A.length - 1; i >= 0; i--){ int head = A[0]; A[0] = A[i]; A[i] = head; heapAdjust(A, 0, i); } return A; } // 堆調整 O(log(n)) public void heapAdjust(int[] A, int parent, int len){ int head = A[parent]; while(parent*2+1 < len){ int childLeft = parent*2 + 1; if(childLeft + 1 < len && A[childLeft + 1] > A[childLeft]){ childLeft++; } if(A[childLeft] < head) break; A[parent] = A[childLeft]; parent = childLeft; } A[parent] = head; }}
2.4 希爾排序(shellSort)
public class ShellSort { public int[] shellSort(int[] A, int n) { for(int i = n; i >= 1; i /= 2){ for(int j = i; j < n; j++){ int key = A[j]; int pre = j - i; while(pre >= 0 && A[pre] > key){ A[pre + i] = A[pre]; pre -= i; } A[pre + i] = key; } } return A; }}
三、 桶排序(時間複雜度O(n))3.1 計數排序
- 計數排序適用於元素在一定有限範圍內的數組;
- 時間複雜度O(n), 空間複雜度 O(m)
import java.util.*;public class CountingSort { public int[] countingSort(int[] A, int n) { if(A == null || A.length < 2) return A; int min = A[0], max = A[0]; for(int i = 0; i < A.length; i++){ min = Math.min(min, A[i]); max = Math.max(max, A[i]); } int[] bucket = new int[max - min + 1]; // 入桶 for(int i = 0; i < A.length; i++){ bucket[A[i] - min]++; } // 出桶 int index = 0; for(int i = 0; i < bucket.length; i++){ while(bucket[i]-- > 0){ A[index++] = i + min; } } return A; }}
3.2 基數排序
// 保證元素均小於等於2000的基數排序public class RadixSort { public int[] radixSort(int[] A, int n) { if(A == null || n < 2) return A; int[][] bucket = new int[10][n]; // 建立10個桶,每個桶需要的長度都有可能為n int[] count = new int[10]; // 記錄每個對應桶中裝入的元素個數 for(int i = 1; i <= 1000; i *= 10) { for(int k = 0; k < n; k++) { int num = A[k] / i % 10; // 擷取位元上對應數字 bucket[num][count[num]++] = A[k]; } // 從bucket中將資料倒回A中 int index = 0; for(int k = 0; k < 10; k++) { for(int j = 0; j < count[k]; j++) { A[index++] = bucket[k][j]; } count[k] = 0; // 重設為0 } } return A; }}
八大排序演算法