筆記
排序算法
分類
基本排序 [快速無Bug]
冒泡
插入
高級排序 [必考]
歸併
快速
拓撲 Topological
其餘重點 [須要研究]
堆排序
桶排序
冒泡排序 [穩定]
耗時: O(n^2)
空間: S(1)
核心思想: 從頭開始,相鄰兩兩比較交換,直到尾部
=> 這一輪中最大/小元素 冒泡到 Array 尾部 [橫着看 吐泡泡]
選擇排序 [非穩定]
耗時: O(n^2/2)
空間: S(1)
和冒泡相似,但不須要頻繁兩兩鄰近賦值操做,只須要更新賦值當前最大/小 => 少許賦值
插入排序 [穩定]
耗時: O(n) / O(n^2/2)
空間: S(1)
核心思想: 從第二個元素開始,向前比較,[後部數組] 右/後移
在原後部第一元素位置 覆蓋 插入元素
歸併排序 [穩定]
耗時: O(nlogn) / O(nlogn)
空間: S(2n)
核心思想: [分治] 將複雜問題拆分紅若干的子問題,而後一一解決
一直將子數組拆分紅更小的子數組(2路=>2個子數組),直到數組中只有1個元素[先局部有序]
而後再合併各個局部有序子數組
=> mergeSort(){
if (hi <= lo) return; // 遞歸終止條件
int mid = lo + (hi - lo) / 2; // 切分項
mergeSort(左半)
mergeSort(右半)
merge() // 合併排序
}
快速排序 [非穩]
耗時: O(nlogn) / O(n^2/2)
空間: S(nlogn)
核心思想: [分治] 將數組 根據 切分項V 分爲2塊,不斷交換左側大於V 及 右側小於V 的2個數組項i,j
也就是粗排序,交換左右不合規元素 => 區域有序,而後再進行遞歸切分,實現更細區域有序
最終,遞歸到底達成全局有序 [反着來的歸併排序]
=> quickSort(){
if (hi <= lo) return; // 遞歸終止條件
partition() // 粗排序+切分項
quickSort(左半)
quickSort(右半)
}
與歸併區別:
同
兩者很像 [最好狀況下將全部內容精確地分紅兩半。這使它有點像「歸併排序」]
知足歸併排序——分而治之遞歸算法:Cn = 2Cn + n 公式 ==> N*lgN
異
1.Partition in-place 就地分區
使用一個額外數組來更容易分區及實現Stable,但這不值得
QS 更快 更節省空間,這是QS優點所在
2.shuffleing 是必要的
對於保證性能來講,失去會致使性能受損。切分項也能夠隨機
隨機性是爲了平衡最壞狀況下的性能 => 最壞狀況下的機率保證
3.排序與遞歸順序
QS [先粗排序再遞歸切分]
MS [先遞歸切分再合併排序]
區域性有序: 區域內 無序,但 均大於 x / 均小於 x
局部性有序: 局部內 有序, 全局無序
堆排序 [非穩]
耗時: O(nlogn) / O(2nlogn + 2n)
空間: S(n)
用途: 在現實嵌入式較少資源狀況下惟一使用的原地排序算法
最重要的是它能在插入、刪除 混合操做動態場景下能保持 對數級別的計算時間
數據結構: 優先隊列([二叉堆]實現(數組))
二叉堆: [堆有序]的[徹底[二叉樹]]結構元素,在數組中按層級存儲
堆有序: 每一個節點都大於等於它的兩個子節點時,稱爲堆有序
二叉樹: 空子節點,或者鏈接指向左右子節點(也是一顆二叉樹)
徹底二叉樹: 完美平衡 => 若是樹高 4,則一二三層均是完整左右子樹,第四層能夠不完美
徹底二叉樹性質:
1.徹底二叉樹可根據 節點數 計算 樹高 [ N nodes => lgN ]
2.只用數組而不須要指針就能夠表示/實現
二叉堆性質:
1.不使用數組索引0
2.從任意節點向上,都能獲取一列 非遞減的元素
從任意節點向下,都能獲取一列 非遞增的元素
3.二叉堆中索引位置爲 k 的元素的父親爲 k/2 向下取整,其子節點爲 2k 及 2k+1
核心思想:
構建 MaxPriorityQueue 二叉堆實現的優先隊列[其實數組就行],並重復刪除最大元素/堆頂元素
=> 即兩階段: 1.堆構造階段(堆有序); 2.下沉階段 (刪除堆頂/輸出最大元素)
與快排歸併區別
異
1.全局無序,條狀有序(從下至上/從上至下一條線)
2.經過不斷 輸出(delete/sink) 全局最大/小 至數組末尾 實現 排序
拓撲排序 [頂點排序]
前提: 必須有向圖; 圖裏沒有環; 沒有孤島 【統計前驅/入度; 廣度優先搜索遍歷;】
耗時: O(n)
空間: S(n)
用途: 理清具備依賴關係的任務 [將圖論的頂點按照相連的性質進行排序]
void sort(){
Queue<Integer> q = new LinkedList();
for(int v = 0; v < V; v++)
if(indegree[v] == 0) q.add(v);
while(!q.isEmpty()){
int v = q.poll();
print(v);
for(int u = 0; u < adj[v].length; u++)
if(--indegree[u] == 0) q.add(u);
}
}
時間複雜度比較圖
inplace? stable? worst average best remarks
selection x N^2/2 N^2/2 N^2/2 N次交換
insertion x x N^2/2 N^2/4 N 適用於小量(15)元素
shell x ? ? N tight code, subquadratic
quick x N^2/2 2NlogN NlogN probabilistic guarantee fastest
3-way quick x N^2/2 2NlogN N 針對重複Key提高
merge x NlogN NlogN NlogN 穩定NlogN(2種含義)
heap x 2NlogN 2NlogN NlogN 代碼簡單 插刪混合對數級別
實現源碼
@SuppressWarnings({"unused"})
public class Sort {
public static void main(String[] args) {
int[] a = {0, 5, 4, 6, 2, 1};
for (int i : a) {
System.out.println(i);
}
}
static int[] popSort(int[] arr) {
boolean hasChanged = true;
for (int n = 1; n < arr.length && hasChanged; n++) {
hasChanged = false;
for (int i = 0; i < arr.length - n; i++) {
int j = i + 1, tmp;
if (arr[i] > arr[j]) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
hasChanged = true;
}
}
}
return arr;
}
static int[] insertSort1(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int k = i;
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > arr[k]) {
int tmp = arr[k];
arr[k] = arr[j];
arr[j] = tmp;
k = j;
}
}
}
return arr;
}
static int[] insertSort2(int[] arr) {
for (int i = 1, j, current; i < arr.length; i++) {
current = arr[i];
for (j = i - 1; j >= 0 && arr[j] > current; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = current;
}
return arr;
}
static void mergeSortEntrance(int[] a) {
int[] aux = new int[a.length];
mergeSort(a, aux, 0, a.length - 1);
}
static void mergeSort(int[] a, int[] aux, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
mergeSort(a, aux, lo, mid);
mergeSort(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
}
static void merge(int[] a, int[] aux, int lo, int mid, int hi) {
if (hi + 1 - lo >= 0) System.arraycopy(a, lo, aux, lo, hi + 1 - lo);
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++) {
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (less(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
}
}
static boolean less(int a, int b) {
return a < b;
}
static void quickSortEntrance(int[] a) {
quick(a, 0, a.length - 1);
}
static void quick(int[] a, int lo, int hi) {
if (hi <= lo) return;
int j = partition(a, lo, hi);
quick(a, lo, j - 1);
quick(a, j + 1, hi);
}
static int partition(int[] a, int lo, int hi) {
int i = lo, j = hi + 1;
int v = a[lo];
while (true) {
while (less(a[++i], v)) if (i == hi) break;
while (less(v, a[--j])) if (j == lo) break;
if (i >= j) break;
exch(a, i, j);
}
exch(a, lo, j);
return j;
}
static void exch(int[] a, int left, int right) {
int tmp = a[left];
a[left] = a[right];
a[right] = tmp;
}
static void heapSort(int[] a) {
int N = a.length-1;
for (int k = N / 2; k >= 1; k--) {
sink(a, k, N);
}
while (N > 1) {
exch(a, 1, N);
sink(a, 1, --N);
}
}
static void sink(int[] a, int k, int N) {
while (2 * k <= N) {
int j = 2 * k;
if (j < N && less(a[j], a[j + 1])) j++;
if (!less(a[k], a[j])) break;
exch(a, k, j);
k = j;
}
}
}