常用的高級排序代碼整理
堆排序
基本思路
a.將無需序列構建成一個堆,根據升序降序需求選擇大頂堆或小頂堆;
b.將堆頂元素與末尾元素交換,將最大元素"沉"到數組末端;
c.重新調整結構,使其滿足堆定義,然後繼續交換堆頂元素與當前末尾元素,反覆執行調整+交換步驟,直到整個序列有序。
時間複雜度 | 空間複雜度 | 穩定性 | ||
---|---|---|---|---|
最好 | 平均 | 最壞 | ||
O(nlogn) | O(nlogn) | O(nlogn) | o(1) | 不穩定 |
def sift(li,low,high):
'''
調整結構
li:列表
low:根節點
high:子節點
'''
i = low # i最開始指向根節點
j = 2 * i + 1 # j開始是左孩子節點
tmp = li[i]
while j <= high: #j超過最後一個節點,只要j位置有數就執行
if j + 1 <= high and li[j+1] > li[j]: #如果右子節點大於左子節點,且右子節點存在
j = j + 1 #j指向較大的節點(右子節點)
if li[j] > tmp:
li[i] = li[j] #往下看一層,看子節點是否滿足堆的條件
i = j
j = i * 2 + 1
else: # tmp更大,則將tmp放在當前i的位置
li[i] = tmp
break
else:
li[i] = tmp # 把tmp放在最後
def heap_sort(li):
n=len(li)
for i in range((n-2)//2,-1,-1): #i表示建立堆的時候調整部分的根的下標
sift(li,i,n-1) #始終使用最後一個元素作爲high,不會影響判斷
for i in range(n-1,-1,-1): #i指向當前堆的最後一個位置
li[0],li[i] = li[i],li[0] # 堆首和堆尾交換位置,將最大的放到堆尾
sift(li,0,i-1) #i-1是新的high
python內部堆排序函數
import heapq
import random
li = list(rangen(100)
heapq.heapify(li) #建立堆
heapq.heappop(li) #彈出最小的值
使用場景:
- 熱搜榜的取前幾名
自己實現topk問題
基本思路
將前k個數建堆,從k+1個數開始遍歷替換堆中的根節點並且調整堆,最後堆中留下來的元素就是最大或者最小且有序的
def sift(li,low,high):
'''
li:列表
low:根節點
high:子節點
'''
i = low # i最開始指向根節點
j = 2 * i + 1 # j開始是左孩子節點
tmp = li[i]
while j <= high: #j超過最後一個節點,只要j位置有數就執行
if j + 1 <= high and li[j+1] < li[j]: #如果右子節點大於左子節點,且右子節點存在
j = j + 1 #j指向較大的節點(右子節點)
if li[j] < tmp:
li[i] = li[j] #往下看一層,看子節點是否滿足堆的條件
i = j
j = i * 2 + 1
else: # tmp更大,則將tmp放在當前i的位置
li[i] = tmp
break
else:
li[i] = tmp # 把tmp放在最後
def topk(li,k):
heap = li[0:k]
for i in range((k-2)//2,-1,-1):
sift(heap,i,k-1)
#建堆
for i in range(k,len(li)-1):
if li[i] > heap[0]:
heap[0] = li[i]
sift(heap,0,k-1)
# 遍歷尋找topk
for i in range(k-1,-1,-1):
heap[0],heap[i] = heap[i],heap[0]
sift(heap,0,i-1)
#出數
return heap
歸併排序
基本思路
歸併的基本思路在於先分解再合併,將數組分解爲左右兩個有序數組,然後將有序數組合併到一個數組中
時間複雜度 | 空間複雜度 | 穩定性 | ||
---|---|---|---|---|
最好 | 平均 | 最壞 | ||
O(nlogn) | O(nlogn) | O(nlogn) | o(n) | 穩定 |
def merge(li,low,mid,high):
'''
合併左右兩個有序數組
'''
i = low
j = mid + 1
ltmp = []
while i <= mid and j<=high: # 從兩個數組的第一個元素開始,只要左右兩邊都有數
if li[i] < li[j]:
ltmp.append(li[i])
i += 1
else:
ltmp.append(li[j])
j +=1 # 哪個元素小將其放入新數組的第一個
while i<= mid: # 如果其中一邊的數組都已經遍歷完,則將另一邊的順序存入新數組中
ltmp.append(li[i])
i += 1
while j<= high:
ltmp.append(li[j])
j += 1
li[low:high+1] = ltmp #將排好的新數組寫回到原數組
def merge_sort(li,low,high):
if low < high: #如果數組中的元素大於1則遞歸
mid = (high + low) // 2
merge_sort(li,low,mid)
merge_sort(li,mid+1,high)
merge(li,low,mid,high)
快速排序
基本思路
在數組中選中一個元素,將素組中所有小於該元素的元素放入左邊,將大於該元素的元素放入右邊,將該元素放於交界處,在將左右兩邊的數組分別按照上述邏輯接着整理,直到最小單元。
時間複雜度 | 空間複雜度 | 穩定性 | ||
---|---|---|---|---|
最好 | 平均 | 最壞 | ||
O(n^2) | O(nlogn) | O(nlogn) | 平均O(logn)最壞o(n) | 不穩定 |
# 這種實現方法不需要開闢新的數組儲存空間
def quick(li,left,right):
'''
將數組在區間內按照左中右排列
'''
i = left
j = right
base = li[i]
while i < j: # 區間中還有元素
while i<j and li[j] <= base: # 將小於base的放在左邊base的空位
j=j-1
li[i] =li[j]
while i<j and li[i] >= base: #將大於base的放在右邊之前空出來的空位
i = i + 1
li[j] = li[i]
li[i] = base #最後i=j的時候,將base放入
return i # 返回中間值(base的索引)
def quick_sort(li,left,right):
if left < right:
mid = quick(li,left,right)
quick_sort(li,left,mid - 1)
quick_sort(li,mid + 1,right)
另一種快排
#這種需要佔用其他的數組儲存空間
def quick_sort2(li):
if len(li)<2:
return li
else:
base = li[0]
lli = [i for i in li[1:] if i <=base]
rli = [i for i in li[1:] if i >base]
return quick_sort2(lli)+[base]+quick_sort2(rli)