常用的高级排序代码整理
堆排序
基本思路
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)