數組排序——從荷蘭國旗問題到快速排序
??本文首先將會介紹荷蘭國旗問題,再講述如何從該問題過渡到快速排序。
荷蘭國旗問題
??荷蘭國旗問題(Dutch National Flag Problem)是由荷蘭計算機科學家Edsger Dijkstra所提出,該問題的描述如下:
給定n個紅、白、藍三種顏色的小球,無序地排列在一起。對這些小球進行排序,使得所有相同顏色的球在一起,且顏色順序依次為紅、白、藍。
因荷蘭國旗的顏色從上到下分別為紅、白、藍,故該問題被稱為荷蘭國旗問題。
??事實上,在Leetcode中,該問題又被描述為:
給定一個包含紅色、白色和藍色、共 n 個元素的數組 nums ,原地對它們進行排序,使得相同顏色的元素相鄰,并按照紅色、白色、藍色順序排列。我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。要求使用的空間復雜度為常數空間。
因此,荷蘭國旗問題又被稱為顏色分類問題。
??針對該問題,其解決的算法采用雙指針法。我們采用兩個指針low和high,分別指向0和2,再使用變量mid對數量進行遍歷。具體的步驟如下:
若mid遍歷到的位置為0,則和low處的元素交換,同時向右移動low和mid。
若mid遍歷到的位置為1,則不需要處理,只需向右移動mid。
若mid遍歷到的位置為2,則和high處的元素交換,交換后只把high向左移動,mid仍然指向原位置。
整個算法的示意圖如下:
荷蘭國旗問題的算法示意圖上述算法的Python代碼如下:
def?sort_colors(nums):
????low,?high,?mid?=?0,?len(nums)-1,?0
????while?mid?<=?high:
????????if?nums[mid]?==?0:
????????????nums[low],?nums[mid]?=?nums[mid],?nums[low]
????????????low?+=?1
????????????mid?+=?1
????????elif?nums[mid]?==?2:
????????????nums[high],?nums[mid]?=?nums[mid],?nums[high]
????????????high?-=?1
????????else:
????????????mid?+=?1
????return?nums
if?__name__?==?'__main__':
????numbers?=?[1,?0,?1,?0,?2,?0,?2,?1]
????result?=?sort_colors(numbers)
????print(result)
輸出結果如下:
[0,?0,?0,?1,?1,?1,?2,?2
快速排序
??對于上述的荷蘭國旗問題,我們可以將思路拓寬,用它來實現快速排序。
??對于數組array,我們取其切片,即從位置low到high的數組片段(包括位置low和high),我們使用類似于荷蘭國旗問題的思路,先選擇一個數字作為樞紐(pivot),再找到合適的位置,使其作為小于pivot的數字排列和等于pivot的數字排列的分割點(類似于荷蘭國旗問題中的數字0和1的分割點位置low)。
??接著,我們再循環(huán)調用上述算法,實現整個數組(從位置0到長度-1的數組切片)的排序。
??上述算法的Python實現代碼如下:
def?partition(array,?low,?high):
????pivot?=?array[low]
????a,?b,?c?=?low,?high,?low
????while?c?<=?b:
????????if?array[c]?<?pivot:
????????????array[a],?array[c]?=?array[c],?array[a]
????????????a?+=?1
????????????c?+=?1
????????elif?array[c]?>?pivot:
????????????array[b],?array[c]?=?array[c],?array[b]
????????????b?-=?1
????????else:
????????????c?+=?1
????return?a
def?quick_sort(array,?low,?high):
????if?low?<?high:
????????pi?=?partition(array,?low,?high)
????????#?Recursive?call?on?the?left?of?pivot
????????quick_sort(array,?low,?pi?-?1)
????????#?Recursive?call?on?the?right?of?pivot
????????quick_sort(array,?pi?+?1,?high)
????return?array
if?__name__?==?'__main__':
????n?=?13
????my_list?=?list(range(n))
????from?random?import?shuffle
????shuffle(my_list)
????print('排序前:',?my_list)
????result?=?quick_sort(my_list,?0,?len(my_list)-1)
????print('排序后:',?result)
輸出結果如下:
排序前:?[12,?8,?7,?3,?1,?0,?4,?2,?9,?6,?11,?10,?5]
排序后:?[0,?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12]
注意,該算法的時間復雜度為O(nlogn),空間復雜度為O(1).
??另外,還有其它的快速排序的實現代碼,如下:
def?quick_sort(array):
????n?=?len(array)
????if?n?<=?1:
????????return?array
????pivot?=?array[n//2]
????left?=?[x?for?x?in?array?if?x?<?pivot]
????middle?=?[x?for?x?in?array?if?x?==?pivot]
????right?=?[x?for?x?in?array?if?x?>?pivot]
????return?quick_sort(left)?+?middle?+?quick_sort(right)
但顯然,該實現代碼在空間復雜度上不如上一種。
總結
??近來ChatGPT大火,我們也嘗試著用ChatGPT來實現快速排序,如下:
ChatGPT實現快速排序??本文介紹了荷蘭國旗問題,并將其擴展快速排序,并介紹了兩種快速排序的算法,最后再用ChatGPT來實現快速排序。
參考文獻
荷蘭國旗問題: https://juejin.cn/post/6890141987988340749
顏色分類: https://leetcode.cn/problems/sort-colors/description/
QuickSort:https://www.geeksforgeeks.org/quick-sort/
