圖文詳述排序算法
名詞解釋:
n:數據規模
k:“桶”的個數
In-place:佔用常數內存,不佔用額外內存
Out-place:佔用額外內存
穩定性:排序後 2 個相等鍵值的順序和排序之前它們的順序相同
冒泡排序
冒泡排序(Bubble Sort),它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。冒泡排序還有一種優化算法,就是立一個 flag,當在一趟序列遍歷中元素沒有發生交換,則證明該序列已經有序。
算法步驟
-
比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
-
對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
-
針對所有的元素重複以上的步驟,除了最後一個。
-
持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
選擇排序
選擇排序是一種簡單直觀的排序算法,無論什麼數據進去都是 O(n²) 的時間複雜度。所以用到它的時候,數據規模越小越好。唯一的好處可能就是不佔用額外的內存空間。
算法步驟
-
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
-
再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。
-
重複第二步,直到所有元素均排序完畢。
插入排序
插入排序,它的工作原理是通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。
插入排序和冒泡排序一樣,也有一種優化算法,叫做拆半插入。
算法步驟
-
將第一待排序序列第一個元素看做一個有序序列,把第二個元素到最後一個元素當成是未排序序列。
-
從頭到尾依次掃描未排序序列,將掃描到的每個元素插入有序序列的適當位置。(如果待插入的元素與有序序列中的某個元素相等,則將待插入元素插入到相等元素的後面。)
希爾排序
希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序算法。
希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
-
插入排序在對幾乎已經排好序的數據操作時,效率高,即可以達到線性排序的效率;
-
但插入排序一般來說是低效的,因爲插入排序每次只能將數據移動一位;
希爾排序的基本思想是:先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序。
算法步驟
-
選擇一個增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;(gap=legth/2)
-
按增量序列個數 k,對序列進行 k 趟排序;
-
每趟排序,根據對應的增量 ti,將待排序列分割成若干長度爲 m 的子序列,分別對各子表進行直接插入排序。僅增量因子爲 1 時,整個序列作爲一個表來處理,表長度即爲整個序列的長度。
歸併排序
該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。
歸併排序的實現由兩種方法:
-
自上而下的遞歸(所有遞歸的方法都可以用迭代重寫);
-
自下而上的迭代;
算法步驟
- 把長度爲n的輸入序列分成兩個長度爲n/2的子序列;
- 對這兩個子序列分別採用歸併排序;
- 將兩個排序好的子序列合併成一個最終的排序序列。
快速排序
通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。
算法步驟
快速排序使用分治法來把一個串(list)分爲兩個子串(sub-lists)。具體算法描述如下:
- 從數列中挑出一個元素,稱爲 “基準”(pivot);
- 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區退出之後,該基準就處於數列的中間位置。這個稱爲分區(partition)操作;
- 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
堆排序
堆排序是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。堆排序可以說是一種利用堆的概念來排序的選擇排序。分爲兩種方法:
-
大頂堆:每個節點的值都大於或等於其子節點的值,在堆排序算法中用於升序排列;
-
小頂堆:每個節點的值都小於或等於其子節點的值,在堆排序算法中用於降序排列;
堆排序的平均時間複雜度爲 Ο(nlogn)。
算法步驟
-
創建一個堆 H[0……n-1];
-
把堆首(最大值)和堆尾互換;
-
把堆的尺寸縮小 1,並調用 shift_down(0),目的是把新的數組頂端數據調整到相應位置;
-
重複步驟 2,直到堆的尺寸爲 1。
計數排序
計數排序是一種非比較型整數排序算法,核心在於將輸入的數據值轉化爲鍵存儲在額外開闢的數組空間中。
作爲一種線性時間複雜度的排序,計數排序要求輸入的數據必須是有確定範圍的整數。
桶排序
桶排序是計數排序的升級版,是一種非比較型整數排序算法。它利用了函數的映射關係,高效與否的關鍵就在於這個映射函數的確定。
爲了使桶排序更加高效,我們需要做到這兩點:桶值範圍=(最大值-最小值+1)/桶數
-
在額外空間充足的情況下,儘量增大桶的數量
-
使用的映射函數能夠將輸入的 N 個數據均勻的分配到 K 個桶中
同時,對於桶中元素的排序,選擇何種比較排序算法對於性能的影響至關重要。
算法步驟
- 設置一個定量的數組當作空桶;
- 遍歷輸入數據,並且把數據一個一個放到對應的桶裏去;
- 對每個不是空的桶進行排序;
- 從不是空的桶裏把排好序的數據拼接起來。
基數排序
基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較。
由於整數也可以表達字符串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是隻能使用於整數。
算法步驟
- 取得數組中的最大數,並取得位數;
- arr爲原始數組,從最低位開始取每個位組成radix數組;
- 對radix進行計數排序(利用計數排序適用於小範圍數的特點);
基數排序 vs 計數排序 vs 桶排序
這三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差異:
-
計數排序:每個桶只存儲單一鍵值;
-
桶排序:每個桶存儲一定範圍的數值;
- 基數排序:根據鍵值的每位數字來分配桶;