数组中第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]
};
到目前为止其实就已经实现了题目的需求。
不使用排序:基于计数法求第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大元素
}
}
}