快速排序
快速排序是一个典型的分治算法,每次经过「划分」操作后,一定可以确定一个元素的最终位置,并且保证该元素左边的元素都小于等于它,右边的元素都大于等于它。所以只要某次划分的元素位置q为倒数第 k个下标的时候,我们就已经找到了答案。
因此我们可以改进快速排序算法来解决这个问题:在分解的过程当中,我们会对子数组进行划分,如果划分得到的 q正好就是我们需要的下标,就直接返回 a[q];如果 q 比目标下标小,就递归右子区间,否则递归左子区间。这样就可以把原来递归两个区间变成只递归一个区间,提高了时间效率。这就是「快速选择」算法。
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def partition(nums, left, right):
import random
p = random.randint(left, right)
nums[left], nums[p] = nums[p], nums[left]
pivot = nums[left] # 初始化一个待比较数据
i, j = left, right
while(i < j):
while(i < j and nums[j] >= pivot): # 从后往前查找,直到找到一个比pivot更小的数
j -= 1
nums[i] = nums[j] # 将更小的数放入左边
while(i < j and nums[i] <= pivot): # 从前往后找,直到找到一个比pivot更大的数
i += 1
nums[j] = nums[i] # 将更大的数放入右边
# 循环结束,i与j相等
nums[i] = pivot # 待比较数据放入最终位置
return i # 返回待比较数据最终位置
left, right, p = 0, len(nums)-1, len(nums)-k
while True:
while left < right:
index = partition(nums, left, right)
if index == p:
return nums[index]
elif index < p:
left = index + 1
else:
right = index - 1
return nums[left]
如果直接选择左端点或右端点作为轴,极端情况发生时效率很低。因此我们使用随机选择轴的方法。方法是先随机选择一个下标,然后将该下标元素与左端点元素进行交换,再以左端点元素作为轴排序。
堆排序
使用容量为 k 的小顶堆。元素个数小于 k 的时候,直接放进去,元素个数大于 k 的时候,小于等于堆顶元素,就扔掉,大于堆顶元素,就替换,最后堆顶中的元素就是堆中最小的,整个数组中的第 k 大元素。
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
import heapq
heap = []
for i in range(k):
heapq.heappush(heap, nums[i])
for j in range(k, len(nums)):
if nums[j] > heap[0]:
heapq.heapreplace(heap, nums[j])
return heap[0]
直接用库了。headq默认是最小堆,heaprepalce() 函数的用法是删除堆中最小元素并加入一个元素。