LeetCode 热题 100 堆

news2025/4/12 12:03:57

215. 数组中的第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 <= 10^5
  • -10^4 <= nums[i] <= 10^4

借助快速排序的思想解决topK问题,快速选择将数组划分为三块,对应三种情况进行查找,不断递归即可:

[left, l - 1] [l, r] [r + 1, right]
  • 当k小于等于右区间的长度,既k <= right - r时,继续在右区间查找第k大的数。
  • 当k小于等于右区间+中间相同字符的长度,既k <= right - l + 1时,第k大的数就在中间区间为key
  • 当k大于右区间+中间相同字符的长度时,既k在左区间时,在左区间查找第k大的数减去右区间+中间相同字符的长度,既第k - (right - l + 1)大的数。
int findKthLargest(vector<int>& nums, int k) {
    srand(time(nullptr));
    int left = 0, right = nums.size() - 1;
    return topK(nums, left, right, k);
}

int topK(vector<int>& nums, int left, int right, int k) {
    if (left == right)
        return nums[left];

    // 随机获取key
    int key = nums[rand() % (right - left + 1) + left];

    // 数组划分为三块
    int i = left, l = left, r = right;
    while (i <= r) {
        if (nums[i] < key)
            swap(nums[l++], nums[i++]);
        else if (nums[i] > key)
            swap(nums[r--], nums[i]);
        else
            i++;
    }

    // [left, l - 1] [l, r] [r + 1, right]
    if (k <= right - r)
        return topK(nums, r + 1, right, k);
    else if (k <= right - l + 1)
        return key;
    else
        return topK(nums, left, l - 1, k - (right - l + 1));
}

快速排序的思想解决topK问题,快速选择将数组划分为三块,对应三种情况进行查找,不断递归即可:

[left, l - 1] [l, r] [r + 1, right]
  • 当k小于等于右区间的长度,既k <= right - r时,继续在右区间查找第k大的数。
  • 当k小于等于右区间+中间相同字符的长度,既k <= right - l + 1时,第k大的数就在中间区间为key
  • 当k大于右区间+中间相同字符的长度时,既k在左区间时,在左区间查找第k大的数减去右区间+中间相同字符的长度,既第k - (right - l + 1)大的数。
int findKthLargest(vector<int>& nums, int k) {
    srand(time(nullptr));
    int left = 0, right = nums.size() - 1;
    return topK(nums, left, right, k);
}

int topK(vector<int>& nums, int left, int right, int k) {
    if (left == right)
        return nums[left];

    // 随机获取key
    int key = nums[rand() % (right - left + 1) + left];

    // 数组划分为三块
    int i = left, l = left, r = right;
    while (i <= r) {
        if (nums[i] < key)
            swap(nums[l++], nums[i++]);
        else if (nums[i] > key)
            swap(nums[r--], nums[i]);
        else
            i++;
    }

    // [left, l - 1] [l, r] [r + 1, right]
    if (k <= right - r)
        return topK(nums, r + 1, right, k);
    else if (k <= right - l + 1)
        return key;
    else
        return topK(nums, left, l - 1, k - (right - l + 1));
}

347. 前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

**进阶:**你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

优先级队列 + 哈希

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> hash;
    for (int i : nums) {
        hash[i]++;
    }

    auto cmp = [](pair<int, int>& a, pair<int, int>& b) {
        return a.second > b.second;
    };
    // 小根堆
    priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> que;
    for (auto& [num, count] : hash) {
        que.emplace(num, count);
        if (que.size() > k) {
            que.pop();
        }
    }
    vector<int> ans;
    while (!que.empty()) {
        ans.push_back(que.top().first);
        que.pop();
    }
    return ans;
}

快速排序

void quickSort(vector<pair<int, int>>& v, int left, int right, int k) {
    if (left >= right) {
        return;
    }
    int key = v[rand() % (right - left + 1) + left].second;
    int i = left, l = left, r = right;
    while (i <= r) {
        if (v[i].second < key) {
            swap(v[i++], v[l++]);
        } else if (v[i].second > key) {
            swap(v[i], v[r--]);
        } else {
            i++;
        }
    }

    // [left, l - 1] [l, r] [r + 1, right]
    if (k <= right - r) {
        quickSort(v, r + 1, right, k);
    } else if (k <= right - l + 1) {
        return;
    } else {
        quickSort(v, left, l - 1, k - (right - l + 1));
    }
}

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> hash;
    for (int i : nums) {
        hash[i]++;
    }

    vector<pair<int, int>> v;
    for (auto& p : hash) {
        v.push_back(p);
    }

    srand(time(nullptr));
    quickSort(v, 0, v.size() - 1, k);

    vector<int> ans;
    int i = v.size() -  1;
    while (k--) {
        ans.push_back(v[i--].first);
    }
    return ans;
}

295. 数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。
  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

提示:

  • -10^5 <= num <= 10^5
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 10^4 次调用 addNumfindMedian
  • que_min 是大根堆,用于存储数据流中较小的一半元素,堆顶元素是较小的一半元素中最大的元素。
  • que_max 是小根堆,用于存储数据流中较大的一半元素,堆顶元素是较大的一半元素中最小的元素。
priority_queue<int, vector<int>, less<int>> que_min; // 大根堆
priority_queue<int, vector<int>, greater<int>> que_max; // 小根堆

MedianFinder() {}

void addNum(int num) {
    if (que_min.empty() || num <= que_min.top()) {
        que_min.push(num);
        if (que_min.size() > que_max.size() + 1) {
            que_max.push(que_min.top());
            que_min.pop();
        }
    } else {
        que_max.push(num);
        if (que_max.size() > que_min.size()) {
            que_min.push(que_max.top());
            que_max.pop();
        }
    }
}

double findMedian() {
    if (que_min.size() > que_max.size())
        return que_min.top();
    return (que_min.top() + que_max.top()) / 2.0;
}

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

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

相关文章

Redis(笔记)

简介&#xff1a; 常用数据类型: 常用操作命令&#xff1a; Redis的Java客户端&#xff1a; 操作字符串类型的数据&#xff1a; 操作Hash类型的数据&#xff1a; 操作列表类型的数据&#xff1a; 操作集合类型的数据&#xff1a; 操作有序集合类型数据&#xff1a; 通用命令…

bootloader+APP中,有些APP引脚无法正常使用?

问&#xff1a;bootloaderAPP程序中&#xff0c;为什么有些APP引脚无法正常使用&#xff1f;无法设置高低电平 主控芯片GD32F415&#xff0c;参考案例bootloader中的引脚使用&#xff1a; 参考案例APP程序的引脚使用&#xff1a; 以及个人使用的无线模组&#xff0c;高电平使能…

高并发内存池:原理、设计与多线程性能优化实践

高并发内存池是一种专门为多线程环境设计的内存管理机制&#xff0c;其核心目标是通过优化内存分配和释放过程&#xff0c;解决传统内存分配器&#xff08;如malloc/free&#xff09;在高并发场景下的性能瓶颈&#xff0c;显著提升多线程程序的内存访问效率。 目录 一、核心设计…

基于内容的课程推荐网站的设计与实现00(SSM+htmlL)

基于内容的课程推荐网站的设计与实现(SSMhtml) 该系统是一个基于内容的课程推荐网站&#xff0c;旨在为用户提供个性化的课程推荐。系统包含多个模块&#xff0c;如教学视频、教学案例、课程信息、系统公告、个人中心和后台管理。用户可以通过首页访问不同的课程分类&#xff…

生活电子常识--删除谷歌浏览器搜索记录

前言 谷歌浏览器会记录浏览器历史搜索,如果不希望看到越来越多的搜索记录,可以如下设置 解决 设置-隐私-自动填充表单 这个和浏览器记录的密码没有关系,可以放心删除

学习threejs,使用Texture纹理贴图,测试repeat重复纹理贴图

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️Texture 纹理贴图1.1.1 ☘️…

蓝桥杯基础算法-字符串与集合

对集合的考察集中在集合的特性和功能。 set-唯一性 list-有序性 集合元素的个数 思路分析&#xff1a;set的唯一性&#xff0c;取出重复的子串 eg&#xff1a; 下标0截取的范围&#xff1a;【0&#xff0c;最大下标】 下标1截取的范围&#xff1a;【1&#xff0c;最大下标…

animals_classification动物分类

数据获取 深度学习训练中第一个是获取数据集&#xff0c;数据集的质量很重要&#xff0c;我们这里做的是动物分类&#xff0c;大致会选择几个动物&#xff0c;来做一个简单的多分类问题&#xff0c;数据获取的方法&#xff0c;鼠鼠我这里选择使用爬虫的方式来对数据进行爬取&a…

解决Oracle PL/SQL中“表或视图不存在“错误的完整指南

解决Oracle PL/SQL中"表或视图不存在"错误的完整指南 前言问题概述根本原因分析一、 编译时与运行时验证差异二、权限问题三、 Schema命名问题 实际案例演示案例1&#xff1a;动态分表查询案例2&#xff1a;权限不足的场景 实用排查步骤排查流程图最佳实践建议解决方…

SSH远程连接服务器(cursor)

安装Remote-SSH插件 Cursor是基于VSCode的&#xff0c;因此支持VSCode的Remote-SSH功能。打开Cursor&#xff0c;进入扩展市场&#xff08;左侧活动栏的“Extensions”图标&#xff09;。搜索“Remote - SSH”插件并安装&#xff08;由Microsoft提供&#xff09;。 配置SSH 在…

idea gitlab 操作

1.拉取脚本 账号登录 就可以获取git代码 2. 版本回退 hard暴力回退到暂存区 缓存区消失 3.版本合并 切换到目标分区 选择点击开发分区 进行合并

【MATLAB第113期】基于MATLAB的EFAST扩展傅里叶幅度敏感性分析方法(有目标函数)

【MATLAB第113期】基于MATLAB的EFAST扩展傅里叶幅度敏感性分析方法&#xff08;有目标函数&#xff09; 一、方法概述 扩展傅里叶幅度敏感性检验&#xff08;EFAST&#xff09;是一种基于频域分析的全局敏感性分析方法&#xff0c;能够同时评估模型参数的一阶敏感性&#xff…

Unity3D开发AI桌面精灵/宠物系列 【三】 语音识别 ASR 技术、语音转文本多平台 - 支持科大讯飞、百度等 C# 开发

Unity3D 交互式AI桌面宠物开发系列【三】ASR 语音识别 该系列主要介绍怎么制作AI桌面宠物的流程&#xff0c;我会从项目开始创建初期到最终可以和AI宠物进行交互为止&#xff0c;项目已经开发完成&#xff0c;我会仔细梳理一下流程&#xff0c;分步讲解。 这篇文章主要讲有关于…

Qt -信号与槽

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【暂无】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 目录 前言引入connect调用链模板类型的connectQObject::connectImplQObjectPrivate::connectImpl qobject_p_p.hconnect作用总结ai对信号与槽的模拟实现 前言 面向对象&am…

Django中使用不同种类缓存的完整案例

Django中使用不同种类缓存的完整案例 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 Django中使用不同种类缓存的完整案例步骤1:设置Django项目步骤2:设置URL路由步骤3:视图级别…

解锁健康密码,拥抱品质生活

在生活节奏不断加快的今天&#xff0c;健康养生已成为人们关注的焦点。它不仅关乎当下生活质量&#xff0c;更是对未来幸福的投资。从日常生活的点滴出发&#xff0c;掌握正确养生方法&#xff0c;我们就能轻松收获健康。​ 饮食是健康的基石。我们应当遵循 “食物多样&#x…

ABAP 新语法 - corresponding

在 ABAP 中&#xff0c;CORRESPONDING 操作符用于根据字段名称自动映射结构体&#xff08;Structure&#xff09;或内表&#xff08;Internal Table&#xff09;的字段值。它比传统的 MOVE-CORRESPONDING 语句更灵活&#xff0c;支持更多控制选项。 基础用法 data: begin of …

HTML零基础入门笔记:狂神版

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】HTML5完整教学通俗易懂_哔哩哔哩_bilibili 第1-2章&#xff1a;Java零基础入门笔记&#xff1a;(1-2)入门&#xff08;简介、基础知识&#xff09;-CSDN博客 第3章&…

FreeRTOS移植笔记:让操作系统在你的硬件上跑起来

一、为什么需要移植&#xff1f; FreeRTOS就像一套"操作系统积木"&#xff0c;但不同硬件平台&#xff08;如STM32、ESP32、AVR等&#xff09;的CPU架构和外设差异大&#xff0c;需要针对目标硬件做适配配置。移植工作就是让FreeRTOS能正确管理你的硬件资源。 二、…

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第五式】动态内存管理

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第五式】动态内存管理 【心法】 【第零章】c语言概述 【第一章】分支与循环语句 【第二章】函数 【第三章】数组 【第四章】操作符 【第五章】指针 【第六章】结构体 【第七章】const与c语言中一些错误代码 【禁忌秘术】 【第一式…