[算法]数组中第k大元素

913 阅读2分钟

数组中第k大元素

数组中的第K个最大元素

这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」。

正文

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

解析

题意求数组中第k大的元素,总体来说是非常简单的题,最简单也是最容易想到的算法就是排序求下标的题解。我们可以先简单实现排序法,然后在此基础上看看有没有其他方法或者更好的方法去实现。

排序法

求数组第k大元素,在排序中,实际上就是求排序数组中第 k + 1 个元素

所以首先实现对数组的排序,在众多排序算法中,可以参考之前文章实现的排序算法:选择排序, 插入排序,冒泡排序,希尔排序,快速排序 等等

由于是JS算法,我们可以利用JS API sort 简单实现排序即可。但是如果有更高的要求,可以考虑针对排序算法做出选择。

整体实现:

var findKthLargest = function(nums, k) {
   nums = nums.sort((a, b) => {
        return b - a
    })
    return nums[k - 1]
};

到目前为止其实就已经实现了题目的需求。

image.png

不使用排序:基于计数法求第k大元素

在众多排序中有一个利用空间交换时间的排序法叫桶排序,也叫计数排序,他是唯一一个不需要通过比较去实现的排序方法。但实际上我们可以利用这一原理,脱离排序去找到第K大元素。

var findKthLargest = function (nums, k) {
    let list = new Array(Math.max(...nums) + 1).fill(0) // 建立长度与最大数字相等的数组,并且元素设置为0
    for (let index = 0 ; index < nums.length; index++) {
        list[nums[index]] = list[nums[index]] + 1 // 利用下标来表示数字,元素的个数就是该下标数字出现的次数
    }
    for (let index = list.length - 1;  index >= 0 ; index--) { // 有大到小开始遍历
        const count = list[index] // 得出每个数字出现的次数
        k = k - count // 除去 k 个数字
        if (k <= 0) { // k 满足条件
            return index // 返回下标就是第k大元素
        } 
    }
}

这种算法巧避开了数字之间的比较,但是缺点也很明显,就是实际占用的空间是比较大的,因为他所占用的空间大小是由数字最大值来决定的。

优化计数法

利用计数法会发现实际上很多数组占位空间是用不到的,实际上我们用到的是 max - min 的空间,所以我们可以在空间上进行优化,提高空间利用率。

并且上述的算法,如果出现负数,将不再适用,因为小数是不能做数组下标的。

所以我们只要考虑 max - min 的差值作为长度,从 min 当作 0 开始计算是最为合理的。

var findKthLargest = function (nums, k) {
    let min = nums[0]
    let max = nums[0]
    // 找出最小, 最大值
    for (let i = 1; i < nums.length; i++) {
        let num = nums[i]
        min = min > num ? num : min
        max = max < num ? num : max
    }
    const list =  new Array(max - min + 1).fill(0) // 利用最大值减去最小值来做长度
    for (let index = 0 ; index < nums.length; index++) {
        const num = nums[index]
        list[num - min] = list[num - min] + 1 // 利用下标来表示数字,元素的个数就是该下标数字出现的次数
    }
    for (let index = list.length - 1;  index >= 0 ; index--) { // 有大到小开始遍历
        const count = list[index] // 得出每个数字出现的次数
        k = k - count // 除去 k 个数字
        if (k <= 0) { // k 满足条件
            return index + min // 返回下标就是第k大元素
        } 
    }
}

image.png