LeetCode #215 数组中的第K个最大元素
题目描述
在未排序的数组中找到第 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
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
方法一:快排
直接调用库函数,写这种肯定找死。注意可能要打乱一下数组,否则最坏情况为 O ( N 2 ) O(N^2) O(N2)
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
random.shuffle(nums)
nums.sort(reverse=True)
return nums[k-1]
时间复杂度:
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN)
空间复杂度:
O
(
1
)
O(1)
O(1)
方法二:快速选择
快排的改进,快排是一种分治思想的实现,没做一层快排可以将数组分成两份并确定一个数的位置。分析题目可以知道,要找到第 k 个最大的元素,找到这个元素被划分在哪边就可以了
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
random.shuffle(nums)
size = len(nums)
target = size -k
left = 0
right = size
while True:
index = self.__partition(nums, left, right)
if index == target:
return nums[index]
elif index < target:
# 下一轮在 [index + 1, right) 里找
left = index + 1
else:
# 下一轮在 [left, index) 里找
right = index
# 只做一层快排
def __partition(self, nums, left, right):
base = nums[left]
j = left
for i in range(left + 1, right):
if nums[i] < base:
j += 1
nums[i], nums[j] = nums[j], nums[i]
nums[left], nums[j] = nums[j], nums[left]
return j
- 时间复杂度: O ( N ) O(N) O(N) (实际上跑得比快排还慢。。。这个 80ms,快排 48ms [捂脸])
- 空间复杂度: O ( 1 ) O(1) O(1)
方法三:堆排序
第 K
大元素,其实就是数组排序后后半段最小的那个元素,因此可以维系一个有 K
个元素的“最小堆”。这里的最小堆指的是后半段元素组成的最小堆。
- 当i<k,压数据进堆
- 当i>=k,比较sums[i]和堆底元素,如果大于,则使用heapreplace进行替换,否则跳过
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
from heapq import heappush, heapreplace
# 使用小顶堆
heap = []
for i in range(len(nums)):
if i < k:
heappush(heap, nums[i])
elif nums[i] > heap[0]:
m = heapreplace(heap, nums[i])
return heap[0]
- 时间复杂度: O ( N ) O(N) O(N):因为堆是初始化出来的,不是重建出来的,所以键堆操作是 O ( k ) O(k) O(k)
- 空间复杂度: O ( k ) O(k) O(k):需要维护一个大小为 k 的大顶堆