大家好,歡迎來到Crossin的編程教室!
算法作為程式設計師的必修課,是每位程式設計師必須掌握的基礎。本篇我們將通過Python來實現5大經典排序算法,結合例圖剖析內部實現邏輯,對比每種算法各自的優缺點和應用點。相信我,耐心看完絕對有收穫。
大家都知道從理論上講,我們一般會使用大O表示法測量算法的運行時複雜度。"大O表示法"表示程序的執行時間或佔用空間隨數據規模的增長趨勢。
但為了測算具體的時間,本篇將使用timeit模塊來衡量實現的運行時間。下面自己寫一個對算法測試時間的函數。
from random import randint
from timeit import repeat
def run_sorting_algorithm(algorithm, array):
# 調用特定的算法對提供的數組執行。
# 如果不是內置sort()函數,那就只引入算法函數。
setup_code = f"from __main__ import {algorithm}" \
if algorithm != "sorted" else ""
stmt = f"{algorithm}({array})"
# 十次執行代碼,並返回以秒為單位的時間
times = repeat(setup=setup_code, stmt=stmt, repeat=3, number=10)
# 最後,顯示算法的名稱和運行所需的最短時間
print(f"Algorithm: {algorithm}. Minimum execution time: {min(times)}")這裡用到了一個騷操作,通過f-strings魔術方法導入了算法名稱,不懂的可以參考:Python 格式化字符串的最佳姿勢
注意:應該找到算法每次運行的平均時間,而不是選擇單個最短時間。由於系統同時運行其他進程,因此時間測量是受影響的。最短的時間肯定是影響最小的,是這樣才使其成為算法時間最短的。
Python中的冒泡排序算法冒泡排序是最直接的排序算法之一。它的名稱來自算法的工作方式:每經過一次新的遍歷,列表中最大的元素就會「冒泡」至正確位置。
在Python中實現冒泡排序
這是Python中冒泡排序算法的實現:
def bubble_sort(array):
n = len(array)
for i in range(n):
# 創建一個標識,當沒有可以排序的時候就使函數終止。
already_sorted = True
# 從頭開始逐個比較相鄰元素,每一次循環的總次數減1,
# 因為每次循環一次,最後面元素的排序就確定一個。
for j in range(n - i - 1):
if array[j] > array[j + 1]:
# 如果此時的元素大於相鄰後一個元素,那麼交換。
array[j], array[j + 1] = array[j + 1], array[j]
# 如果有了交換,設置already_sorted標誌為False算法不會提前停止
already_sorted = False
# 如果最後一輪沒有交換,數據已經排序完畢,退出
if already_sorted:
break
return array為了正確分析算法的工作原理,看下這個列表[8, 2, 6, 4, 5]。假設使用bubble_sort()排序,下圖說明了算法每次迭代時數組的實際換件情況:
冒泡排序過程
測算冒泡算法的大O運行複雜度冒泡排序的實現由兩個嵌套for循環組成,其中算法先執行n-1個比較,然後進行n-2個比較,依此類推,直到完成最終比較。因此,總的比較次數為(N - 1)+(N - 2)+(N - 3)+ ... + 2 + 1 = N(N-1)/ 2,也可以寫成 ½n 2 - ½n。
去掉不會隨數據規模n而變化的常量,可以將符號簡化為 n2 -n。由於 n2的增長速度快於n,因此也可以捨棄最後一項,使冒泡排序的平均和最壞情況下的時間複雜度為 O(n 2)。
在算法接收到已排序的數組的情況下,運行時間複雜度將降低到更好的O(n),因為算法循環一遍沒有任何交換,標誌是true,所以循環一遍比較了N次直接退出。因此,O(n)是冒泡排序的最佳情況運行時間複雜度。但最好的情況是個例外,比較不同的算法時,應該關注平均情況。
冒泡排序的時間運行測試
使用run_sorting_algorithm()測試冒泡排序處理具有一萬個元素的數組所花費的時間。
ARRAY_LENGTH = 10000if __name__ == "__main__":
# 生成包含「 ARRAY_LENGTH」個元素的數組,元素是介於0到999之間的隨機整數值
array = [randint(0, 1000) for i in range(ARRAY_LENGTH)]
# 使用排序算法的名稱和剛創建的數組調用該函數
run_sorting_algorithm(algorithm="bubble_sort", array=array)現在運行腳本來獲取bubble_sort的執行時間:
$ python sorting.py
Algorithm: bubble_sort. Minimum execution time: 73.21720498399998分析冒泡排序的優缺點冒泡排序算法的主要優點是它的簡單性,理解起來非常簡單。但也看到了冒泡排序的缺點是速度慢,運行時間複雜度為O(n 2)。因此,一般對大型數組進行排序的時候,不會考慮使用冒泡排序。
Python中的插入排序算法像冒泡排序一樣,插入排序算法也易於實現和理解。但是與冒泡排序不同,它通過將每個元素與列表的其餘元素進行比較並將其插入正確的位置,來一次構建一個排序的列表元素。此「插入」過程為算法命名。
一個例子,就是對一副紙牌進行排序。將一張卡片與其餘卡片進行逐個比較,直到找到正確的位置為止,然後重複進行直到您手中的所有卡都被排序。
在Python中實現插入排序
插入排序算法的工作原理與紙牌排序完全相同,Python中的實現:
def insertion_sort(array):
# 從數據第二個元素開始循環,直到最後一個元素
for i in range(1, len(array)):
# 這個是我們想要放在正確位置的元素
key_item = array[i]
# 初始化變量,用於尋找元素正確位置
j = i - 1
# 遍曆元素左邊的列表元素,一旦key_item比被比較元素小,那麼找到正確位置插入。
while j >= 0 and array[j] > key_item:
# 把被檢測元素向左平移一個位置,並將j指向下一個元素(從右向左)
array[j + 1] = array[j]
j -= 1
# 當完成元素位置的變換,把key_item放在正確的位置上
array[j + 1] = key_item
return array下圖顯示了對數組進行排序時算法的不同迭代[8, 2, 6, 4, 5]:
插入排序過程
測量插入排序的大O時間複雜度
與冒泡排序實現類似,插入排序算法具有兩個嵌套循環,遍歷整個列表。內部循環非常有效,因為它會遍歷列表,直到找到元素的正確位置為止。
最壞的情況發生在所提供的數組以相反順序排序時。在這種情況下,內部循環必須執行每個比較,以將每個元素放置在正確的位置。這仍然給您帶來O(n2)運行時複雜性。
最好的情況是對提供的數組進行了排序。這裡,內部循環永遠不會執行,導致運行時複雜度為O(n),就像冒泡排序的最佳情況一樣。
儘管冒泡排序和插入排序具有相同的大O時間複雜度,但實際上,插入排序比冒泡排序有效得多。如果查看兩種算法的實現,就會看到插入排序是如何減少了對列表進行排序的比較次數的。
插入排序時間測算
為了證明插入排序比冒泡排序更有效,可以對插入排序算法進行計時,並將其與冒泡排序的結果進行比較。調用我們寫好的測試函數。
if __name__ == "__main__":
# 生成包含「 ARRAY_LENGTH」個元素的數組,元素是介於0到999之間的隨機整數值
array = [randint(0, 1000) for i in range(ARRAY_LENGTH)]
# 使用排序算法的名稱和剛創建的數組調用該函數
run_sorting_algorithm(algorithm="insertion_sort", array=array)執行腳本:
$ python sorting.py
Algorithm: insertion_sort. Minimum execution time: 56.71029764299999可以看到,插入排序比冒泡排序實現少了17秒,即使它們都是O(n 2)算法,插入排序也更加有效。
分析插入排序的優點和缺點
就像冒泡排序一樣,插入排序算法的實現也很簡單。儘管插入排序是O(n 2)算法,但在實踐中它也比其他二次實現(例如冒泡排序)更有效。
有更強大的算法,包括合併排序和快速排序,但是這些實現是遞歸的,在處理小型列表時通常無法擊敗插入排序。如果列表足夠小,可以提供更快的整體實現,則某些快速排序實現甚至在內部使用插入排序。Timsort還在內部使用插入排序對輸入數組的一小部分進行排序。
也就是說,插入排序不適用於大型陣列,這為可以更有效地擴展規模的算法打開了大門。
Python中的合併排序算法合併排序是一種非常有效的排序算法。它基於分治法,這是一種用於解決複雜問題的強大算法技術。
要正確理解分而治之,應該首先了解遞歸的概念。遞歸涉及將問題分解成較小的子問題,直到它們足夠小以至於無法解決。在編程中,遞歸通常由調用自身的函數表示。
分而治之算法通常遵循相同的結構:
原始輸入分為幾個部分,每個部分代表一個子問題,該子問題與原始輸入相似,但更為簡單。每個子問題都遞歸解決。所有子問題的解決方案都組合成一個整體解決方案。在合併排序的情況下,分而治之方法將輸入值的集合劃分為兩個大小相等的部分,對每個一半進行遞歸排序,最後將這兩個排序的部分合併為一個排序列表。
在Python中實現合併排序
合併排序算法的實現需要兩個不同的部分:
遞歸地將輸入分成兩半的函數合併兩個半部的函數,產生一個排序數組這是合併兩個不同數組的代碼:
def merge(left, right):
# 如果第一個數組為空,那麼不需要合併,
# 可以直接將第二個數組返回作為結果
if len(left) == 0:
return right
# 如果第二個數組為空,那麼不需要合併,
# 可以直接將第一個數組返回作為結果
if len(right) == 0:
return left
result = []
index_left = index_right = 0
# 查看兩個數組直到所有元素都裝進結果數組中
while len(result) < len(left) + len(right):
# 這些需要排序的元素要依次被裝入結果列表,因此需要決定將從
# 第一個還是第二個數組中取下一個元素
if left[index_left] <= right[index_right]:
result.append(left[index_left])
index_left += 1
else:
result.append(right[index_right])
index_right += 1
# 如果哪個數組達到了最後一個元素,那麼可以將另外一個數組的剩餘元素
# 裝進結果列表中,然後終止循環
if index_right == len(right):
result += left[index_left:]
break
if index_left == len(left):
result += right[index_right:]
break
return result現在還缺少的部分是將輸入數組遞歸拆分為兩個並用於merge()產生最終結果的函數:
def merge_sort(array):
# 如果輸入數組包含元素不超過兩個,那麼返回它作為函數結果
if len(array) < 2:
return array
midpoint = len(array) // 2
# 對數組遞歸地劃分為兩部分,排序每部分然後合併裝進最終結果列表
return merge(
left=merge_sort(array[:midpoint]),
right=merge_sort(array[midpoint:]))看一下合併排序將對數組進行排序的步驟[8, 2, 6, 4, 5]:
合併排序過程
該圖使用黃色箭頭表示在每個遞歸級別將數組減半。綠色箭頭表示將每個子陣列合併在一起。
衡量合併排序的大O複雜度
要分析合併排序的複雜性,可以分別查看其兩個步驟:
merge()具有線性運行時間。它接收兩個數組,它們的組合長度最多為n(原始輸入數組的長度),並且通過最多查看每個元素一次來組合兩個數組。這導致運行時複雜度為O(n)。
第二步以遞歸方式拆分輸入數組,並調用merge()每一部分。由於將數組減半直到剩下單個元素,因此此功能執行的減半運算總數為log 2 n。由於merge()每個部分都被調用,因此總運行時間為O(n log 2 n)。
對合併排序進行測算時間
同樣通過之前時間測試函數:
if __name__ == "__main__":
# 生成包含「 ARRAY_LENGTH」個元素的數組,元素是介於0到999之間的隨機整數值
array = [randint(0, 1000) for i in range(ARRAY_LENGTH)]
# 使用排序算法的名稱和剛創建的數組調用該函數
run_sorting_algorithm(algorithm="merge_sort", array=array)執行時間:
$ python sorting.py
Algorithm: merge_sort. Minimum execution time: 0.6195857160000173與冒泡排序和插入排序相比,合併排序實現非常快,可以在一秒鐘內對一萬個數組進行排序了!
分析合併排序的優點和缺點
由於其運行時複雜度為O(n log 2 n),因此合併排序是一種非常有效的算法,可以隨著輸入數組大小的增長而很好地擴展。並行化也很簡單,因為它將輸入數組分成多個塊,必要時可以並行分配和處理這些塊。
缺點是對於較小的列表,遞歸的時間成本就較高了,冒泡排序和插入排序之類的算法更快。例如,運行包含十個元素的列表的實驗的時間:
Algorithm: bubble_sort. Minimum execution time: 0.000018774999999998654
Algorithm: insertion_sort. Minimum execution time: 0.000029786000000000395
Algorithm: merge_sort. Minimum execution time: 0.00016983000000000276合併排序的另一個缺點是,它在遞歸調用自身時會創建數組。它還在內部創建一個新列表,這使得合併排序比氣泡排序和插入排序使用更多的內存。
Python中的快速排序算法就像合併排序一樣,快速排序算法採用分而治之的原理將輸入數組分為兩個列表,第一個包含小項目,第二個包含大項目。然後,該算法將對兩個列表進行遞歸排序,直到對結果列表進行完全排序為止。
劃分輸入列表稱為對列表進行分區。快排首先選擇一個pivot元素,然後將列表劃分為pivot,然後將每個較小的元素放入low數組,將每個較大的元素放入high數組。
將low列表中的每個元素放在列表的左側,列表中的pivot每個元素high放在右側,將其pivot精確定位在最終排序列表中的確切位置。這意味著該函數現在可以遞歸地將相同的過程應用於low,然後high對整個列表進行排序。
在Python中實現快排
這是快排的一個相當緊湊的實現:
from random import randint
def quicksort(array):
# 如果第一個數組為空,那麼不需要合併,
# 可以直接將第二個數組返回作為結果
if len(array) < 2:
return array
low, same, high = [], [], []
# 隨機選擇 pivot 元素
pivot = array[randint(0, len(array) - 1)]
for item in array:
# 元素小於pivot元素的裝進low列表中,大於piviot元素值的裝進high列表中
# 如果和pivot相等,則裝進same列表中
if item < pivot:
low.append(item)
elif item == pivot:
same.append(item)
elif item > pivot:
high.append(item)
# 最後的結果列表包含排序的low列表、same列表、hight列表
return quicksort(low) + same + quicksort(high)以下是快排對數組進行排序的步驟的說明[8, 2, 6, 4, 5]:
快排排序過程
快速排序流程黃線表示陣列的劃分成三個列表:low,same,high。綠線表示排序並將這些列表放在一起。
選擇pivot元素
為什麼上面的實現會pivot隨機選擇元素?選擇輸入列表的第一個或最後一個元素會不會一樣?
由於快速排序算法的工作原理,遞歸級別的數量取決於pivot每個分區的結尾位置。在最佳情況下,算法始終選擇中值元素作為pivot。這將使每個生成的子問題恰好是前一個問題的一半,從而導致最多log 2 n級。
另一方面,如果算法始終選擇數組的最小或最大元素作為pivot,則生成的分區將儘可能不相等,從而導致n-1個遞歸級別。對於快速排序,那將是最壞的情況。
如你所見,快排的效率通常取決於pivot選擇。如果輸入數組未排序,則將第一個或最後一個元素用作,pivot將與隨機元素相同。但是,如果輸入數組已排序或幾乎已排序,則使用第一個或最後一個元素作為pivot可能導致最壞的情況。pivot隨機選擇使其更有可能使快排選擇一個接近中位數的值並更快地完成。
另一個選擇是找到數組的中值,並強制算法將其用作pivot。這可以在O(n)時間內完成。儘管該過程稍微複雜一些,但將中值用作pivot快速排序可以確保您擁有最折中的大O方案。
衡量快排的大O複雜度
使用快排,將輸入列表按線性時間O(n)進行分區,並且此過程將平均遞歸地重複log 2 n次。這導致最終複雜度為O(n log 2 n)。
當所選擇的pivot是接近陣列的中位數,最好的情況下會發生O(n),當pivot是陣列的最小或最大的值,最差的時間複雜度為O(n 2)。
從理論上講,如果算法首先專注於找到中值,然後將其用作pivot元素,那麼最壞情況下的複雜度將降至O(n log 2 n)。數組的中位數可以在線性時間內找到,並將其用作pivot保證代碼的快速排序部分將在O(n log 2 n)中執行。
通過使用中值作為pivot,最終運行時間為O(n)+ O(n log 2 n)。你可以將其簡化為O(n log 2 n),因為對數部分的增長快於線性部分。
隨機選擇pivot使最壞情況發生的可能性很小。這使得隨機pivot選擇對於該算法的大多數實現都足夠好。
對快排測量運行時間
調用測試函數:
if __name__ == "__main__":
# 生成包含「 ARRAY_LENGTH」個元素的數組,元素是介於0到999之間的隨機整數值
array = [randint(0, 1000) for i in range(ARRAY_LENGTH)]
# 使用排序算法的名稱和剛創建的數組調用該函數
run_sorting_algorithm(algorithm="quicksort", array=array)執行腳本:
$ python sorting.py
Algorithm: quicksort. Minimum execution time: 0.11675417600002902快速排序不僅可以在不到一秒鐘的時間內完成,而且比合併排序(0.11幾秒鐘對0.61幾秒鐘)要快得多。如果增加ARRAY_LENGTH從10,000到數量1,000,000並再次運行腳本,合併排序最終會在97幾秒鐘內完成,而快排則僅在10幾秒鐘內對列表進行排序。
分析快排的優勢和劣勢
顧名思義,快排非常快。儘管從理論上講,它的最壞情況是O(n 2),但在實踐中,快速排序的良好實現勝過大多數其他排序實現。而且,就像合併排序一樣,快排也很容易並行化。
快排的主要缺點之一是缺乏保證達到平均運行時複雜度的保證。儘管最壞的情況很少見,但是某些應用程式不能承受性能不佳的風險,因此無論輸入如何,它們都選擇不超過O(n log 2 n)的算法。
就像合併排序一樣,快排也會在內存空間與速度之間進行權衡。這可能成為對較大列表進行排序的限制。
通過快速實驗對十個元素的列表進行排序,可以得出以下結果:
Algorithm: bubble_sort. Minimum execution time: 0.0000909000000000014
Algorithm: insertion_sort. Minimum execution time: 0.00006681900000000268
Algorithm: quicksort. Minimum execution time: 0.0001319930000000004結果表明,當列表足夠小時,快速排序也要付出遞歸的代價,完成時間比插入排序和冒泡排序都要長。
Python中的Timsort算法最後這個算法就有意思了!
所述Timsort算法被認為是一種混合的排序算法,因為它採用插入排序和合併排序的最佳的兩個世界級組合。Timsort與Python社區也很有緣,它是由Tim Peters於2002年創建的,被用作Python語言的標準排序算法。我們使用的內置sorted函數就是這個算法。
Timsort的主要特徵是它利用了大多數現實數據集中存在的已排序元素。這些稱為natural runs。然後,該算法會遍歷列表,將元素收集到運行中,然後將它們合併到一個排序的列表中。
在Python中實現Timsort
本篇創建一個準系統的Python實現,該實現說明Timsort算法的所有部分。如果有興趣,也可以查看Timsort的原始C實現。
實施Timsort的第一步是修改insertion_sort()的實現:
def insertion_sort(array, left=0, right=None):
if right is None:
right = len(array) - 1
# 從指示的left元素循環,直到right被指示
for i in range(left + 1, right + 1):
# 這個是我們想要放在正確位置的元素
key_item = array[i]
# 初始化變量,用於尋找元素正確位置
j = i - 1
# 遍曆元素左邊的列表元素,一旦key_item比被比較元素小,那麼找到正確位置插入
while j >= left and array[j] > key_item:
# 把被檢測元素向左平移一個位置,並將j指向下一個元素(從右向左)
array[j + 1] = array[j]
j -= 1
# 當完成元素位置的變換,把key_item放在正確的位置上
array[j + 1] = key_item
return array此修改後的實現添加了兩個參數left和right,它們指示應該對數組的哪一部分進行排序。這使Timsort算法可以對數組的一部分進行排序。修改功能而不是創建新功能意味著可以將其同時用於插入排序和Timsort。
現在看一下Timsort的實現:
def timsort(array):
min_run = 32
n = len(array)
# 開始切分、排序輸入數組的小部分,切分在`min_run`定義
for i in range(0, n, min_run):
insertion_sort(array, i, min((i + min_run - 1), n - 1))
# 現在可以合併排序的切分塊了
# 從`min_run`開始, 每次循環都加倍直到超過數組的長度
size = min_run
while size < n:
# Determine the arrays that will
# be merged together
for start in range(0, n, size * 2):
# 計算中點(第一個數組結束第二個數組開始的地方)和終點(第二個數組結束的地方)
midpoint = start + size - 1
end = min((start + size * 2 - 1), (n-1))
# 合併兩個子數組
# left數組應該從起點到中點+1, right數組應該從中點+1到終點+1
merged_array = merge(
left=array[start:midpoint + 1],
right=array[midpoint + 1:end + 1])
# 最後,把合併的數組放回數組
array[start:start + len(merged_array)] = merged_array
# 每次迭代都應該讓數據size加倍
size *= 2
return array儘管實現比以前的算法要複雜一些,但是我們可以通過以下方式快速總結一下:
之前已經了解到,插入排序在小列表上速度很快,而Timsort則利用了這一優勢。Timsort使用新引入的left和right參數在insertion_sort()對列表進行適當排序,而不必像merge sort和快排那樣創建新數組。
定義min_run = 32作為值有兩個原因:
1. 使用插入排序對小數組進行排序非常快,並且min_run利用此特性的價值很小。使用min_run太大的值進行初始化將無法達到使用插入排序的目的,並使算法變慢。
2. 合併兩個平衡列表比合併不成比例的列表要有效得多。min_run在合併算法創建的所有不同運行時,選擇一個為2的冪的值可確保更好的性能。
結合以上兩個條件,可以提供幾種min_run選擇。本教程中的實現min_run = 32是其中一種可能性。
衡量Timsort的大O時間複雜性
平均而言,Timsort的複雜度為O(n log 2 n),就像合併排序和快速排序一樣。對數部分來自執行每個線性合併操作的運行大小加倍。
但是,Timsort在已排序或接近排序的列表上表現特別出色,從而導致了O(n)的最佳情況。在這種情況下,Timsort明顯勝過合併排序,並與快速排序的最佳情況相匹配。但是,對於Timsort來說,最糟糕的情況也是O(n log 2 n)。
對Timsort測量運行時間
調用時間運行測試函數:
if __name__ == "__main__":
# 生成包含「 ARRAY_LENGTH」個元素的數組,元素是介於0到999之間的隨機整數值
array = [randint(0, 1000) for i in range(ARRAY_LENGTH)]
# 使用排序算法的名稱和剛創建的數組調用該函數
run_sorting_algorithm(algorithm="timsort", array=array)執行時間:
$ python sorting.py
Algorithm: timsort. Minimum execution time: 0.51216907899999980.51秒,Timsort比合併排序整整快0.1秒,即17%。它也比插入排序快11,000%!
現在,嘗試使用這四種算法對已經排序的列表進行排序,然後看看會發生什麼。
if __name__ == "__main__":
# 生成包含「 ARRAY_LENGTH」個元素的數組
array = [i for i in range(ARRAY_LENGTH)]
# 調用每個函數
run_sorting_algorithm(algorithm="insertion_sort", array=array)
run_sorting_algorithm(algorithm="merge_sort", array=array)
run_sorting_algorithm(algorithm="quicksort", array=array)
run_sorting_algorithm(algorithm="timsort", array=array)現在執行腳本,那麼所有算法都將運行並輸出相應的執行時間:
Algorithm: insertion_sort. Minimum execution time: 53.5485634999991
Algorithm: merge_sort. Minimum execution time: 0.372304601
Algorithm: quicksort. Minimum execution time: 0.24626494199999982
Algorithm: timsort. Minimum execution time: 0.23350277099999994這次,Timsort的速度比合併排序快了37%,比快排快了5%,從而增強了利用已排序運行的能力。
請注意,Timsort如何從兩種算法中受益,這兩種算法單獨使用時速度要慢得多。Timsort的神奇之處在於將這些算法結合起來並發揮其優勢,以獲得令人印象深刻的結果。
分析Timsort的優勢和劣勢
Timsort的主要缺點是它的複雜性。儘管實現了原始算法的非常簡化的版本,但由於它同時依賴於insertion_sort()和merge(),因此仍需要更多代碼。
Timsort的優點之一是其能夠以O(n log 2 n)的方式執行預測,而與輸入數組的結構無關。與快排相比,快排可以降級為O(n 2)。對於小數組,Timsort也非常快,因為該算法變成了單個插入排序。
對於現實世界中的使用(通常對已經具有某些預先存在的順序的數組進行排序),Timsort是一個不錯的選擇。它的適應性使其成為排序任何長度的數組的絕佳選擇。
結論排序是任何Pythonista工具包中必不可少的工具。了解Python中不同的排序算法以及如何最大程度地發揮它們的潛力,你就可以實現更快,更高效的應用程式和程序!
參考:
1. https://realpython.com/sorting-algorithms-python/
2. https://blog.csdn.net/weixin_38483589/article/details/84147376
3. https://mp.weixin.qq.com/s/OHoe6TTX--Ys5G-5yR6juA