快速排序之python
阿新 • • 發佈:2018-06-02
插入 價值 時間復雜度 選擇 reverse 版本 elf time 獨立
快速排序( Quick sort)
快速排序的基本思想:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行遞歸排序,以達到整個序列有序。
1.算法描述:
- 另一個分而治之
- 將數組劃分為兩個部分,然後獨立地對部分進行排序
- 首先選擇一個數據透視,並從列表中刪除(隱藏在最後)
- 然後這些元素被分成兩部分.一個小於樞軸,另一個大於樞軸. 這種分區是通過交換價值來實現的
- 然後在中間恢復樞軸,並且這兩個部分遞歸地快速排序
示例:
pivot:中間樞紐 ( 5)
portitiom:分區
Two points:兩個指針 ( i:左->右 j:左<-右)
2.算法屬性:
- 不穩定
- 在一般情況下,通過數學歸納法可證明,快速排序的時間復雜度為O(nlog(n))
3.代碼實現
def _quick_sorted(nums: list) -> list: if len(nums) <= 1: return nums pivot = nums[0] #pivot左右邊分別調用quick_sort left_nums = _quick_sorted([x for x in nums[1:] if x < pivot]) #找左半邊比樞紐小的 right_nums = _quick_sorted([x forx in nums[1:] if x >= pivot]) #找右半邊比樞紐大的 return left_nums + [pivot] + right_nums def quick_sorted(nums: list, reverse=False)-> list: """Quick Sort""" start = time.time() nums = _quick_sorted(nums) if reverse: nums = nums[::-1] t = time.time() - start returnnums, len(nums), t lis = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] lis = quick_sorted(lis, reverse=False) print(lis) #輸出結果 ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 10, 0.0)
另一個版本
def quick_sort(nums): # 封裝一層的目的是方便用戶調用 def qsort(lst, begin, end): if begin >= end: return i = begin key = lst[begin] for j in range(begin+1, end+1): if lst[j] < key: i += 1 lst[i], lst[j] = lst[j], lst[i] lst[begin], lst[i] = lst[i], lst[begin] qsort(lst, begin, i-1) qsort(lst,i+1,end) qsort(nums, 0, len(nums)-1)
4.基本的快速排序還有可以優化的地方:
1)優化選取的pivot_key
前面我們每次選取pivot_key的都是子序列的第一個元素,也就是lis[low],這就比較看運氣。運氣好時,該值處於整個序列的靠近中間值,則構造的樹比較平衡,運氣比較差,處於最大或最小位置附近則構造的樹接近斜樹。
為了保證pivot_key選取的盡可能適中,采取選取序列左中右三個特殊位置的值中,處於中間值的那個數為pivot_key,通常會比直接用lis[low]要好一點。在代碼中,在原來的pivot_key = lis[low]這一行前面增加下面的代碼:
m = low + int((high-low)/2) if lis[low] > lis[high]: self.swap(low, high) if lis[m] > lis[high]: self.swap(high, m) if lis[m] > lis[low]: self.swap(m, low)
如果覺得這樣還不夠好,還可以將整個序列先劃分為3部分,每一部分求出個pivot_key,再對3個pivot_key再做一次上面的比較得出最終的pivot_key。這時的pivot_key應該很大概率是一個比較靠譜的值。
2)減少不必要的交換
原來的代碼中pivot_key這個記錄總是再不斷的交換中,其實這是沒必要的,完全可以將它暫存在某個臨時變量中,如下所示:
def partition(self, low, high): lis = self.r m = low + int((high-low)/2) if lis[low] > lis[high]: self.swap(low, high) if lis[m] > lis[high]: self.swap(high, m) if lis[m] > lis[low]: self.swap(m, low) pivot_key = lis[low] # temp暫存pivot_key的值 temp = pivot_key while low < high: while low < high and lis[high] >= pivot_key: high -= 1 # 直接替換,而不交換了 lis[low] = lis[high] while low < high and lis[low] <= pivot_key: low += 1 lis[high] = lis[low] lis[low] = temp return low
3)優化小數組時的排序
快速排序算法的遞歸操作在進行大量數據排序時,其開銷能被接受,速度較快。但進行小數組排序時則不如直接插入排序來得快,也就是殺雞用牛刀,未必就比菜刀來得快。
因此,一種很樸素的做法就是根據數據的多少,做個使用哪種算法的選擇而已,如下改寫qsort方法:
def qsort(self, low, high): """根據序列長短,選擇使用快速排序還是簡單插入排序""" # 7是一個經驗值,可根據實際情況自行決定該數值。 MAX_LENGTH = 7 if high-low < MAX_LENGTH: if low < high: pivot = self.partition(low, high) self.qsort(low, pivot - 1) self.qsort(pivot + 1, high) else: # insert_sort方法是我們前面寫過的簡單插入排序算法 self.insert_sort()
4)優化遞歸操作
可以采用尾遞歸的方式對整個算法的遞歸操作進行優化,改寫qsort方法如下:
def qsort(self, low, high): """根據序列長短,選擇使用快速排序還是簡單插入排序""" # 7是一個經驗值,可根據實際情況自行決定該數值。 MAX_LENGTH = 7 if high-low < MAX_LENGTH: # 改用while循環 while low < high: pivot = self.partition(low, high) self.qsort(low, pivot - 1) # 采用了尾遞歸的方式 low = pivot + 1 else: # insert_sort方法是我們前面寫過的簡單插入排序算法 self.insert_sort()
快速排序之python