题目:
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
解析
无脑的做法
先使用排序算法,然后线性时间取出第k大的元素即可。时间复杂度是O(NlogN)
堆,也称为优先队列
建一个大小为K的最大堆或者最小堆。遍历数组,把遍历到的值加入到堆当中,这个操作的时间复杂度是O(logK),遍历N次。总共是O(NlogK)
使用python的heapq中的nlargest即可直接获得。空间复杂度是O(K)
之所以这个方法也称作优先队列法,因为堆中的数据都是按照大小排好的。符合优先队列按照值大小这个优先条件排序,所以是优先队列。
快排+递归
在快排算法中,首先要随机选一个基准值,把小于这个基准的值放到基准位置的左边,把大于基准值的值放到基准值的右边。然后在对两个部分继续按照这种方式递归的划分。
首先要将第k大的问题看成第N-k+1小的问题。
在这道题中,我们只需要关注一边即可,如果基准值的index大于N-k(第k大等于第N-k+1小),则只需要关注index+1到这一段的最右边这个区域。如果基准值的index小于N-k,则我们只需要关注这一段的左边到index-1这个区域。因为我们要的值都在这个区域内。如果等于,则已经找到了。
再来解释一下第K大等于N-k+1小这个含义。如果一个序列[2,3,4,1,5,6],需要找到第2大的值,是5,同时5也是第5小的值。2和5满足 5 = N-k+1(N=6)。但是在数组中,起始位置是从0开始的,为了统一索引位置,我们就认为5是第4小的数。
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def spilt(left, right, index):
base = nums[index]
nums[index], nums[right] = nums[right], nums[index]
store_index = left
for i in range(left, right):
if nums[i] < base:
nums[i], nums[store_index] = nums[store_index], nums[i]
store_index += 1
nums[store_index], nums[right] = nums[right], nums[store_index]
return store_index
def select(left, right, kthsmall):
if left == right:
return nums[left]
index = random.randint(left, right)
index = spilt(left, right, index)
if index == kthsmall:
return nums[index]
elif index > kthsmall:
return select(left, index - 1, kthsmall)
else:
return select(index + 1, right, kthsmall)
return select(0, len(nums)-1, len(nums) - k)