十大排序算法图解
排序基礎(chǔ)知識(shí)
排序的定義
排序, 就是重新排列表中的元素, 使表中的元素滿足按關(guān)鍵字遞增或遞減的過程。為了査找方便,通常要求計(jì)算機(jī)中的表是按關(guān)鍵字有序的 。 排序的確切定義如下:
輸 入: n個(gè) 記 錄 R1,R2,R3…Rn, 對(duì)對(duì)應(yīng)的關(guān)鍵字為K1,K2,K3…Kn
輸出: 輸入序列的一個(gè)重排R1’,R2’,R3’…Rn’, 使得有K1’ ≤ K2’ ≤ K3’… ≤ Kn’ (其中 ≤可以換成其它的比較大小符號(hào))。
算法的穩(wěn)定性:
若待排序表中有兩個(gè)元素 Ri 和 Rj,其對(duì)應(yīng)的關(guān)鍵字 keyi = kcyj , 且在排序前
Ri 在 Rj的前面。使用某一排序算法排序后, Ri 仍然在 Rj 的前面盡的前面, 則稱這個(gè)排序算法是穩(wěn)定的。否則稱排序算法是不穩(wěn)定的。 需要注意的是, 算法是否具有穩(wěn)定性并不能衡量—個(gè)算法的優(yōu)劣,它主要足對(duì)算法的性質(zhì)進(jìn)行描述。
只需舉出一組關(guān)徤字的實(shí)例, 即可說明一個(gè)算法是不穩(wěn)定的。
時(shí)間復(fù)雜度:[1](來自百度百科)
分析:隨著模塊n的增大,算法執(zhí)行的時(shí)間的增長率和 f(n) 的增長率成正比,所以 f(n) 越小,算法的時(shí)間復(fù)雜度越低,算法的效率越高。
空間復(fù)雜度:[2](來自百度百科)
類似于時(shí)間復(fù)雜度的討論,一個(gè)算法的空間復(fù)雜度 S(n) 定義為該算法所耗費(fèi)的存儲(chǔ)空間,它也是問題規(guī)模n的函數(shù)。漸近空間復(fù)雜度也常常簡稱為空間復(fù)雜度。空間復(fù)雜度(SpaceComplexity)是對(duì)一個(gè)算法在運(yùn)行過程中臨時(shí)占用存儲(chǔ)空間大小的量度。一個(gè)算法在計(jì)算機(jī)存儲(chǔ)器上所占用的存儲(chǔ)空間,包括存儲(chǔ)算法本身所占用的存儲(chǔ)空間,算法的輸入輸出數(shù)據(jù)所占用的存儲(chǔ)空間和算法在運(yùn)行過程中臨時(shí)占用的存儲(chǔ)空間這三個(gè)方面。算法的輸入輸出數(shù)據(jù)所占用的存儲(chǔ)空間是由要解決的問題決定的,是通過參數(shù)表由調(diào)用函數(shù)傳遞而來的,它不隨本算法的不同而改變。存儲(chǔ)算法本身所占用的存儲(chǔ)空間與算法書寫的長短成正比,要壓縮這方面的存儲(chǔ)空間,就必須編寫出較短的算法。算法在運(yùn)行過程中臨時(shí)占用的存儲(chǔ)空間隨算法的不同而異,有的算法只需要占用少量的臨時(shí)工作單元,而且不隨問題規(guī)模的大小而改變,我們稱這種算法是“就地"進(jìn)行的,是節(jié)省存儲(chǔ)的算法,有的算法需要占用的臨時(shí)工作單元數(shù)與解決問題的規(guī)模 n 有關(guān),它隨著n的增大而增大,當(dāng)n較大時(shí),將占用較多的存儲(chǔ)單元,例如快速排序和歸并排序算法就屬于這種情況。
算法的分類可以按照是否是比較類的算法來分類,也可以按照排序過程中數(shù)據(jù)是否都存在于內(nèi)存中來分類:
如下:
按照內(nèi)部排序和外部排序分類:
按照是否為比較類的排序來分:
排序算法比較類排序非比較類排序交換排序冒泡排序快速排序插入排序簡單插入排序希爾排序選擇排序簡單選擇排序堆排序歸并排序二路歸并排序多路歸并排序計(jì)數(shù)排序桶排序基數(shù)排序算法時(shí)間復(fù)雜度
| 插入排序 | O(n2)O(n^2)O(n2) | O(n2)O(n^2)O(n2) | O(n)O(n)O(n) | O(1)O(1)O(1) | 穩(wěn)定 |
| 希爾排序 | O(n1.3)O(n^{1.3})O(n1.3) | O(n2)O(n^2)O(n2) | O(n)O(n)O(n) | O(1)O(1)O(1) | 不穩(wěn)定 |
| 選擇排序 | O(n2)O(n^2)O(n2) | O(n2)O(n^2)O(n2) | O(n2)O(n^2)O(n2) | O(1)O(1)O(1) | 不穩(wěn)定 |
| 堆排序 | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(1)O(1)O(1) | 不穩(wěn)定 |
| 冒泡排序 | O(n2)O(n^2)O(n2) | O(n2)O(n^2)O(n2) | O(n)O(n)O(n) | O(1)O(1)O(1) | 穩(wěn)定 |
| 快速排序 | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(n2)O(n^2)O(n2) | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | 不穩(wěn)定 |
| 歸并排序 | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(nlog?2n)O(n\log_2{n})O(nlog2?n) | O(n)O(n)O(n) | 穩(wěn)定 |
| 計(jì)數(shù)排序 | O(n+k)O(n+k)O(n+k) | O(n+k)O(n+k)O(n+k) | O(n+k)O(n+k)O(n+k) | O(n+k)O(n+k)O(n+k) | 穩(wěn)定 |
| 基數(shù)排序 | O(n×k)O(n\times k)O(n×k) | O(n×k)O(n\times k)O(n×k) | O(n×k)O(n\times k)O(n×k) | O(n+k)O(n+k)O(n+k) | 穩(wěn)定 |
| 桶排序 | O(n+k)O(n+k)O(n+k) | O(n2)O(n^2)O(n2) | O(n)O(n)O(n) | O(n+k)O(n+k)O(n+k) | 穩(wěn)定 |
1. 插入排序(Insertion Sort)
插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工作原理是通過構(gòu)建有序序列,對(duì)于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入。
算法描述
一般來說,插入排序都采用in-place在數(shù)組上實(shí)現(xiàn)。具體算法描述如下:
- 從第一個(gè)元素開始,該元素可以認(rèn)為已經(jīng)被排序;
- 取出下一個(gè)元素,在已經(jīng)排序的元素序列中從后向前掃描;
- 如果該元素(已排序)大于新元素,將該元素移到下一位置;
- 重復(fù)步驟3,直到找到已排序的元素小于或者等于新元素的位置;
- 將新元素插入到該位置后;
- 重復(fù)步驟2~5。
動(dòng)圖演示
C代碼實(shí)現(xiàn)
算法分析
插入排序在實(shí)現(xiàn)上,通常采用in-place排序(即只需用到O(1)的額外空間的排序),因而在從后向前掃描過程中,需要反復(fù)把已排序元素逐步向后挪位,為最新元素提供插入空間。
2. 希爾排序
1959年Shell發(fā)明,第一個(gè)突破O(n2)的排序算法,是簡單插入排序的改進(jìn)版。它與插入排序的不同之處在于,它會(huì)優(yōu)先比較距離較遠(yuǎn)的元素。希爾排序又叫縮小增量排序。
算法描述
先將整個(gè)待排序的記錄序列分割成為若干子序列分別進(jìn)行直接插入排序,具體算法描述:
動(dòng)圖演示
C代碼實(shí)現(xiàn)
算法分析
希爾排序是基于插入排序的以下兩點(diǎn)性質(zhì)而提出改進(jìn)方法的:
- 插入排序在對(duì)幾乎已經(jīng)排好序的數(shù)據(jù)操作時(shí), 效率高, 即可以達(dá)到線性排序的效率
- 插入排序在對(duì)幾乎已經(jīng)排好序的數(shù)據(jù)操作時(shí), 效率高, 即可以達(dá)到線性排序的效
但插入排序一般來說是低效的, 因?yàn)椴迦肱判蛎看沃荒軐?shù)據(jù)移動(dòng)一位
時(shí)間復(fù)雜度:最壞情況下為O(n^2),平均時(shí)間復(fù)雜度為O(nlogn);
空間復(fù)雜度:歸并排序需要一個(gè)大小為1的臨時(shí)存儲(chǔ)空間用以保存合并序列,所以空間復(fù)雜度為O(1)
算法穩(wěn)定性:從上面圖片中可以看出,數(shù)字5在排序后交換了位置,所以它是不穩(wěn)定的算法。
3. 選擇排序(Selection Sort)
選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續(xù)尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
算法描述
n個(gè)記錄的直接選擇排序可經(jīng)過n-1趟直接選擇排序得到有序結(jié)果。具體算法描述如下:
動(dòng)圖演示
C語言實(shí)現(xiàn)
算法分析
表現(xiàn)最穩(wěn)定的排序算法之一,因?yàn)闊o論什么數(shù)據(jù)進(jìn)去都是O(n2)的時(shí)間復(fù)雜度,所以用到它的時(shí)候,數(shù)據(jù)規(guī)模越小越好。唯一的好處可能就是不占用額外的內(nèi)存空間了吧。理論上講,選擇排序可能也是平時(shí)排序一般人想到的最多的排序方法了吧。
4. 堆排序
堆排序(Heapsort)是指利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法。堆積是一個(gè)近似完全二叉樹的結(jié)構(gòu),并同時(shí)滿足堆積的性質(zhì):即子結(jié)點(diǎn)的鍵值或索引總是小于(或者大于)它的父節(jié)點(diǎn)。
算法描述
動(dòng)圖演示
代碼實(shí)現(xiàn):
算法分析:
堆排序是一種選擇排序,整體主要由構(gòu)建初始堆+交換堆頂元素和末尾元素并重建堆兩部分組成。其中構(gòu)建初始堆經(jīng)推導(dǎo)復(fù)雜度為O(n),在交換并重建堆的過程中,需交換n-1次,而重建堆的過程中,根據(jù)完全二叉樹的性質(zhì),[log2(n-1),log2(n-2)…1]逐步遞減,近似為nlogn。所以堆排序時(shí)間復(fù)雜度一般認(rèn)為就是O(nlogn)級(jí)。
5. 冒泡排序
冒泡排序是一種簡單的排序算法。它重復(fù)地走訪過要排序的數(shù)列,一次比較兩個(gè)元素,如果它們的順序錯(cuò)誤就把它們交換過來。走訪數(shù)列的工作是重復(fù)地進(jìn)行直到?jīng)]有再需要交換,也就是說該數(shù)列已經(jīng)排序完成。這個(gè)算法的名字由來是因?yàn)樵叫〉脑貢?huì)經(jīng)由交換慢慢“浮”到數(shù)列的頂端。
算法描述
動(dòng)圖演示
C語言實(shí)現(xiàn)
算法分析
若文件的初始狀態(tài)是正序的,一趟掃描即可完成排序。所需的關(guān)鍵字比較次數(shù)C和記錄移動(dòng)次數(shù)M均達(dá)到最小值:Cmin = N - 1, Mmin = 0。所以,冒泡排序最好時(shí)間復(fù)雜度為O(N)。
若初始文件是反序的,需要進(jìn)行 N -1 趟排序。每趟排序要進(jìn)行 N - i 次關(guān)鍵字的比較(1 ≤ i ≤ N - 1),且每次比較都必須移動(dòng)記錄三次來達(dá)到交換記錄位置。在這種情況下,比較和移動(dòng)次數(shù)均達(dá)到最大值:
Cmax = N(N-1)/2 = O(N2)
Mmax = 3N(N-1)/2 = O(N2)
冒泡排序的最壞時(shí)間復(fù)雜度為O(N2)。
因此,冒泡排序的平均時(shí)間復(fù)雜度為O(N2)。
6. 快速排序
快速排序的基本思想:通過一趟排序?qū)⒋庞涗浄指舫瑟?dú)立的兩部分,其中一部分記錄的關(guān)鍵字均比另一部分的關(guān)鍵字小,則可分別對(duì)這兩部分記錄繼續(xù)進(jìn)行排序,以達(dá)到整個(gè)序列有序。
算法描述
快速排序使用分治法來把一個(gè)串(list)分為兩個(gè)子串(sub-lists)。具體算法描述如下:
動(dòng)圖演示
C語言實(shí)現(xiàn)
function quickSort(arr, left, right) {var len = arr.length,partitionIndex,left = typeof left != 'number' ? 0 : left,right = typeof right != 'number' ? len - 1 : right;if (left < right) {partitionIndex = partition(arr, left, right);quickSort(arr, left, partitionIndex-1);quickSort(arr, partitionIndex+1, right);}return arr; }function partition(arr, left ,right) { // 分區(qū)操作var pivot = left, // 設(shè)定基準(zhǔn)值(pivot)index = pivot + 1;for (var i = index; i <= right; i++) {if (arr[i] < arr[pivot]) {swap(arr, i, index);index++;} }swap(arr, pivot, index - 1);return index-1; }function swap(arr, i, j) {var temp = arr[i];arr[i] = arr[j];arr[j] = temp; }算法分析:
當(dāng)數(shù)據(jù)有序時(shí),以第一個(gè)關(guān)鍵字為基準(zhǔn)分為兩個(gè)子序列,前一個(gè)子序列為空,此時(shí)執(zhí)行效率最差。
而當(dāng)數(shù)據(jù)隨機(jī)分布時(shí),以第一個(gè)關(guān)鍵字為基準(zhǔn)分為兩個(gè)子序列,兩個(gè)子序列的元素個(gè)數(shù)接近相等,此時(shí)執(zhí)行效率最好。
所以,數(shù)據(jù)越隨機(jī)分布時(shí),快速排序性能越好;數(shù)據(jù)越接近有序,快速排序性能越差。
7. 歸并排序(Merge Sort)
歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個(gè)非常典型的應(yīng)用。將已有序的子序列合并,得到完全有序的序列;即先使每個(gè)子序列有序,再使子序列段間有序。若將兩個(gè)有序表合并成一個(gè)有序表,稱為2-路歸并。
*算法描述
動(dòng)圖演示
C語言實(shí)現(xiàn)
算法分析
歸并排序是一種穩(wěn)定的排序方法。和選擇排序一樣,歸并排序的性能不受輸入數(shù)據(jù)的影響,但表現(xiàn)比選擇排序好的多,因?yàn)槭冀K都是O(nlogn)的時(shí)間復(fù)雜度。代價(jià)是需要額外的內(nèi)存空間。
8. 計(jì)數(shù)排序
計(jì)數(shù)排序不是基于比較的排序算法,其核心在于將輸入的數(shù)據(jù)值轉(zhuǎn)化為鍵存儲(chǔ)在額外開辟的數(shù)組空間中。 作為一種線性時(shí)間復(fù)雜度的排序,計(jì)數(shù)排序要求輸入的數(shù)據(jù)必須是有確定范圍的整數(shù)。
算法描述
動(dòng)圖演示
C語言實(shí)現(xiàn)
算法分析
計(jì)數(shù)排序是一個(gè)穩(wěn)定的排序算法。當(dāng)輸入的元素是 n 個(gè) 0到 k 之間的整數(shù)時(shí),時(shí)間復(fù)雜度是O(n+k),空間復(fù)雜度也是O(n+k),其排序速度快于任何比較排序算法。當(dāng)k不是很大并且序列比較集中時(shí),計(jì)數(shù)排序是一個(gè)很有效的排序算法。
9. 基數(shù)排序
基數(shù)排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位。有時(shí)候有些屬性是有優(yōu)先級(jí)順序的,先按低優(yōu)先級(jí)排序,再按高優(yōu)先級(jí)排序。最后的次序就是高優(yōu)先級(jí)高的在前,高優(yōu)先級(jí)相同的低優(yōu)先級(jí)高的在前。
算法描述
動(dòng)圖演示
C語言實(shí)現(xiàn)
算法分析
基數(shù)排序基于分別排序,分別收集,所以是穩(wěn)定的。但基數(shù)排序的性能比桶排序要略差,每一次關(guān)鍵字的桶分配都需要O(n)的時(shí)間復(fù)雜度,而且分配之后得到新的關(guān)鍵字序列又需要O(n)的時(shí)間復(fù)雜度。假如待排數(shù)據(jù)可以分為d個(gè)關(guān)鍵字,則基數(shù)排序的時(shí)間復(fù)雜度將是O(d*2n) ,當(dāng)然d要遠(yuǎn)遠(yuǎn)小于n,因此基本上還是線性級(jí)別的。
基數(shù)排序的空間復(fù)雜度為O(n+k),其中k為桶的數(shù)量。一般來說n>>k,因此額外空間需要大概n個(gè)左右。
10. 桶排序
桶排序是計(jì)數(shù)排序的升級(jí)版。它利用了函數(shù)的映射關(guān)系,高效與否的關(guān)鍵就在于這個(gè)映射函數(shù)的確定。桶排序 (Bucket sort)的工作的原理:假設(shè)輸入數(shù)據(jù)服從均勻分布,將數(shù)據(jù)分到有限數(shù)量的桶里,每個(gè)桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續(xù)使用桶排序進(jìn)行排)。
算法描述
動(dòng)圖演示[4]
C語言實(shí)現(xiàn)
function bucketSort(arr, bucketSize) {if (arr.length === 0) {return arr;}var i;var minValue = arr[0];var maxValue = arr[0];for (i = 1; i < arr.length; i++) {if (arr[i] < minValue) {minValue = arr[i]; // 輸入數(shù)據(jù)的最小值} else if (arr[i] > maxValue) {maxValue = arr[i]; // 輸入數(shù)據(jù)的最大值}}// 桶的初始化var DEFAULT_BUCKET_SIZE = 5; // 設(shè)置桶的默認(rèn)數(shù)量為5bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; var buckets = new Array(bucketCount);for (i = 0; i < buckets.length; i++) {buckets[i] = [];}// 利用映射函數(shù)將數(shù)據(jù)分配到各個(gè)桶中for (i = 0; i < arr.length; i++) {buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);}arr.length = 0;for (i = 0; i < buckets.length; i++) {insertionSort(buckets[i]); // 對(duì)每個(gè)桶進(jìn)行排序,這里使用了插入排序for (var j = 0; j < buckets[i].length; j++) {arr.push(buckets[i][j]); }}return arr; }算法分析
桶排序最好情況下使用線性時(shí)間O(n),桶排序的時(shí)間復(fù)雜度,取決與對(duì)各個(gè)桶之間數(shù)據(jù)進(jìn)行排序的時(shí)間復(fù)雜度,因?yàn)槠渌糠值臅r(shí)間復(fù)雜度都為O(n)。很顯然,桶劃分的越小,各個(gè)桶之間的數(shù)據(jù)越少,排序所用的時(shí)間也會(huì)越少。但相應(yīng)的空間消耗就會(huì)增大。
[1]https://baike.baidu.com/item/%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6/1894057?fr=aladdin
[2]https://baike.baidu.com/item/%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6
[3]https://www.cnblogs.com/onepixel/articles/7674659.html
[4]https://www.jianshu.com/p/e605fe7a23b0
總結(jié)
- 上一篇: U-Boot 图形化配置及其原理
- 下一篇: Information_Schema系统