力扣100(38)堆-数组中的第K个最大元素

方法一:快速选择算法(最优解,O (n) 时间复杂度)

这是唯一满足题目 O (n) 时间复杂度要求的解法,也是面试首选。

1. 核心思想:基于快速排序的改进

快速排序的核心是划分(partition)

  • 选一个元素作为主元(pivot)
  • 把数组分成两部分:左边所有元素 ≤ 主元,右边所有元素 ≥ 主元
  • 主元的最终位置就确定了,不会再变

快速选择的精髓是:我们不需要把整个数组排序,只需要找到目标位置的元素即可

  • 我们要找的是第 k 大的元素,也就是升序排序后下标为 n-k 的元素(因为升序数组的倒数第 k 个就是第 n-k 个)
  • 每次划分后,看主元的位置 q 和目标位置n-k的关系:
    1. 如果 q == n-k:主元就是我们要找的元素,直接返回
    2. 如果 q < n-k:目标元素在右半部分,递归右半区间
    3. 如果 q > n-k:目标元素在左半部分,递归左半区间

这样我们每次只需要递归一个区间,而不是两个,时间复杂度从快速排序的 O (nlogn) 降到了期望 O (n)。

2. 完整解题步骤

  1. 计算数组长度 n,目标下标为 target = n - k
  2. 调用快速选择函数,在区间[0, n-1]中找下标为 target 的元素
  3. 快速选择函数:
    • 如果区间只有一个元素(l==r),返回该元素

    • 对区间进行划分,得到主元的位置 q

    • 根据 q 和 target 的关系,递归左半或右半区间

      cpp 复制代码
      class Solution {
      public:
          int quickselect(vector<int> &nums,int l,int r,int k){
                  //参数1:输入
                  //2:左指针的初始位置
                  //3:右指针的初始位置
                  //4:从左往右数第k个
      
             //左指针等于右指针 返回
              if(l == r){
                  return nums[k];
              }
      
              //选择一个基准数 要求这个数的左边的值都小于他 右边的数都大于他
              int p = nums[l];
              int i = l-1;//出格 避免下面的循环不启动
              int j = r+1;
      
              while(i<j){
      
                  do i++; while(nums[i]<p);//要满足左边的数小于基准值,循环结束的时候 i就是大于基准值的位置
                  do j--; while(nums[j]>p);//右边的数要大于基准值,循环结束,j就是小于基准值的位置。
      
                  //小的需要在左边 大的在右边 
                  //所以如果i在j左边 就得交换
                  if(i<j){
                      swap(nums[i],nums[j]);
              }
              }
              //循环结束j的位置就是基准的位置
      
              //接下来看看k目标位置和基准位置的关系 如果等于直接返回j。如果小于j就在左半部分 直接递归左半部分 反之亦然
              if(k <= j )
                  return quickselect(nums,l,j,k);
              else return quickselect(nums,j+1,r,k);
      
          }
          int findKthLargest(vector<int>& nums, int k) {
      
      
              int n  = nums.size();
              //n-k是相当于升序后的倒数第K个 也就是第k个最大元素
              return quickselect(nums,0,n-1,n-k);
      
             
             
              
          }
      };

大根堆法:

大根堆 = 堆顶永远是整个堆里最大的数

我们要找第 K 大,思路超级简单:

  1. 把数组建成大根堆
  2. 把最大的扔掉(1 次)
  3. 把第二大的扔掉(2 次)
  4. ......
  5. 扔完 K-1 次以后,堆顶就是第 K 大!

完美解决!

堆长啥样?(一句话)

堆就是一个数组 ,但逻辑上是完全二叉树

  • 下标 i左孩子 = i*2 + 1
  • 下标 i右孩子 = i*2 + 2
  • 父节点永远比孩子大(大根堆)

三、堆法只有 3 个核心动作

1. 调整堆(heapify)

让一个位置重新变成大根堆: 把当前节点和左右孩子比,谁大谁放上面,不对就交换,再递归调整。

2. 建堆

从最后一个非叶子节点开始,从下往上全部调整一遍。

3. 删除堆顶(拿最大值)

  • 堆顶和最后一个元素交换
  • 堆大小 -1
  • 重新调整堆顶
cpp 复制代码
class Solution {
public:
    void heapify(vector<int>& a, int i, int size) {
        // 让i这个位置变成大根堆
        int left = i * 2 + 1;  // 左孩子
        int right = i * 2 + 2; // 右孩子

        int maxIdx = i; // 先假设自己最大

        // 如果左孩子或者右孩子更大 就最大值变成他 最后maxIdx存的就是最大值
        // 左孩子更大
        if (left < size && a[left] > a[maxIdx])
            maxIdx = left;

        // 右孩子更大
        if (right < size && a[right] > a[maxIdx])
            maxIdx = right;

        // 如果最大的不是自己 那就交换 让自己是最大的
        if (maxIdx != i) {
            swap(a[i], a[maxIdx]);
            // 换完后调整
            heapify(a, maxIdx, size);
        }
    }

    // 核心2:建堆 → 把整个数组变成大根堆
    void buildHeap(vector<int>& a, int size) {
        // 从最后一个非叶子节点往上建
        for (int i = size / 2 - 1; i >= 0; i--) {
            heapify(a, i, size);
        }
    }

    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size();
        // 建堆
        buildHeap(nums, n); // 堆顶是最大数
         int h = n;
        // 把k-1次最大值弄出来
        for (int i = n - 1; i >= n - k + 1; i--) {
            swap(nums[0], nums[i]); // 堆顶最大的数 放到最后
            // 然后重新调整新堆顶
            
            h--;
            heapify(nums, 0, h);
        }

        return nums[0];
    }
};
相关推荐
全球通史19 小时前
Jetson Nano 双摄像头芯片检测视觉系统:小尺度难定位问题解决,从零开始实现教程说明
嵌入式硬件·算法·ubuntu·性能优化
千寻girling19 小时前
机器学习 | 监督学习算法(了解) | 尚硅谷学习
开发语言·人工智能·后端·python·学习·算法·机器学习
强盛机器学习~19 小时前
2026年SCI一区新算法-灰叶猴优化算法(GLO)-公式原理详解与性能测评 Matlab代码免费获取
算法·matlab·进化计算·群体智能·智能优化算法·元启发式算法
c2385619 小时前
MyVector模拟实现
算法
圣保罗的大教堂20 小时前
leetcode 1871. 跳跃游戏 VII 中等
leetcode
hoiii18720 小时前
matlab基础贝叶斯变换的压缩感知
算法·机器学习·matlab
闻缺陷则喜何志丹20 小时前
P8134 [ICPC 2020 WF] Opportunity Cost|普及+
c++·算法·洛谷
c2385620 小时前
MySrting的模拟实现
开发语言·c++·算法
周杰伦fans20 小时前
DeepSeek 智能效果全景展示
算法