1. 题目描述
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
2. 思路
本题和 【剑指 Offer】 40. 最小的k个数. 相似
【求最大 top k 问题用最小堆,求最小 top k 问题用最大堆】
- 维护一个长度为 k k k 的小顶堆
- 随后从第 k + 1 k + 1 k+1 个数开始遍历,如果当前遍历到的数比大顶堆的堆顶的数要大,就把堆顶的数弹出,再插入当前遍历到的数。而后再 heapify 操作恢复一下最小堆结构
- 最后将小顶堆的堆顶(即第 k k k 小的数)返回即可。
3. 代码
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
n = len(nums)
hp = nums[:k]
heapq.heapify(hp)
for i in range(k, n):
if hp[0] < nums[i]:
hp[0] = nums[i]
heapq.heapify(hp)
return hp[0]
时间复杂度 : >
O
(
N
l
o
g
k
)
O(Nlogk)
O(Nlogk)
因为在 for 循环里多了 heapify()
的操作
推荐如下写法:
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
n = len(nums)
hp = nums[:k]
heapq.heapify(hp)
for i in range(k, n):
if hp[0] < nums[i]:
# hp[0] = nums[i]
# heapq.heapify(hp)
heapq.heappop(hp)
heapq.heappush(hp, nums[i])
return hp[0]
时间复杂度 : O ( N l o g k ) O(Nlogk) O(Nlogk)
手撸的 大顶堆 代码详见 参考 1.
参考:
====================================================
变形题是 第四范式 的算法一面题
变形:
给定一个规则的list, 形如山峰(满足单峰,且峰值点有可能在端点):[1,3,9,10,8,6,2],可能存在重复值
请写出程序,返回第k大的数,
不使用内置排序程序
解题思路 & 代码
由于有 峰值的特性,最终的时间复杂度肯定得 低于遍历一遍的 O ( N ) O(N) O(N) 法,所以考虑二分法。
- 由二分法找到最大值(峰值)的索引
- 根据峰值的索引,设置两个指针,分别从峰值往两侧探索
#coding=utf-8
import sys
#str = input()
#print(str)
import heapq
"""
def findKthLargest1(nums, k):
"""
小顶堆
"""
n = len(nums)
hp = nums[:k]
heapq.heapify(hp)
for i in range(k, n):
if hp[0] < nums[i]:
heapq.heappop(hp)
heapq.heappush(hp, nums[i])
return hp[0]
"""
def searchPeakIndex(nums):
left, right = 0, len(nums) - 1
# 特殊判断峰在端点的情况
if nums[left] > nums[left + 1]:
return 0
if nums[right - 1] < nums[right]:
return len(nums) - 1
while left <= right:
pivot = left + (right - left) // 2
if nums[pivot - 1] < nums[pivot] and nums[pivot] > nums[pivot + 1]: # 刚好在峰值处
return pivot
if nums[pivot - 1] < nums[pivot] < nums[pivot + 1]: # 左半部分,升序
left = pivot + 1
elif nums[pivot - 1] > nums[pivot] > nums[pivot + 1]: # 右半部分,升序
right = pivot - 1
return -1
def findKthLargest2(nums, index, k):
"""
指针法
index: 假设已经二分法已经找到了峰值位置对应的索引
"""
i = index - 1
j = index + 1
n = len(nums)
res = [index]
while i >=0 and j <= n-1:
if nums[i] >= nums[j]:
res.append(i)
res.append(j)
i -= 1
j += 1
return nums[res[k-1]]
if __name__ == '__main__':
nums = [1,3,9,10,8,6,2]
k = 4
#res1 = findKthLargest1(nums, k)
#print("res1", res1)
index = searchPeakIndex(nums)
res2 = findKthLargest2(nums, index, k) # 示例中,峰值 10 的索引为 3
print("res2", res2)
题主本硕机械专业,自学转互联网 算法岗成功,获得阿里、字节、美团、华为等 15+ offer
后续会在公众号 「苏学算法」分享各类学习笔记、面试经验,感兴趣的可以关注一波 😊 😊 😊~