春招打卡|高频题:第k个最大元素-快速选择算法

148 阅读2分钟

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

二、思路分析

  1. 最简单的做法是使用内置库排序函数,数组排序后,返回倒数第k个元素,以快排为例平均复杂度为O(nlogn)
  2. 快排过程中每次划分区间,每轮必然使得一个元素到达最终排序位置。通过这一性质,如果第k大元素与达到最终位置的元素相同,则在快排的过程中即选出了元素,可以达到最优O(N)的时间复杂度。举例:数组[3,2,1,5,6,4] 第一轮快排选择第一个元素3为主元,则3左边为2,1 右边为5,6,4 ,3的位置被确定。
  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)