链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/
这道题可以用来练习快排、归排、快速选择三种算法,性价比非常高,建议隔天一刷。
1.题目描述
给定整数数组 nums 和整数 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
2.快排解法
把快排模板理解了并背住,直接就上手写了。但是快排模板非常多,这时候一个简单好记的就尤为重要,这里给出一个较为简洁的版本。
class Solution:
"""
快排解法: 写法二(更加简介) 时间 O(nlogn) 空间O(1)
"""
def findKthLargest(self, nums: List[int], k: int) -> int:
if not nums:
return -1
self.quick_sort(nums, 0, len(nums) - 1)
return nums[-k]
def quick_sort(self, nums, begin, end):
if begin >= end:
return
pivot = nums[(begin + end) >> 1]
left, right = begin, end
while left <= right:
while left <= right and nums[right] > pivot:
right -= 1
while left <= right and nums[left] < pivot:
left += 1
if left <= right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
self.quick_sort(nums, begin, right)
self.quick_sort(nums, left, end)
3.归并排序
归并排序也是面试热门考点,虽然没快排频率那么高,但是多练练也没坏处。归并排序还要O(n)的额外空间消耗,所以实际用得比较少,这里采用思维复杂度比较低(较简单)的归并方法:采用原数组大小的额外空间。其实也可以不用开这么大的数组,可以另外试试一道合并有序数组的题:88. 合并两个有序数组。
class Solution:
"""
归并排序: 时间 O(nlogn) 空间O(n)
"""
def findKthLargest(self, nums: List[int], k: int) -> int:
if not nums:
return -1
temp = [0] * len(nums)
self.merge_sort(nums, 0, len(nums) - 1, temp)
return nums[-k]
def merge_sort(self, nums, begin, end, temp):
if begin >= end:
return
mid = (begin + end) >> 1
self.merge_sort(nums, begin, mid, temp)
self.merge_sort(nums, mid + 1, end, temp)
# merge
self.merge(nums, begin, end, temp)
def merge(self, nums, begin, end, temp):
mid = (begin + end) >> 1
left, right = begin, mid + 1
index = begin
while left <= mid and right <= end:
if nums[left] <= nums[right]:
temp[index] = nums[left]
left += 1
if nums[left] > nums[right]:
temp[index] = nums[right]
right += 1
index += 1
while left <= mid:
temp[index] = nums[left]
left += 1
index += 1
while right <= end:
temp[index] = nums[right]
right += 1
index += 1
for i in range(begin, end + 1):
nums[i] = temp[i]
3.快速选择
快速选择算法 时间O(n),非常强大,其实也是使用了快排的思想,只不过不再做全排序,只通过缩小区间找第k大的数。下面给出了代码和时间复杂度证明。
class Solution:
"""
Quick Select: 时间O(n)
T(n) = O(n) + T(n/2)
= O(n) + O(n/2) + T(n/4)
= O(n) + O(n/2) + O(n/4) + T(n/8)
= O(n) + O(n/2) + O(n/4) + ... + O(2) + T(1)
= O(n) + O(n/2) + ... + O(2) + O(1)
limT(n)_{n->inf} = (a0 - an*q) / (1-q)
= (n - 1/2) / (1/2)
= 2n - 1
= O(n)
"""
def findKthLargest(self, nums: List[int], k: int) -> int:
if not nums:
return -1
return self.quick_select(nums, 0, len(nums) - 1, k)
def quick_select(self, nums, begin, end, k):
if begin == end:
return nums[begin]
index = (begin + end) >> 1
nums[begin], nums[index] = nums[index], nums[begin]
pivot = nums[begin]
left, right = begin, end
while left < right:
while left < right and nums[right] < pivot:
right -= 1
if left < right:
nums[left] = nums[right]
left += 1
while left < right and nums[left] > pivot:
left += 1
if left < right:
nums[right] = nums[left]
right -= 1
nums[left] = pivot
if (begin + k - 1) < left:
return self.quick_select(nums, begin, left - 1, k)
if (begin + k - 1) > left:
return self.quick_select(nums, left + 1, end, k - (left + 1 - begin))
return pivot