这是我参与更文挑战的第8天,活动详情查看: 更文挑战。
题目215. 数组中的第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 总是有效的,且 1 ≤ k ≤ 数组的长度。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/kt… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:快排
用性能较好的排序方法对整个数组排序,找到第K大的元素。
代码参考
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var findKthLargest = function(nums, k) {
const sortedArr = nums.sort((a,b)=>a-b)
return sortedArr[nums.length-k]
};
方法二:快排优化
快排里的分而治之,每次拿到一个基准,可以确定基准的最终位置,即最终在有序数组里的顺序。 基准和我们要查找的K大元素的位置对比,不断缩小查找范围,知道最终的基准就是要找到的位置,即可实现对部分元素排序找到K大元素。
-_-||不知道为什么在LeetCode上执行超时,而同样的case我本地没问题。。。
代码
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var findKthLargest = function(nums, k) {
const len = nums.length
const target = len - k
let left = 0
let right = len-1
while (true) {
//分而治之,每次确定了数组里某个pivort的位置。这个位置和目标位置的关系做比较,决定后续要不要继续查找
const resultIndex = partition(nums,left,right)
if(resultIndex < target){
left = resultIndex+1
}else if(resultIndex > target){
right = resultIndex - 1
}else{
return nums[resultIndex]
}
}
};
function partition(nums,left,right) {
const len = right - left + 1
const pivort = Math.floor(Math.random() * len )+left
const lessThanPivortValue = nums.slice(left,right+1).filter((item,index) =>{
const flag = (item <= nums[pivort]) && ((index+left) !== pivort)
return flag
})
const bigThanPivortValue = nums.slice(left,right+1).filter((item,index) =>{
const flag = item > nums[pivort]&& (index+left) !== pivort
return flag
})
const newArr = lessThanPivortValue.concat([nums[pivort]],bigThanPivortValue)
nums.splice(left,len,...newArr)
return lessThanPivortValue.length
}
方法三:堆实现
用堆来存储K大的元素,遍历完数组后返回第K大元素。因此可以用最小堆,这样堆顶就是第K大元素。
最小堆我们采用完全二叉树来存储,这样有一个特定,从树根从1开始数,它的左子树的序号是最近父节点i的2i倍,右子树是最近父节点i的2i+1倍。因此我们从1开始存储堆里的有效数据。
最小堆的特性是最小的放在上面,最大的放在下面。
当我们往堆里插入一个元素,放到末尾后。这个元素如果是比它的父节点小,那么就应该和父节点交换位置,这样一直比下去,找到它的最终的大小合适的位置。
同理,当我们删除堆顶后,把末尾元素放到堆顶,这样最小保持了堆的结构。堆顶应该小于它的子节点,这样一直比较下去,找到堆顶元素合适的位置。
代码参考
class MinHeap{
constructor(){
//第0个元素不存储,从1开始存储
this.heap = [0]
}
insert(data){
//插入新的数据,并对数据排序使得最小的元素在堆顶
this.heap.push(data)
this.shiftUp(this.heap.length-1)
}
shiftUp(index){
//元素上浮,和父元素比较大小,
const parentIndex = this.getParentIndex(index)
if(index === 0 || parentIndex === 0) return
if (this.heap[parentIndex] > this.heap[index]) {
this.swap(parentIndex,index)
this.shiftUp(parentIndex)
}
}
getParentIndex(i){
return parseInt(i/2)
}
getLeftIndex(i){
return 2 * i
}
getRightIndex(i){
return 2 * i +1
}
swap(i1, i2){
const temp = this.heap[i1]
this.heap[i1]= this.heap[i2]
this.heap[i2] = temp
}
delete(){
this.heap[1] = this.heap.pop()
this.shiftDown(1)
}
shiftDown(index){
//元素下沉,和子元素比较大小
const leftIndex = this.getLeftIndex(index)
const rightIndex = this.getRightIndex(index)
if (this.heap[leftIndex] <this.heap[index]) {
this.swap(leftIndex,index)
this.shiftDown(leftIndex)
}
if (this.heap[rightIndex] <this.heap[index]) {
this.swap(rightIndex,index)
this.shiftDown(rightIndex)
}
}
getSize(){
return this.heap.length - 1
}
getPeek(){
return this.heap[1]
}
}
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var findKthLargest = function(nums, k) {
const heap = new MinHeap()
for (let index = 0; index < nums.length; index++) {
heap.insert(nums[index])
const size = heap.getSize()
if(size > k){
heap.delete()
}
}
return heap.getPeek()
};