LeetCode 热题 100之 堆

1.数组中第k个最大元素

在这里插入图片描述
和Acwing 786 第k个数一模一样 排序

思路分析1:此题要求时间复杂度未为O(n)。虽然库函数sort和快速排序都能过,但是时间复杂度不满足条件。下面优化快速排序,写一个快速选择算法。我们可以引入随机化来加速这个过程,它的时间代价的期望是 O(n)。

  • 我们的目标是找到第 k 大的元素。根据索引的定义,第 k 大的元素在从小到大排序后的数组中对应的是第 n - k 小的元素(n 是数组长度)。
  • 调用 quickselect 函数,将 n - k 作为目标索引位置。
  • 辅助函数 quickselect:
    • 如果 l == r,表示区间只剩一个元素,直接返回该元素。
    • 将区间的第一个元素 nums[l] 设为基准值 partition,并初始化两个指针 i 和 j,分别从左向右和从右向左查找。
    • 使用双指针分区法:i 向右查找直到找到第一个大于或等于 partition 的元素,j 向左查找直到找到第一个小于或等于 partition 的元素。如果 i < j,交换 nums[i] 和 nums[j]。
    • 分区结束后,根据 k 的位置选择下一步要递归的区间:如果 k 在 j 的左侧,则递归左侧;否则,递归右侧。

具体实现代码(详解版):

class Solution {
public:// Quickselect函数,用于在区间 [l, r] 内找到第 k 小的元素int quickselect(vector<int> &nums, int l, int r, int k) {// 如果区间只剩一个元素,直接返回该元素if (l == r)return nums[k];// 选择区间的第一个元素作为基准值(pivot)int partition = nums[l];// 初始化左右指针,i 在 l 的前一位,j 在 r 的后一位int i = l - 1, j = r + 1;// 进行分区操作while (i < j) {// 从左到右找到第一个大于或等于基准值的元素do i++; while (nums[i] < partition);// 从右到左找到第一个小于或等于基准值的元素do j--; while (nums[j] > partition);// 如果 i 和 j 没有交叉,交换 nums[i] 和 nums[j]if (i < j)swap(nums[i], nums[j]);}// 现在 j 是分区位置的边界:所有小于基准值的元素在 j 左边,反之在右边// 根据 k 的位置选择继续搜索的区间if (k <= j) // 如果 k 位于左侧区间,则在左侧递归return quickselect(nums, l, j, k);else // 如果 k 位于右侧区间,则在右侧递归return quickselect(nums, j + 1, r, k);}// 主函数:找到数组中第 k 个最大的元素int findKthLargest(vector<int> &nums, int k) {int n = nums.size();// 第 k 大的元素在排序后是第 n - k 小的元素(索引从0开始)return quickselect(nums, 0, n - 1, n - k);}
  • 时间复杂度:
    • 平均情况下,quickselect 的时间复杂度是O(n),因为每次递归都有效缩小了查找区间。
    • 最坏情况下,时间复杂度为 O ( n 2 ) O(n^ 2 ) O(n2),可以通过随机选择基准值来减少最坏情况的发生概率。

思路分析2:利用最小堆(小顶堆)可以让我们高效地找到第 k 大的元素,且时间复杂度接近O(n)。

  • 使用小顶堆:维护一个大小为k的小顶堆。堆顶元素就是当前的第k大元素;
  • 构建堆:priority_queue<int, vector, greater> minHeap;创建一个小顶堆,优先队列默认是大顶堆,使用 greater 转成小顶堆
  • 更新堆
    • 从k+1个元素开始遍历数组
    • 如果当前元素比堆顶元素大,则替换堆顶元素为当前元素,并重新调整堆1。这保证堆中始终保持着当前最大的k个元素
  • 返回结果:遍历完数组后,堆顶元素就是数组中的第k大的元素

具体实现代码(详解版):

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {// 创建一个小顶堆,优先队列默认是大顶堆,使用 greater<int> 转成小顶堆priority_queue<int, vector<int>, greater<int>> minHeap;// 将前 k 个元素放入小顶堆for (int i = 0; i < k; ++i) {minHeap.push(nums[i]);}// 遍历剩余的元素for (int i = k; i < nums.size(); ++i) {if (nums[i] > minHeap.top()) { // 如果当前元素比堆顶元素大minHeap.pop(); // 移除堆顶元素minHeap.push(nums[i]); // 插入当前元素}}// 返回堆顶元素,即第 k 大的元素return minHeap.top();}
};

2.前k个高频元素

在这里插入图片描述
思路分析1:用排序算法对元素按照频率由高到低进行排序,然后再取前 k 个元素

  • 统计频率:使用哈希表统计每个元素的频率;
  • 转换成频率对:将哈希表中的数据转化为一组(元素,频率)的pair,便于排序;vector<pair<int, int>> freqPairs(freqMap.begin(), freqMap.end());
    -** 排序:使用标准排序算法将这些 pair 按照频率从高到低排序。其中cmp即排序规则可以直接使用lambda表达式
    sort(freqPairs.begin(), freqPairs.end(), [](const pair<int, int>& a, const pair<int, int>& b) { return a.second > b.second; });
  • 提取前 k 个元素:选择排序后的前 k 个元素作为结果。
    具体实现代码(详解版):
class Solution {
public:vector<int> topKFrequent(vector<int>& nums, int k) {// 1. 使用哈希表统计每个元素的频率unordered_map<int, int> freqMap;for (int num : nums) {freqMap[num]++;}// 2. 将哈希表转换成 (元素, 频率) 的 pair 列表vector<pair<int, int>> freqPairs(freqMap.begin(), freqMap.end());// 3. 使用标准排序算法对频率对进行排序,按频率从高到低sort(freqPairs.begin(), freqPairs.end(), [](const pair<int, int>& a, const pair<int, int>& b) {return a.second > b.second;});// 4. 选择前 k 个元素vector<int> result;for (int i = 0; i < k; ++i) {result.push_back(freqPairs[i].first);}return result;}
};
  • 时间复杂度:总体时间复杂度为 O(n+mlogm),在最坏情况下为 O(nlogn)。
  • 空间复杂度:O(m+k)

思路分析2:可以使用桶排序来解决这个问题,通过将出现频率相同的元素分组到对应的桶中,再按照频率从高到低提取前 k 个元素。

  • 统计频率:使用 unordered_map 统计每个元素的出现频率;
  • 创建桶:创建一个数组(桶)buckets,其中第 i 个桶存储出现频率为 i 的所有元素。数组大小设置为 nums.size() + 1,因为数组中某个元素的最大可能频率不会超过数组的长度。
  • 填充桶:根据每个元素的频率,将其加入到对应频率的桶中。
  • 按频率从高到低提取元素:从频率最高的桶(即 buckets 的末尾)向前遍历,收集桶中的元素,直到收集了 k 个元素为止。

具体实现代码(详解版):

class Solution {
public:vector<int> topKFrequent(vector<int>& nums, int k) {// 1. 统计每个元素的频率unordered_map<int, int> freqMap;for (int num : nums) {freqMap[num]++;}// 2. 创建桶,桶的索引是频率,桶里存的是具有该频率的所有元素int n = nums.size();vector<vector<int>> buckets(n + 1); // 每个频率可能出现的次数范围是 0 到 nfor (auto& [num, freq] : freqMap) {buckets[freq].push_back(num);}// 3. 按频率从高到低收集前 k 个高频元素vector<int> result;for (int i = n; i >= 0 && result.size() < k; --i) {for (int num : buckets[i]) {result.push_back(num);if (result.size() == k) {break;}}}return result;}
};
  • 时间复杂度:O(n);
  • 空间复杂度:O(n)

3.数据流的中位数

在这里插入图片描述
思路分析:为了实现一个能够动态获取中位数的数据结构 MedianFinder,可以利用两个堆(优先队列)来高效地维护中位数

  • 维护两个堆:使用一个大顶堆和一个小顶堆
    • 大顶堆:用于存储数据流中较小的一般数字,堆顶为较小部分的最大值;
    • 小顶堆:用于存储数据流中较大的一半数字;
  • 中位数的计算
    • 如果数据流的总长度为奇数,maxHeap的堆顶就是中位数;
    • 如果数据流的总长度为偶数,中位数是两个堆顶元素的平均值
  • 调整堆的平衡
    • 每次添加新数字时,将数字插入 maxHeap 或 minHeap 之一,并根据堆的大小调整两者的平衡,以确保 maxHeap 和 minHeap 的元素数量差最多为 1。
    • 如果 maxHeap 的大小大于 minHeap 的大小超过 1,将 maxHeap 堆顶元素移动到 minHeap。
    • 如果 minHeap 的大小大于 maxHeap,将 minHeap 堆顶元素移动到 maxHeap。

具体实现代码(详解版):

class MedianFinder {
private:priority_queue<int> maxHeap; // 大顶堆priority_queue<int, vector<int>, greater<int>> minHeap; // 小顶堆public:// 初始化MedianFinder() {}// 添加元素void addNum(int num) {// 先添加到大顶堆maxHeap.push(num);// 调整大小:如果 maxHeap 堆顶元素大于 minHeap 堆顶,将它移动到 minHeapif (!minHeap.empty() && maxHeap.top() > minHeap.top()) {minHeap.push(maxHeap.top());maxHeap.pop();}// 平衡大小:确保 maxHeap 的元素数量不小于 minHeapif (maxHeap.size() > minHeap.size() + 1) {minHeap.push(maxHeap.top());maxHeap.pop();} else if (minHeap.size() > maxHeap.size()) {maxHeap.push(minHeap.top());minHeap.pop();}}// 返回中位数double findMedian() {// 如果元素总数是奇数,返回 maxHeap 堆顶if (maxHeap.size() > minHeap.size()) {return maxHeap.top();}// 如果是偶数,返回两个堆顶的平均值return (maxHeap.top() + minHeap.top()) / 2.0;}
};
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

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

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

相关文章

使用SpringBoot+Vue+Echarts制作一个文章贡献度表

使用SpringBootVueEcharts制作一个文章贡献度表 制作博客贡献表 使用了ECharts中的 calendar-effectscatter 组件制作贡献表&#xff1a;点我传送 首先附上完整的vue代码&#xff1a; <template><div id"container" style" width: 100%; height: 30…

使用Matlab建立决策树

综述 除了神经网络模型以外&#xff0c;树模型及基于树的集成学习模型是较为常用的效果较好的预测模型。我们以下先构建一个决策树模型。 决策树算法的优点如下&#xff1a;1、 决策树易于理解和实现&#xff0c;用户在学习过程中不需要了解过多的背景知识&#xff0c;其能够…

LangGPT结构化提示词编写实践

基础任务 如果直接询问大模型strawberry有几个r&#xff0c;大模型会给出错误的答案&#xff1a; 这里我们引入思维连Chain of Thought&#xff0c;我们让大模型遍历一遍单词&#xff0c;每次累加得到最终结果 之前怎么都做不对的题&#xff0c;让大模型一步一步思考&#xf…

开源ISP(Infinite-ISP)介绍

ISP&#xff08;Image Signal Processor&#xff09;我介绍了很多了&#xff0c;大家可以先看下面的文章&#xff0c;了解基本概念&#xff1a; ISP算法及架构分析介绍 谈谈FPGA工程师如何做ISP 图像信号处理器和 Infinite-ISP ISP从图像传感器获取 RAW 像素&#xff0c;并将其…

如何在c++侧编译运行一个aclnn(AOL)算子?

1 AOL算子库 CANN&#xff08;Compute Architecture for Neural Networks&#xff09;提供了算子加速库&#xff08;Ascend Operator Library&#xff0c;简称AOL&#xff09;。该库提供了一系列丰富且深度优化过的高性能算子API&#xff0c;更亲和昇腾AI处理器&#xff0c;调…

三分钟学会Docker基本操作,快速入门容器技术!

如果您时常遭遇以下困境&#xff1a; 被繁琐的应用安装依赖与环境配置耗尽了宝贵时间与精力&#xff1f; 即便严格遵循安装指南&#xff0c;仍频遇障碍&#xff0c;导致应用无法启动&#xff0c;让您倍感挫败与焦虑&#xff1f; 向研发团队反馈安装难题&#xff0c;却只换来“…

快速入门Zookeeper

Zookeeper ZooKeeper作为一个强大的开源分布式协调服务&#xff0c;扮演着分布式系统中至关重要的角色。它提供了一个中心化的服务&#xff0c;用于维护配置信息、命名、提供分布式同步以及提供组服务等。通过其高性能和可靠的特性&#xff0c;ZooKeeper能够确保在复杂的分布式…

uniapp—android原生插件开发(3Android真机调试)

本篇文章从实战角度出发&#xff0c;将UniApp集成新大陆PDA设备RFID的全过程分为四部曲&#xff0c;涵盖环境搭建、插件开发、AAR打包、项目引入和功能调试。通过这份教程&#xff0c;轻松应对安卓原生插件开发与打包需求&#xff01; 一、打包uniapp资源包&#xff1a; 打包…

Windows 11开发环境配置与应用开发

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 Windows 11是微软发布的新一代操作系统&#xff0c;它不仅在视觉和用户体验上进行了革新&#xff0c;还为开发者提供了更…

停车共享小程序ssm+论文源码调试讲解

2 系统关键技术 2.1 微信小程序 微信小程序&#xff0c;简称小程序&#xff0c;英文名Mini Program&#xff0c;是一种全新的连接用户与服务的方式&#xff0c;可以快速访问、快速传播&#xff0c;并具有良好的使用体验。 小程序的主要开发语言是JavaScript&#xff0c;它与普…

【MRAN】情感分析中情态缺失问题的多模态重构和对齐网络

abstract 多模态情感分析&#xff08;MSA&#xff09;旨在通过文本、视觉和声音线索识别情感类别。然而&#xff0c;在现实生活中&#xff0c;由于各种原因&#xff0c;可能会缺少一到两种模式。当文本情态缺失时&#xff0c;由于文本情态比视觉和听觉情态包含更多的语义信息&…

通过 Windows IIS 服务访问腾讯云 CFS 文件系统

互联网信息服务&#xff08;IIS&#xff09;可以像访问本地数据一样访问文件存储&#xff08;Cloud File Storage&#xff0c;CFS&#xff09;系统上的数据&#xff0c;并提供 Web 服务&#xff0c;实现网站存储与计算分离。本文介绍如何配置 IIS 访问 CFS 文件系统。 背景信息…

L7.【LeetCode笔记】相交链表

1.题目 . - 力扣&#xff08;LeetCode&#xff09; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结…

Java开发插件:JRebel热部署(最佳实践+激活方式)

使用场景&#xff1a; 在庞大的项目&#xff0c;我们启动项目的时间较长&#xff0c;尤其每次修改完代码要进行测试&#xff0c;就要重新编译启动项目&#xff0c;耗时且繁琐&#xff0c;热部署插件通过设置更新操作&#xff0c;就可以实现快速启动项目&#xff0c;开发效率显…

2024Python安装与配置IDE汉化集活的全套教程

【一】Python解释器下载【运行环境】 【1】Python官网 包含编程资料、学习路线图、源代码、软件安装包等&#xff01;【[点击这里]】&#xff01; [https://www.python.org]&#xff08;官网进不去的可以点击点击领取&#xff0c;100%免费&#xff01;安装包&#xff09; 【2…

【OD-支持在线评测】数字涂色(100分)

📎 在线评测链接 https://app5938.acapp.acwing.com.cn/contest/11/problem/OD1081 🍓 OJ题目截图 🍿 最新机试E卷,全、新、准,题目覆盖率达 95% 以上,支持题目在线评测,专栏文章质量平均 94 分 🌍 评测功能需要 ⇒ 订阅专栏 ⇐ 后私信联系解锁~ 文章目录 📎…

JAVA学习接口案例实例

要求&#xff1a; 结果&#xff1a; 测试类&#xff1a; package Z; public class Test {public static void main(String[] args) {ClassMnger p new ClassMnger();p.Students();p.Studentall();p.studentavg();} } 实体数据类 public class ClassAll {//存入班级全部学…

远程连接服务器

1、远程连接服务器 1.1 远程连接服务器------通过文字或图形接口方式来远程登录系统&#xff0c;让你在远程终端前登录linux主机以取得可操 作主机接口&#xff08;shell&#xff09;&#xff0c;而登录后的操作感觉就像是坐在系统前面一样。 1.2 功能------分享主机的运算能…

1分钟教你利用ai工具免费制作10W+情感视频,自动化批量操作,效率提升10倍!

觉得风之馨的文章对你有用的话&#xff0c;记得点赞、关注加星标哦&#xff01; 今天刷到这种人生感悟号,很容易唤起大家的共鸣。转眼间一年即将过去,摸摸口袋没剩下几个钱。内心突然间就伤感起来了&#xff0c;生活不易&#xff0c;且行且珍惜。 评论出大神,有出来拉仇恨的&a…

CISCO产品介绍

思科防火墙是由全球领先的网络解决方案提供商思科&#xff08;Cisco&#xff09;公司研发和生产的一系列网络安全设备。 思科的产品和服务涵盖了多个领域&#xff0c;包括但不限于&#xff1a; 网络硬件&#xff1a;思科的路由器和交换机是其核心产品&#xff0c;广泛应用于企…