思路1:快排
考虑先使用快排实现,练习快排。
时间复杂度O(N):如果我们把每次分区遍历的元素个数加起来,就是:n+n/2+n/4+n/8+…+1。这是一个等比数列求和,最后的和等于 2n-1。所以,时间复杂度就为 O(n)。
因为一次快排可以实现一个数归位,比较index和n-K的大小
如果index==n-K就找到了
空间复杂度 : O(1)
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def getindex(low,high): # 返回依次排序,首元素就位后的索引
key = nums[low]
while low<high:
while low<high and nums[high]>=key:
high-=1
nums[low]=nums[high]
while low<high and nums[low]<=key:
low+=1
nums[high] = nums[low]
# 跳出循环时low和high相等,此时的low或high就是key的正确索引位置
nums[low] = key
return low
n = len(nums)
low,high = 0,n-1
index = getindex(low,high)
while index!=n-k:
if n-k<index:
high = index-1
else:
low = index+1
index = getindex(low,high)
return nums[index]
优化:虽然时间复杂度不高,但是运行也太慢了。对比了官方解答,发现快很多。关键点在于每次不以索引0作为pivot,而是n-k。这样能够避免极端测试用例,通常能够更快的找到答案。
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def getindex(low, high,pivot): # 修改
key = nums[pivot] # 修改
nums[pivot] = nums[low] # 修改 需要把首元素先复制出来,否则会丢失
while low < high:
while low < high and nums[high] >= key:
high -= 1
nums[low] = nums[high]
while low < high and nums[low] <= key:
low += 1
nums[high] = nums[low]
# 跳出循环时low和high相等,此时的low或high就是key的正确索引位置
nums[low] = key
return low
n = len(nums)
low, high = 0, n - 1
index = getindex(low, high,0) # 修改
while index != n - k:
if n - k < index:
high = index - 1
else:
low = index + 1
index = getindex(low, high,n-k) # 修改
return nums[index]
思路2:
库函数 heapq.nlargest(k,nums)
时间复杂度O(nlogk)
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
return heapq.nlargest(k,nums)[-1] # 堆的前k个数,逆序排列,取末尾的最小值
思路3:手写快排
参考 powcai-排序
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def adjust_maxheap(idx,maxlen):
left = 2*idx+1 # 左孩子
right = 2*idx+2 # 右孩子
new_max = idx # 新的最大值的索引初始值
if left<maxlen and nums[left]>nums[new_max]:
new_max = left
if right <maxlen and nums[right]>nums[new_max]:
new_max = right
if new_max !=idx: # 如果新的最大值不是旧的最大值,即需要进行调整
nums[new_max],nums[idx] = nums[idx],nums[new_max]
adjust_maxheap(new_max,maxlen) # 那么需要把新的做大值的树进行继续调整
# 建堆
n = len(nums)
for i in range(n//2-1,-1,-1): # 起点为倒数第一个非叶子节点,终点是第一个节点
adjust_maxheap(i,n)
# print(nums)
# 取数调整堆
for j in range(1,k): # 循环k-1次调整,此时第k大的值在根位置
nums[0],nums[-j] = nums[-j],nums[0]
adjust_maxheap(0,n-j)
return nums[0]