現今社會,資料量不斷地增加,因此排序演算法的重要性日益提升,從大數據排序到網路搜尋引擎排序,排序演算法在我們日常生活中扮演了重要的角色。
本文將介紹10種常見的排序演算法,來和我一起學習掌握排序演算法的原理及實作過程吧!
下文介紹的 10 種排序演算法都會附上寫好的函式 這裡提供測試模版 在區域中加上各種排序演算法函式即可運作 可 EOF 輸入
氣泡排序是一種簡單的排序演算法,它的原理是依序比較相鄰的兩個元素,如果前一個元素大於後一個元素,就交換它們的位置,重複進行直到排序完成。因為排序過程中較大的元素像氣泡一樣慢慢浮到數列的右端,所以叫氣泡排序。
外層 for 控制輪數,每輪內層 for 會比較相鄰的元素並交換它們的位置,如果沒有發生交換,就代表數列已經排好序了,可以提前結束,這樣可以減少後面發生不必要的比較
時間複雜度:
每一輪排序都需要進行 次比較,而最多需要進行 輪排序。
因此,總的比較次數為
取最高次項所以時間複雜度為
氣泡排序演算法是一種簡單但效率較低的排序演算法,通常只適用於小規模數據的排序。
選擇排序是一種簡單的排序演算法,它的原理是選擇最小的元素,與第一個元素交換位置,然後在剩下的元素中選擇最小的元素,與第二個元素交換位置,以此類推,重複直到排序完成。
時間複雜度:
選擇排序的核心操作是選擇最小元素,將其與當前位置交換,而選擇最小元素需要在未排序的序列中進行線性搜索,因此需要執行 次循環和 $n4 次內層循環,時間複雜度為 。
選擇排序演算法是一種簡單但效率較低的排序演算法,通常只適用於小規模數據的排序,且較不穩定,如當元素相等時,彼此順序還是會改變。
將未排序的數據依次插入已排序序列中,形成新的已排序序列。
要遍歷 個元素,每次要往前掃瞄並搬動元素
插入排序演算法是一種簡單但效率較低的排序演算法,通常只適用於小規模數據的排序。
合併排序 Merge Sort 是一種基於分治法的排序演算法,也是一種比較經典且常用的排序演算法之一。
合併排序的主要流程包括分解、排序和合併三個步驟。首先將要排序的序列分成兩部分,分別對這兩部分進行排序,最後將排好序的兩個部分合併起來即可得到排序後的序列。在排序的過程中,通過遞歸地將序列分解成小問題,再利用合併操作將小問題的解合併成原問題的解。
呼叫時請用 MergeSort(0,v.size());
平均時間複雜度:
在平均情況下,合併排序的時間複雜度為 。這是因為在合併排序的過程中,每個數據都需要進行一次比較,而且每個數據都需要被移動到新數組中的正確位置,這樣每個數據都需要進行 級別的操作。
空間複雜度:
合併排序需要額外的存儲空間來存儲排序過程中的數據,這些存儲空間的大小與待排序數列的大小相同。
合併排序是一種高效穩定的排序算法,通過分治的思想,先拆分問題再合併解決。其時間複雜度為 ,在處理大量數據時表現良好。在實作時需要注意指針的起始值、合併區間的邊界問題等細節。
快速排序 Quick Sort 是一種常見的分治演算法,被認為是最快的排序演算法之一。它是選擇一個基準元素,通常是中間點,通過一趟排序將待排序列分為兩部分,其中一部分的所有元素都比基準元素小,另一部分的所有元素都比基準元素大,然後再按照此方法對這兩部分分別進行快速排序,直到整個序列有序。
本動畫以左邊當基準點
呼叫時請用 QuickSort(0,v.size());
平均時間複雜度:
在平均情況下,每次切分都能將數列分為近似相等的兩個子數列,快速排序的時間複雜度為 。
最壞時間複雜度:
當數列已經排好序或接近排好序時,選擇第一個或最後一個元素作為基準元素,時間複雜度會退化為 。
快速排序演算法是透過分治,達成高效率的排序演算法。它可以在短時間內對大型數據進行排序。儘管最壞情況下的時間複雜度較高,但在大多數情況下,它的表現都很優秀。
堆積排序 Heap Sort 是一種使用二元樹 Binary Tree 資料結構的排序演算法。
堆可以看作是一個完全二元樹,它具有以下兩個性質:
時間複雜度:O(nlogn)
在堆積排序中,排序的主要操作是下潛,即將堆頂元素下潛到合適的位置,這個操作的時間複雜度是 O(logn)。在排序過程中,需要執行 n 次下潛操作,因此排序的時間複雜度為 O(nlogn)。
空間複雜度:O(1)
由於堆積排序是一種原地排序算法,因此它的空間複雜度是 O(1),即不需要額外的空間。在堆排序中,只需要用到常數個變量作為中間變量,不需要額外的數組或其他數據結構。
堆排序是一種高效的排序算法,它具有良好的時間複雜度和空間複雜度,並且它只需要一個輔助空間來存儲堆,可以實現原地排序,因此堆排序在排序大數據時非常有效。但是在實際應用中,由於堆排序的常數因子比較大,因此實際運行速度可能不如快速排序和插入排序等算法。
希爾排序(Shell Sort)是一種插入排序的改進版,其基本思想是先將待排序的序列按照一定間隔分成幾個子序列,然後對每個子序列進行插入排序。接著逐步縮小間隔,重複進行上述操作,直到間隔縮小到1時,最後對整個序列進行一次插入排序,完成排序。
希爾排序的主要優點是在比較次數和移動次數上都有所改進。因為希爾排序采用分組的方式進行插入排序,每次排序可以使得一定程度上有序,因此在進行後面的排序時就可以利用前面排序時建立的有序性,減少比較次數和移動次數。此外,希爾排序不需要額外的內存空間,適合在內存較小的情況下進行排序。
時間複雜度:
希爾排序的時間複雜度取決於子序列的間隔序列(Increment sequence),一般會使用Hibbard增量序列(Hibbard's increment sequence),其公式為:h_k = 2^k - 1,其中k為子序列的索引,h_k為對應的增量。
空間複雜度:O(1)
希爾排序是一種原地排序算法,只需要一個輔助變量來進行元素交換,因此空間複雜度為O(1)。
希爾排序是一種高效的排序算法,它通常比傳統的插入排序要快很多,特別是對於大型數據集。希爾排序采用分組的方式進行插入排序,每次排序可以使得一定程度上有序,因此在進行後面的排序時就可以利用前面排序時建立的有序性,減少比較次數和移動次數。
計數排序(Counting Sort)是一種線性時間的排序算法,它可以用於排序一定範圍內的整數。計數排序的核心思想是先統計每個元素出現的次數,然後根據元素出現的次數,將元素排列成有序序列。
時間複雜度:O(n+k)
計數排序的時間複雜度可以分為兩部分:計數過程和排序過程。首先是計數過程,需要對整個序列進行一次遍歷,把每個元素出現的次數記錄在計數數組中。由於計數數組的大小等於待排序序列的範圍,因此計數過程的時間複雜度為 O(n+k),其中 n 是序列的長度,k 是序列中元素的範圍。接下來是排序過程,需要遍歷待排序序列,根據計數數組中的信息將每個元素放置到排序好的位置上。由於只需要遍歷一次待排序序列,因此排序過程的時間複雜度為 O(n)。因此,計數排序的時間複雜度為 O(n+k),其中 n 為待排序元素的數量,k 為待排序元素的最大值。需要注意的是,當範圍 k 比較大時,計數排序的效率可能會比較低。
空間複雜度:O(k)
計數排序的空間複雜度主要取決於計數數組的大小 k。因此,計數排序的空間複雜度為 O(k)。需要注意的是,當範圍 k 比較大時,計數排序的空間複雜度也會相應增加。
計數排序是一種高效的排序算法,適用於元素範圍較小的場景,在各種應用中都有著廣泛的應用,例如對於年齡、成績等數值型數據的排序。
儘管它的時間複雜度比其他常用排序算法(如快速排序和合併排序)更小,但是它的應用受到了很大的限制,因為它需要在內存中創建一個大小為k的計數數組,如果k太大,計數數組將占用大量內存。此外,計數排序也不適用於具有負值元素的數組。
桶排序(Bucket Sort)是一種非常簡單的排序演算法,它的基本思想是將要排序的資料分為幾個桶,每個桶裡的資料都有一定的範圍。然後,對每個桶中的資料進行排序,最後按照桶的順序將所有桶中的資料合併起來。
時間複雜度:O(n+k)
計數排序的時間複雜度可以分為兩部分:計數過程和排序過程。首先是計數過程,需要對整個序列進行一次遍歷,把每個元素出現的次數記錄在計數數組中。由於計數數組的大小等於待排序序列的範圍,因此計數過程的時間複雜度為 O(n+k),其中 n 是序列的長度,k 是序列中元素的範圍。接下來是排序過程,需要遍歷待排序序列,根據計數數組中的信息將每個元素放置到排序好的位置上。由於只需要遍歷一次待排序序列,因此排序過程的時間複雜度為 O(n)。因此,計數排序的時間複雜度為 O(n+k),其中 n 為待排序元素的數量,k 為待排序元素的最大值。需要注意的是,當範圍 k 比較大時,計數排序的效率可能會比較低。
空間複雜度:O(n+k)
桶排序的空間複雜度取決於桶的數量和每個桶內部元素的個數。由於每個桶內部的元素個數都不超過n/k,因此每個桶所需的空間是O(n/k)。總空間複雜度就是O(n + k)。如果k接近n,則空間複雜度就會接近O(n)。需要注意的是,當k比較大時,可能會出現空間浪費的情況,因此需要根據具體情況來選擇適當的桶數量。
桶排序是一種簡單但有效的線性時間複雜度排序算法,優點是簡單易懂,而且比較容易實現。桶排序在數據分佈比較集中的情況下效果較好,但當數據分佈比較分散時,則會產生較多的桶(bucket)。適用於待排序數據分布範圍有限的情況。
基數排序是一種非比較排序算法,適用於整數排序。基本思想是根據排序元素的位數,將整數按照位數從低到高或者從高到低進行排序,可以使用桶排序或計數排序等算法來實現。它的排序過程是先從最低有效位開始,依次對每一位進行排序,直到最高有效位。
例如,將一個整數序列按照個位、十位、百位的順序來排序。首先,按照個位進行排序,將序列中所有數字根據個位數分成10個桶,分別把它們放進對應的桶中。然後,按照桶的順序把數字放回原序列中。接下來,再按照十位進行排序,以此類推,直到按照最高有效位進行排序為止。
時間複雜度:O(d(n+k))
其中 d 為最大位數,n 為數組大小,k 為桶子數量,通常為 10(代表數字 0~9)。因為每一位數都要進行一次計數排序,計數排序的時間複雜度為 O(n+k),所以時間複雜度為 O(d(n+k))。
空間複雜度:O(n+k)
基數排序的空間複雜度主要由暫存陣列和計數陣列決定,因此空間複雜度為 O(n+k)。
總結來說,基數排序是一種穩定性較好且時間複雜度為線性的排序算法,但對於數字位數較大的情況下,其空間複雜度較高,可能需要額外的存儲空間。基數排序的優點是能夠處理不同長度的數字,且在數字大小範圍有限的情況下,表現優於快速排序和堆排序。但是,它需要額外的空間儲存桶,且當數字大小範圍非常大時,需要大量的額外空間,並且其時間複雜度也會增加。
排序演算法 | 最差時間複雜度 | 平均時間複雜度 | 最佳時間複雜度 | 空間複雜度 | 方式 | 穩定度 |
---|---|---|---|---|---|---|
氣泡排序 | In-place | ✅ | ||||
選擇排序 | In-place | ❌ | ||||
插入排序 | In-place | ✅ | ||||
合併排序 | Out-place | ✅ | ||||
快速排序 | In-place | ❌ | ||||
堆積排序 | In-place | ❌ | ||||
希爾排序 | In-place | ❌ | ||||
計數排序 | Out-place | ✅ | ||||
桶排序 | Out-place | ❌ | ||||
基數排序 | Out-place | ✅ |
排序演算法 | 主要特點 | 優點 | 缺點 |
---|---|---|---|
氣泡排序 | 一種簡單的交換排序演算法,每次將相鄰的元素進行比較和交換 | 實現簡單,程式易懂 | 時間複雜度較高,效率低 |
選擇排序 | 每次選出最小(大)的元素放到已排序序列的末尾 | 實現簡單,程式易懂,穩定 | 時間複雜度較高,效率低 |
插入排序 | 將未排序元素逐個插入到已排序的序列中,從後往前比較 | 實現簡單,對小規模資料排序效率高 | 時間複雜度較高,對大規模資料排序效率較低 |
合併排序 | 分治策略,將序列遞迴划分為子序列,然後將子序列合併 | 時間複雜度較低,效率較高,穩定 | 需要較大的輔助空間 |
快速排序 | 分治策略,選定一個基準元素,將序列分為左右兩部分,遞迴排序 | 時間複雜度較低,效率較高,適用於大規模資料排序 | 不穩定,最壞情況下時間複雜度較高 |
堆積排序 | 將序列構建成大根堆(小根堆),每次將堆頂元素與末尾元素交換,重新調整堆 | 時間複雜度較低,效率較高,適用於大規模資料排序 | 不穩定 |
希爾排序 | 插入排序的改進版本,設定一個增量,將序列劃分為若干子序列進行排序 | 對於中等規模資料排序效率較高 | 不穩定 |
計數排序 | 統計序列中各元素的出現次數,根據出現次數和元素值的關係排序 | 時間複雜度較低,適用於數據範圍較小的整數排序 | 對於數據範圍較大的情況需要較大的輔助空間 |
桶排序 | 將元素劃分到不同的桶中,對每個桶中的元素進行排序,最後合併 | 適用於元素值分佈較均勻的情況,時間複雜度較低 | 對於元素值分佈不均勻的情況效率較低 |
基數排序 | 按照元素的位數進行排序,從低位到高位進行排序,每一位使用穩定排序演算法進行排序 | 適用於大規模資料排序且穩定,可以處理多關鍵字排序 | 需要額外的記憶體空間且時間複雜度高,效率較低 |
在本文中,我們介紹了 10 種常見的排序演算法,每種演算法都有其優點和缺點。在實際應用中,需要根據具體的情況選擇最適合的排序演算法。如果你想學習排序演算法,我建議利用本文章及網路上各種資源,理解各個演算法中的原理,並且嘗試自己實作出這些演算法。通過不斷的練習,你將能更深入地理解這些排序演算法的原理和應用,或許能夠應用來解決現實中的問題,希望此篇文章能讓你有所收穫!
此篇文章因內容繁多,所以在整理資料及撰寫上,可能會有些錯誤,也請大家多留言或善用右側聊天室提出問題,我會馬上勘誤修正,謝謝。