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

这篇博客探讨了如何在整数数组中找到第K个最大元素的方法,包括堆排序和快速排序两种策略。堆排序通过构建大顶堆找到最大值并交换到末尾,反复操作得到有序序列。快速排序的partition操作能在一次操作中确定基准值的正确位置,以此判断第K大元素在序列的哪一侧。
摘要由CSDN通过智能技术生成

题目

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

题解一 堆排序

var findKthLargest = function(nums, k) {
   let heapSize=nums.length
    buildMaxHeap(nums,heapSize) // 构建好了一个大顶堆
    // 进行下沉 大顶堆是最大元素下沉到末尾
    for(let i=nums.length-1;i>=nums.length-k+1;i--){
        swap(nums,0,i)
        --heapSize // 下沉后的元素不参与到大顶堆的调整
        // 重新调整大顶堆
         maxHeapify(nums, 0, heapSize);
    }
    return nums[0]
   // 自下而上构建一颗大顶堆
   function buildMaxHeap(nums,heapSize){
     for(let i=Math.floor(heapSize/2)-1;i>=0;i--){
        maxHeapify(nums,i,heapSize)
     }
   }
   // 从左向右,自上而下的调整节点
   function maxHeapify(nums,i,heapSize){
       let l=i*2+1
       let r=i*2+2
       let largest=i
       if(l < heapSize && nums[l] > nums[largest]){
           largest=l
       }
       if(r < heapSize && nums[r] > nums[largest]){
           largest=r
       }
       if(largest!==i){
           swap(nums,i,largest) // 进行节点调整
           // 继续调整下面的非叶子节点
           maxHeapify(nums,largest,heapSize)//调整不了就不调了
       }
   }
   function swap(a,  i,  j){
        let temp = a[i];
        a[i] = a[j];
        a[j] = temp;
   }
};

笔记:

  1. 堆排序:

    基本思想
    将待排序序列构造成一个大顶堆

    注意:这里使用的是数组,而不是一颗二叉树

    此时:整个序列的 最大值就是堆顶的根节点

    将其 与末尾元素进行交换,此时末尾就是最大值

    然后将剩余 n-1 个元素重新构造成一个堆,这样 就会得到 n 个元素的次小值。如此反复,便能的得到一个有序序列。

    • 顺序存储二叉树
      特点
      第 n 个元素的 左子节点 为 2n+1
      第 n 个元素的 右子节点 为 2
      n+2
      第 n 个元素的 父节点 为 (n-1)/2
      最后一个非叶子节点为 Math.floor(arr.length/2)-1
  2. 最后一个非叶子节点为 Math.floor(arr.length/2)-1,先得到一个数组,构造成一棵树,从最后一个非叶子节点开始,跟两个子节点比较大小,将大的那个放在父节点上,然后按顺序将前面的父节点的位置调整好,最后根节点就是最大的。取第二大的节点,将根节点与最后一个子节点交换,然后将最后一个子节点去除,再将树重排一遍,这时新的根节点就是第二大的节点。

题解二 快排

let findKthLargest = function(nums, k) {
    return quickSelect(nums, nums.length - k)
};

let quickSelect = (arr, k) => {
  return quick(arr, 0 , arr.length - 1, k)
}

let quick = (arr, left, right, k) => {
  let index
  if(left < right) {
    // 划分数组
    index = partition(arr, left, right)
    // Top k
    if(k === index) {
        return arr[index]
    } else if(k < index) {//都要有return!!!
        // Top k 在左边
        return quick(arr, left, index-1, k)
    } else {
        // Top k 在右边
        return quick(arr, index+1, right, k)
    }
  }
  return arr[left]
}

let partition = (arr, left, right) => {
  // 取中间项为基准
  var datum = arr[Math.floor(Math.random() * (right - left + 1)) + left],
      i = left,
      j = right
  // 开始调整
  while(i < j) {
    // 左指针右移
    while(arr[i] < datum) {
      i++
    }
    // 右指针左移
    while(arr[j] > datum) {
      j--
    }
    // 交换 如果提前停下说明arr[i]>arr[j] 需要交换
    if(i < j) swap(arr, i, j)
    // 当数组中存在重复数据时,即都为datum,但位置不同
    // 继续递增i,防止死循环
    if(arr[i] === arr[j] && i !== j) {
        i++
    }
  }
  return i
}

// 交换
let swap = (arr, i , j) => {
    let temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp
}


var findKthLargest = function(nums, k) {
   return quickSort(nums,0,nums.length-1,nums.length-k)
};
let quickSort = (arr,left,right,k)=>{
    let index;
    if(left<right){
        index=partition(arr,left,right)
        if(index === k){
            return arr[index]
        }else if(k<index){
            quickSort(arr,left,index-1,k)
        }else{
            quickSort(arr,index+1,right,k)
        }
    }
    return arr[left];
}
let partition = (arr,left,right)=>{
    let dataRad=arr[Math.floor(Math.random()*(right-left+1))+left],i=left,j=right
    while(i<j){
        while(arr[i]<dataRad){i++}
        while(arr[j]>dataRad){j--}
        if(i<j){swap(arr,i,j)}
        if(arr[i]===arr[j]&&i!==j){
            i++
        }
    }
    return i
}
let swap = (arr, i , j) => {
    let temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp
}

笔记:

  1. 快排,分区,partition

    其实没有必要全部排序,可以利用快速排序的 partition 操作,找到第 K 个最大元素。

    每进行一次快速排序的 partition 操作,就能找到这次我们选中的基准值排序之后的正确位置。

    如果它的位置刚好是排序之后第 K 个最大元素的位置,即 len - k,我们直接得到了答案;
    因为进行 partition 操作之后,位于基准值之前的元素都要小于基准值,位于基准值之后的元素都要大于等于基准值。

    如果它的位置小于排序之后第 K 个最大元素的位置,我们就去它之后寻找第 K 个最大元素;
    如果它的位置大于排序之后第 K 个最大元素的位置,我们就去它之前寻找第 K 个最大元素;

  2. 我们仅仅需要在每执行一次的时候,比较基准值位置是否在 n-k 位置上,如果小于 n-k ,则第 k 个最大值在基准值的右边,我们只需递归基准值右边的子序列即可;如果大于 n-k ,则第 k 个最大值在基准值的做边,我们只需递归***基准值左边的子序列即可;如果等于 n-k ,则第 k 个最大值就是基准值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codrab

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值