【Leetcode037】数组中的第K个最大元素

215、数组中的第K个最大元素

给定整数数组 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

提示:
1 <= k <= nums.length <= 104
-104 <= nums[i] <= 104

方法一:sort直接粗暴

1.1 思路分析

调用sort包,排序后返回指定数值

1.2 代码实现

func findKthLargest(nums []int, k int) int {
    sort.Ints(nums)   // 升序
    return nums[len(nums)-k]
}

1.3 测试结果

【Leetcode037】数组中的第K个最大元素_第1张图片

1.4 复杂度

  • 时间复杂度:O(nlogn)。取决于sort.Ints()排序算法
  • 空间复杂度:O(1)。取决于sort.Ints()排序算法

方法二:快速排序

2.1 思路分析

2.2 代码实现

func findKthLargest(nums []int, k int) int {
    // 随机种子
    rand.Seed(time.Now().UnixNano())
    return quickSelect(nums, 0, len(nums)-1, len(nums)-k)
}

func quickSelect(a []int, l, r int, index int) int{
    q := randomPartition(a, l, r)
    if q == index{
        return a[q]
    }else if q < index{
        return quickSelect(a, q+1, r, index)
    }
    return quickSelect(a, l, q-1, index)
}

func randomPartition(a []int, l, r int) int{
    p := rand.Int() % (r-l+1) + l
    a[p], a[r] = a[r], a[p]    // 把基准放在最右边
    return partition2(a, l, r)
}
// 双路快排
func partition2(a []int, l, r int) int{
    key := a[r]

    for l < r{
        // 当队首元素小于等于key
        for l<r && a[l] <= key{
            l++
        }
        // 当队首元素大于key时,需要将其赋值给l
        a[r] = a[l]
        // 当队尾元素大于等于key
        for l<r && a[r] >= key{
            r--
        }
        a[l] = a[r]
    }
    a[l] = key
    return l
}
// 单路快排
func partition(a []int, l, r int) int{
    key := a[r]
    i := l-1
    for j:=l; j<r; j++{
        if key>=a[j]{
            i++
            a[i], a[j] = a[j], a[i]
        }
    }
    a[i+1], a[r] = a[r], a[i+1]
    return i+1
}

2.3 测试结果

  • 单路快排:
    【Leetcode037】数组中的第K个最大元素_第2张图片
  • 双路快排:
    【Leetcode037】数组中的第K个最大元素_第3张图片

2.4 复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(logn)

方法三:堆排序

3.1 思路分析

3.2 代码实现

func findKthLargest(nums []int, k int) int {
    heapSize := len(nums)
    buildMaxHeap(nums, heapSize)  // 形成大顶堆
    for i:=len(nums)-1; i>=len(nums)-k+1; i--{
        nums[0], nums[i] = nums[i], nums[0] // 最大值与尾部交换
        heapSize--  // 成为最大值的尾部切除
        heapify(nums, 0, heapSize)  // 新的堆,从0开始
    }
    return nums[0]
}

// 创建大顶堆
func buildMaxHeap(nums []int, heapSize int){
    for i:=heapSize/2; i>=0; i--{   // 从后向前遍历非叶子节点
        heapify(nums, i, heapSize)
    }
}

// 检查并调整形成大顶堆
func heapify(nums []int, i, heapSize int){
    left, right, largest := 2*i+1, 2*i+2, i // 左子叶,右子叶,根
    if left<heapSize && nums[largest]<nums[left]{
        largest = left  // 如果有左子叶且大于当前最大值
    }
    if right<heapSize && nums[largest]<nums[right]{
        largest = right // 如果有右子叶且大于当前最大值
    }
    if largest != i{
        // 最大值不是根节点
        nums[largest], nums[i] = nums[i], nums[largest]
        heapify(nums, largest, heapSize)  // 交换后向先前最大值位置的子叶进行检查调整是否大顶堆
    }
}

3.3 测试结果

【Leetcode037】数组中的第K个最大元素_第4张图片

3.4 复杂度

  • 时间复杂度:O(nlogn),建堆的时间代价是 O(n),删除的总代价是 O(klogn),因为 k < nk
  • 空间复杂度:O(logn),即递归使用栈空间的空间代价。

你可能感兴趣的:(算法题,排序算法,算法,leetcode)