leetcode215. 数组中的第K个最大元素,小根堆/快排思想

news2024/11/13 21:35:40

leetcode215. 数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104

目录

  • leetcode215. 数组中的第K个最大元素
    • 题目分析
  • 方法一:自建小根堆
    • 堆(优先队列)的概念介绍
      • 堆的定义
      • 大根堆和小根堆
      • 数组表示堆
      • 建堆的过程
      • 找前K个最大的数应该用小根堆
    • 算法步骤
    • 算法流程
    • 具体代码
    • 堆(优先队列)的构建与调整复杂度分析
      • 建堆的复杂度
      • 调整堆的复杂度
    • 总的时间复杂度
    • 空间复杂度
  • 方法二:快排思想
    • 题目分析
    • 算法步骤
    • 算法流程
    • 具体代码
    • 算法分析
    • 相似题目

题目分析

给定一个整数数组 nums 和一个整数 k,返回数组中第 k 小的元素。这个问题可以通过最小堆(优先队列)/快排思想来解决。

方法一:自建小根堆

在这里插入图片描述

堆(优先队列)的概念介绍

堆的定义

堆是一种特殊的完全二叉树,其中每个父节点的值都必须小于或等于其所有子节点的值(小根堆)或每个父节点的值都必须大于或等于其所有子节点的值(大根堆)。

大根堆和小根堆

  • 大根堆:每个父节点的值都必须大于或等于其所有子节点的值。
  • 小根堆:每个父节点的值都必须小于或等于其所有子节点的值。

数组表示堆

在计算机中,堆通常使用数组来表示。堆的根节点位于数组的第一个位置,数组的索引从0开始。

建堆的过程

堆的构建过程就是从最后一个非叶子结点开始从下往上调整。 最后一个非叶子节点怎么找?这里我们用数组表示待排序序列,则最后一个非叶子结点的位置是:数组长度/2-1。

找前K个最大的数应该用小根堆

  • 原因:使用小根堆可以在O(n)时间内找到第K大的数,这是因为每次从堆顶取出最小的数,直到剩下K个数,堆顶即为第K大的数。
  • 实现:初始化一个大小为K的小根堆,遍历数组,将每个元素与堆顶元素比较,如果小于堆顶元素,则将该元素与堆顶元素交换,并调整堆。重复此过程,直到数组遍历结束。

算法步骤

  1. 初始化一个最小堆 a,大小为 k
  2. 调用 buildMinHeap(a, k) 函数构建最小堆。
  3. 从后往前遍历数组 nums,每次将堆顶元素与当前遍历到的元素比较。
  4. 如果当前元素小于堆顶元素,则将当前元素与堆顶元素交换,然后对堆进行 adjMinHeap 操作。
  5. 重复步骤3和4,直到数组遍历结束。
  6. 堆顶元素即为第 k 小的元素。

算法流程

开始
初始化最小堆a
调用buildMinHeap a, k
遍历nums的每个元素
当前元素小于堆顶元素
交换当前元素和堆顶元素
调用adjMinHeap a, k
返回堆顶元素
结束

具体代码

class Solution {
public:
    void adjMinHeap(vector<int>& nums, int root, int heapsize) {
        int left = root * 2 + 1, right = root * 2 + 2, minimum = root;
        if (left < heapsize && nums[left] < nums[minimum])
            minimum = left;
        if (right < heapsize && nums[right] < nums[minimum])
            minimum = right;
        if (minimum != root) {
            swap(nums[minimum], nums[root]);
            adjMinHeap(nums, minimum, heapsize);
        }
    }

    void buildMinHeap(vector<int>& nums, int k) {
        for (int i = k / 2 - 1; i >= 0; i--)
            adjMinHeap(nums, i, k);
    }
    int findKthLargest(vector<int>& nums, int k) {
        buildMinHeap(nums, k);
        for (int i = k; i < nums.size(); i++) {
            if (nums[i] < nums[0])
                continue;
            swap(nums[0], nums[i]);
            adjMinHeap(nums, 0, k);
        }
        return nums[0];
    }
};

堆(优先队列)的构建与调整复杂度分析

建堆的复杂度

  • 时间复杂度:O(k),其中 k 是堆的大小。
  • 原因:建堆的过程是从数组的中间位置开始,然后逐步向上调整,直到根节点。由于堆的大小是 k,因此建堆的时间复杂度是 O(k)。

调整堆的复杂度

  • 时间复杂度:O(n log k),其中 n 是数组的大小,k 是堆的大小。
  • 原因:每次调整堆的时间复杂度是 O(log k),因为堆的调整是通过递归进行的,每次递归都会将问题规模减少一半。由于需要对数组中的每个元素进行一次调整,因此总的时间复杂度是 O(n log k)。

总的时间复杂度

  • 总时间复杂度:O(k + n log k)
  • 为什么是 O(n):尽管建堆和调整堆的时间复杂度分别是 O(k) 和 O(n log k),但由于 k 的数量级通常远小于 n,因此总的时间复杂度近似为 O(n log k)。在实际应用中,通常可以忽略 k 相对于 n 的影响,从而认为总的时间复杂度是 O(n)。

空间复杂度

  • O(k),因为只需要存储 k 个元素的最大堆。

方法二:快排思想

在这里插入图片描述

题目分析

快速选择算法是一种用于在未排序的数组中找到第 k 大的元素的算法。它基于快速排序的思想,但与快速排序不同,快速选择算法只对部分数组进行排序,从而找到第 k 大的元素。

算法步骤

  1. 选择数组的第一个元素作为划分元素。
  2. 将数组分为两部分:小于划分元素的和大于划分元素的。
  3. 如果 k 小于等于划分元素左侧的元素数量,则在左侧递归查找第 k 大的元素。
  4. 如果 k 大于划分元素左侧的元素数量,则在右侧递归查找第 k 大的元素。
  5. 重复步骤2-4,直到找到第 k 大的元素。

算法流程

开始
选择数组的第一个元素作为划分元素
将数组分为两部分 小于划分元素和大于划分元素
如果k小于等于划分元素左侧的元素数量
在左侧递归查找第k大的元素
否则在右侧递归查找第k大的元素
在右侧递归查找第k大的元素
返回第k大的元素
结束

具体代码

class Solution {
public:
    // 快速选择函数,用于找到数组中第k大的元素
    int quickselect(vector<int> &nums, int l, int r, int k) {
        // 如果左指针等于右指针,说明数组只有一个元素,直接返回该元素
        if (l == r)
            return nums[k];
        // 选择左边界作为划分元素
        int partition = nums[l], i = l - 1, j = r + 1;
        // 双指针遍历,将小于划分元素的放在左边,大于的放在右边
        while (i < j) {
            // 找到第一个大于等于划分元素的索引
            do i++; while (nums[i] < partition);
            // 找到第一个小于等于划分元素的索引
            do j--; while (nums[j] > partition);
            // 如果两个指针还没有相遇,交换它们指向的元素
            if (i < j)
                swap(nums[i], nums[j]);
        }
        // 如果k小于等于右指针,说明第k大的元素在左边,递归左半部分
        if (k <= j)return quickselect(nums, l, j, k);
        // 否则在右边,递归右半部分
        else return quickselect(nums, j + 1, r, k);
    }

    // 主函数,用于找到数组中第k大的元素
    int findKthLargest(vector<int> &nums, int k) {
        // 获取数组长度
        int n = nums.size();
        // 调用快速选择函数,注意第k大的元素实际上是第n-k小的元素
        return quickselect(nums, 0, n - 1, n - k);
    }
};

算法分析

  • 时间复杂度: O(n),最坏情况下时间复杂度为 O(n^2),但平均情况下接近 O(n)。
  • 空间复杂度: O(log n),由于使用了递归,递归深度为 log n。
  • 应用场景: 适用于查找第 k 大的元素,尤其当 k 远小于 n 时,效率较高。

相似题目

题目链接
215. 数组中的第K个最大元素https://leetcode.cn/problems/kth-largest-element-in-an-array/
218. 天际线问题https://leetcode.cn/problems/the-skyline-problem/
234. 回文链表https://leetcode.cn/problems/palindrome-linked-list/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2074921.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue面试集合

缓存 浏览器缓存和http缓存 浏览器缓存&#xff1a; 1&#xff0c;简单的缓存方式有cookie&#xff0c;localStorage和sessionStorage。 2&#xff0c;vue中keep-alive缓存动态组件&#xff1a; 全部缓存&#xff1a;使用<keep-alive>标签包裹缓存路由&#xff0c;ro…

JAVA电子器件制造行业生产管理系统计算机毕设计算机毕业设计

项目开发意义 目前小型企业基本上是采用人工完成生产及物料的车间计划,由于企业运作是以订单驱动而非计划生产,人工手段无法及时随新订单的到来更新计划,造成计划偏离实际;各个生产单位(车间)各自为战,分别提出物料、设备、专用工具的需求,在整个企业层面上很难较精确地控制物料…

机器学习:集成学习之随机森林

目录 前言 一、集成学习 1.集成学习的含义 2.集成学习的代表 3.集成学习的应用 二、随机森林 1.随机森林的特点 2.随机森林生成步骤 3.随机森林优点 4.随机森林的缺点 三、代码实现 1.完整代码 2.数据预处理 3.创建并训练模型 4.测试模型 总结 前言 随机森林是…

集合及数据结构第十二节(下)————哈希表、字符串常量池和练习题

系列文章目录 集合及数据结构第十二节&#xff08;下&#xff09;————哈希表、字符串常量池和练习题 哈希表、字符串常量池和练习题 哈希表的概念冲突-概念冲突-避免冲突-解决冲突严重时的解决办法冲突严重时的解决办法的实现性能分析和 java 类集的关系Hashmap的使用案…

R8RS标准之重要特性及用法实例(四十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

LDR6500Type-C pd OTGi协议芯片讲解

LDR6500是一款由乐得瑞科技推出的USB-C DRP&#xff08;Dual Role Port&#xff0c;双角色端口&#xff09;接口USB PD&#xff08;Power Delivery&#xff0c;功率传输&#xff09;通信芯片。这款芯片具备一系列先进的功能和特点&#xff0c;特别适合于手机音频转接器、USB Ty…

QT中引入SQLITE3数据库

1、把sqlite3.dll、.h、.lib这三个文件拷贝到工程目录下 2、在pro文件中配置一下即可 LIBS $$PWD/sqlite3.lib 3、保存一下pro文件 4、引入sqlite3.h头文件 5、验证 先新建一个文件夹data&#xff0c;若没有user.db&#xff0c;则会自动新建&#xff1b;有就直接使用 运行成…

UTONMOS:探索未来游戏的元宇宙纪元新篇章

元宇宙游戏&#xff0c;作为融合了虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;、区块链、人工智能&#xff08;AI&#xff09;等前沿技术的综合性数字世界&#xff0c;元宇宙游戏不仅重新定义了游戏的边界&#xff0c;更预示着一个沉浸式、交互性…

YOlOV5入门教程

前言 因项目需求&#xff0c;所以要使用yolo进行操作&#xff0c;现在对yolov5进行教程&#xff0c;代码可以在这下载&#xff1a;https://github.com/ultralytics/yolov5 项目结构 下载完成后可以看到资源如图所示。 1.1.github文件夹 ISSUE_TEMPLATE 目录 这个目录下的文件…

Cesium 展示——绘制水面动态升高

文章目录 需求分析需求 如图,绘制水面动态升高,作为洪水淹没的效果 分析 我们首先需要绘制一个面然后给这个面一个高度,在回调函数中进行动态设置值【这里有两种,一种是到达水面一定高度停止升高,一种是水面重新升高】/*** @description :洪水淹没* @author : Hukang*…

关闭IDEA启动画面

新版IDEA启动时启动画面居中且无法最小化&#xff0c;所以想把它给隐藏掉。&#xff08;此操作不会加快启动速度&#xff09; 在快捷方式后加入参数 nosplash&#xff0c;记得有个空格。

Java | Leetcode Java题解之第374题猜数字大小

题目&#xff1a; 题解&#xff1a; public class Solution extends GuessGame {public int guessNumber(int n) {int left 1, right n;while (left < right) { // 循环直至区间左右端点相同int mid left (right - left) / 2; // 防止计算时溢出if (guess(mid) < 0)…

CSV文件的高级处理:从大型文件处理到特殊字符管理

目录 一、处理大型CSV文件 1.1 面临的挑战 1.2 使用Pandas库 1.3 注意事项 二、跳过无效行 2.1 无效行的原因 2.2 使用异常处理机制 2.3 注意事项 三、处理特殊字符 3.1 特殊字符的问题 3.2 使用引号包围字段 3.3 使用库函数处理特殊字符 结论 CSV&#xff08;Com…

Web大学生网页作业成品——节日端午节介绍网页设计与实现(HTML+CSS)(5个页面)

&#x1f389;&#x1f389;&#x1f389; 常见网页设计作业题材有**汽车、环保、明星、文化、国家、抗疫、景点、人物、体育、植物、公益、图书、节日、游戏、商城、旅游、家乡、学校、电影、动漫、非遗、动物、个人、企业、美食、婚纱、其他**等网页设计题目, 可满足大学生网…

计算机网络面试真题总结(三)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ TCP 和 UDP 分别对应的常见应用层协议有哪些&#xff1f; TCP 对应…

帮助我们从曲线图中获取数据的软件分享——GetData Graph Digitizer

在科技论文写作和数据分析过程中&#xff0c;我们常常需要将自己的数据与前人的研究成果进行对比。然而&#xff0c;有时我们只能从别人的论文中获得一张包含坐标轴的曲线图&#xff0c;而无法直接获取原始数据。在这种情况下&#xff0c;GetData Graph Digitizer 软件就显得尤…

(24)(24.4) MultiWii/DJI/HDZero OSD (version 4.2 and later)(三)

文章目录 前言 3 显示端口OSD 前言 经过 WTF-OSD 修改的 HDZero、Walksnail 和 DJI 能够进行 DisplayPort 操作。 3 显示端口OSD DisplayPort 是一种 MSP 协议扩展&#xff0c;允许自动驾驶仪在兼容的外部操作系统上远程绘制文本。DisplayPort 是一种 MSP 协议扩展&#xf…

架构师篇-21、工作坊实战DDD分解业务

课程内容&#xff1a; 采用工作坊的教学模式共创主题一&#xff1a;DDD业务分析步骤共创主题二&#xff1a;DDD领域模型输出共创主题三&#xff1a;业务架构蓝图输出 收益&#xff1a; 如何采用DDD进行业务分解&#xff1f;【循序渐进不断实践】共创输出项目业务架构图及业务…

xtrabackup 用户权限

xtrabackup 用户权限 1.1、建用户及授权 The database user needs the following privileges on the tables/databases to be backed up: RELOAD and LOCK TABLES (unless the --no-lock option is specified) in order to FLUSH TABLES WITH READ LOCK and FLUSH ENGINE LO…

【C++】vector(上)

个人主页~ vector类 一、vector的介绍和使用1、vector的介绍2、vector的使用&#xff08;1&#xff09;vector的定义&#xff08;2&#xff09;vector iterator的使用&#xff08;3&#xff09;vector 空间增长&#xff08;4&#xff09;vector的增删查改&#xff08;5&#xf…