Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
输入:[3,2,1,5,6,4] k=2 输出:5
输入:[3,2,3,1,2,4,5,5,6] k=4 输出:4
二、思路分析
- 最简单的做法是使用内置库排序函数,数组排序后,返回倒数第k个元素,以快排为例平均复杂度为
O(nlogn) - 快排过程中每次划分区间,每轮必然使得一个元素到达最终排序位置。通过这一性质,如果第k大元素与达到最终位置的元素相同,则在快排的过程中即选出了元素,可以达到最优O(N)的时间复杂度。举例:数组
[3,2,1,5,6,4]第一轮快排选择第一个元素3为主元,则3左边为2,1右边为5,6,4,3的位置被确定。 - 需要注意,该题目要求的是规定大小的数组,而不是流数据,如果数组大小不确定,则基于堆排序进行选择,选择主元时随机效率更优。
三、AC 代码
func findKthLargest(nums []int, k int) int {
return quickeSelect(nums,0,len(nums)-1,len(nums)-k)
}
func quickeSelect(nums []int,l,r,index int) int{
q := randomPartition(nums,l,r )
//主元下标与目标一致,返回第k大数字
if q == index {
return nums[q]
}else if q < index {
return quickeSelect(nums, q+1,r,index)
}
return quickeSelect(nums,l,q-1,index)
}
//随机主元下标,并且更换到nums[r]
func randomPartition(nums []int,l,r int) int {
rand.Seed(time.Now().UnixNano())
i := rand.Int()%(r-l+1) + l
nums[i],nums[r] = nums[r],nums[i]
return partition(nums,l,r)
}
//分区
func partition(nums []int,l,r int) int {
pivot := nums[r]
i := l -1
for j := l;j<r ;j++ {
if nums[j] <= pivot {
i++
nums[i],nums[j] = nums[j],nums[i]
}
}
//更换新的主元
nums[i+1],nums[r] = nums[r],nums[i+1]
return i+1
}
四、总结
快速选择是基于快排的算法,快速选择出第K大元素,写法上的区别与快排相比,传入了需要找到的第k个元素的下标。代码中为len(nums) - k ,如果快速选择的主元下标与其相等,则找到了目标元素。如果小于目标元素下标,则右侧继续选择,即quickeSelect(nums, q+1,r,index) 。