当前位置: 首页 > article >正文

通过第k个最大元素深入浅出快排和堆排序

快排和堆排序在确定k个元素有着得天独厚的优势,原因是无论快排还是堆排序在每一轮排序中均可以确定一个元素

  • 快排:每一轮排序均可以确定一个元素位置
  • 堆排序:每一轮排序都可以确定一个最小值或最大值

他们的时间复杂度都是O(nlogk),但是快排的空间复杂度是O(logn),而堆排序的空间复杂度是O(k)。

所以便想着带着大家通过数组中的第K个最大元素来详细的学习一下如何将这两种思路去落地和应用

题目:《数组中的第K个最大元素》
题目描述:

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

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

题目要求我们用O(n)的时间复杂度实现,但是我们可以先不用考虑时间复杂度,只单纯的透过该题目去快速上手这两种方法

首先,你要对这两个排序有一定理解,为了节约篇幅突出重点,大家先通过下面这两篇文章了解一下:

  • 《快速排序》
  • 《堆排序》

作为程序员的自我修养,请大家务必都以上两种排序的过程和代码的实现烂熟于心

1.快排思路

这道题目的核心就是找排序后的倒数第 [latex] 个位置,一般看到这种需要第N个的数据往快排思路上去思考一般方向不会出错。

现在回到题目,因为把整个数组排序是没有必要的,因为我们的目的并不是排序,而是找到length-k索引位,所以在快排的每一轮的排序中,利用它可以确定一个元素的位置就可以很快找到第k个最大元素。

核心代码如下:

    private int partition(int[] nums, int begin, int end) {int pivot = nums[begin];while (begin < end) {while (begin < end&& nums[end] >= pivot) {end--;}nums[begin] = nums[end];while (begin < end&& nums[begin] <= pivot) {begin++;}nums[end] = nums[begin];}nums[begin] = pivot;return begin;}

这面这段代码是快排的核心代码,它的作用是在每一轮排序中确定一个元素的位置,然后返回这个元素的位置

一旦partition方法返回的值和length-k相等,那么就可以直接返回这个元素了,因为这个元素就是第k个最大元素。

核心代码如下:

    public int findKthLargest(int[] nums, int k) {quickSelect(nums, 0, nums.length - 1, nums.length - k);return nums[nums.length - k];}private void quickSelect(int[] nums, int left, int right, int index) {if (left < right) {int partitionIndex = partition(nums, left, right);if (partitionIndex == index) {// 找到了目标位置,无需进一步操作return;} else if (partitionIndex < index) {// 目标位于右侧子数组quickSelect(nums, partitionIndex + 1, right, index);} else {// 目标位于左侧子数组quickSelect(nums, left, partitionIndex - 1, index);}}}private int partition(int[] nums, int begin, int end) {int pivot = nums[begin];while (begin < end) {while (begin < end&& nums[end] >= pivot) {end--;}nums[begin] = nums[end];while (begin < end&& nums[begin] <= pivot) {begin++;}nums[end] = nums[begin];}nums[begin] = pivot;return begin;}

2.堆排序思路

大顶堆的话是每一次调整都可以确定一个最大值,所以我们可以通过构建一个大顶堆,然后不断的调整堆,直到调整到第k次,那么这个堆顶元素就是第k个最大元素。

当然Java中我们可以使用优先队列实现堆的逻辑,但是并不推荐,原因是可以通过考察如何建堆以及如何调整堆来判断候选人的基本素养。

  • 调整堆
   private void buildMaxHeap(int[] nums, int length) {int i = nums.length / 2 - 1;while (i >= 0) {heapify(nums, i--, length);}}private void heapify(int[] nums, int i, int len) {//找到左右子树int left = i * 2 + 1;int right = i * 2 + 2;int largeIndex = i;if (left < len&& nums[left] > nums[largeIndex]) {largeIndex = left;}if (right < len&& nums[right] > nums[largeIndex]) {largeIndex = right;}if (largeIndex != i) {//交换int t = nums[i];nums[i] = nums[largeIndex];nums[largeIndex] = t;//交换之后子节点值改变需要再次调整heapify(nums, largeIndex, len);}}

上面这个方法是调整堆的方法,它的作用是将一个堆调整为大顶堆。每一次调整均可以确定一个最大值

所以我们只需在调整k次后就会找到第k个最大元素。
核心代码如下:

 private int heapSelect(int[] nums, int k) {//构建大根堆int len = nums.length;buildMaxHeap(nums, len);k--;if (k == 0) {return nums[0];}for (int i = len - 1; i >= 0; i--) {//交换根节点和尾结点元素,并让数组大小减一int t = nums[0];nums[0] = nums[len - 1];nums[len - 1] = t;heapify(nums, 0, --len);k--;if (k == 0) {return nums[0];}}return nums[0];}

完整代码如下:

    public int findKthLargest1(int[] nums, int k) {return heapSelect(nums, k);}private int heapSelect(int[] nums, int k) {//构建大根堆int len = nums.length;buildMaxHeap(nums, len);k--;if (k == 0) {return nums[0];}for (int i = len - 1; i >= 0; i--) {//交换根节点和尾结点元素,并让数组大小减一int t = nums[0];nums[0] = nums[len - 1];nums[len - 1] = t;heapify(nums, 0, --len);k--;if (k == 0) {return nums[0];}}return nums[0];}private void buildMaxHeap(int[] nums, int length) {int i = nums.length / 2 - 1;while (i >= 0) {heapify(nums, i--, length);}}private void heapify(int[] nums, int i, int len) {//找到左右子树int left = i * 2 + 1;int right = i * 2 + 2;int largeIndex = i;if (left < len&& nums[left] > nums[largeIndex]) {largeIndex = left;}if (right < len&& nums[right] > nums[largeIndex]) {largeIndex = right;}if (largeIndex != i) {//交换int t = nums[i];nums[i] = nums[largeIndex];nums[largeIndex] = t;//交换之后子节点值改变需要再次调整heapify(nums, largeIndex, len);}}

3.实际应用场景

大数据处理:在海量数据中查找第K大的元素

排行榜系统:获取用户排名

数据统计:获取数据分布的中位数或其他分位数

相关文章:

通过第k个最大元素深入浅出快排和堆排序

快排和堆排序在确定k个元素有着得天独厚的优势&#xff0c;原因是无论快排还是堆排序在每一轮排序中均可以确定一个元素 快排&#xff1a;每一轮排序均可以确定一个元素位置堆排序&#xff1a;每一轮排序都可以确定一个最小值或最大值 他们的时间复杂度都是O(nlogk)&#xff…...

NVR接入录像回放平台EasyCVR视频系统守护舌尖上的安全,打造“明厨亮灶”云监管平台

一、方案背景 近年来&#xff0c;餐饮行业食品安全和卫生等问题频发&#xff0c;比如后厨卫生脏乱差等&#xff0c;持续引发关注&#xff0c;这些事情导致连锁反应&#xff0c;使其收益遭受损失。同时&#xff0c;给消费者造成了心理和生理上的伤害。 加强餐饮行业的监管成为…...

Airflow+Spark/Flink vs. Kettle

在迁移亿级&#xff08;单表超过1.3亿&#xff09;结构化数据&#xff08;达梦→星环&#xff09;的场景下&#xff0c;Airflow&#xff08;结合分布式计算框架&#xff09;的综合效果优于Kettle&#xff0c;以下是详细对比与方案建议&#xff1a; 一、核心对比&#xff1a;Air…...

Cribl 导入文件来检查pipeline 的设定规则(eval 等)

Cribl 导入文件来检查pipeline 的设定规则(eval 等) 从这个页面先下载,或者copy 内容来创建pipeline: Reducing Windows XML Events | Cribl Docs...

[C++面试] new、delete相关面试点

一、入门 1、说说new与malloc的基本用途 int* p1 (int*)malloc(sizeof(int)); // C风格 int* p2 new int(10); // C风格&#xff0c;初始化为10 new 是 C 中的运算符&#xff0c;用于在堆上动态分配内存并调用对象的构造函数&#xff0c;会自动计算所需内存…...

一周学会Pandas2 Python数据处理与分析-Jupyter Notebook安装

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili Jupyter (Project Jupyter | Home&#xff09;项目是一个非营利性开源项目&#xff0c;于2014年由IPython项目中诞生…...

第30周Java分布式入门 消息队列 RabbitMQ

RabbitMQ章节介绍 一、RabbitMQ概述 RabbitMQ学习内容: 本章节将学习RabbitMQ的概念、安装启动、管理后台、代码实操、交换机工作模式以及Spring Boot整合RabbitMQ。消息队列定义: 消息队列是一种用于在分布式系统中传递消息的机制。消息队列特性: 消息队列具有异步、解耦、削…...

北斗导航 | THE GNSS AMBIGUITY RATIO-TEST REVISITED: A BETTER WAY OF USING IT【论文要点】

THE GNSS AMBIGUITY RATIO-TEST REVISITED: A BETTER WAY OF USING IT 总结该论文的核心贡献及关键方法如下:论文核心内容概述 传统比率测试的局限性 传统比率测试通过比较最优与次优模糊度解的残差平方和比值(即 R = q (...

MySQL 面试知识点详解(索引、存储引擎、事务与隔离级别、MVCC、锁机制、优化)

一、索引基础概念 1 索引是什么&#xff1f; 定义&#xff1a;索引是帮助MySQL高效获取数据的有序数据结构&#xff0c;类似书籍的目录。核心作用&#xff1a;减少磁盘I/O次数&#xff0c;提升查询速度&#xff08;以空间换时间&#xff09;。 2 索引的优缺点 优点缺点加速…...

Linux / Windows 下 Mamba / Vim / Vmamba 安装教程及安装包索引

目录 背景0. 前期环境查询/需求分析1. Linux 平台1.1 Mamba1.2 Vim1.3 Vmamba 2. Windows 平台2.1 Mamba2.1.1 Mamba 12.1.2 Mamba 2- 治标不治本- 终极版- 高算力版 2.2 Vim- 治标不治本- 终极版- 高算力版 2.3 Vmamba- 治标不治本- 终极版- 高算力版 3. Linux / Windows 双平…...

deepseek v3-0324 Markdown 编辑器 HTML

Markdown 编辑器 HTML 以下是一个美观的 Markdown 编辑器 HTML 页面&#xff0c;支持多种主题切换和实时预览功能&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&q…...

视频设备轨迹回放平台EasyCVR如何搭建公共娱乐场所远程视频监控系统

一、背景介绍 由于KTV、酒吧、足疗店等服务场所人员流动频繁、环境复杂&#xff0c;一直是治安管理的重点区域。为有效打击 “黄赌毒”、打架斗殴、寻衅滋事等违法犯罪的活动&#xff0c;打造安全有序的娱乐消费环境&#xff0c;我国相关部门将加大对这类场所的清查与管控力度…...

网络安全基础知识总结

什么是网络安全 采取必要措施&#xff0c;来防范对网络的攻击&#xff0c;侵入&#xff0c;干扰&#xff0c;破坏和非法使用&#xff0c;以及防范一些意外事故&#xff0c;使得网络处于稳定可靠运行的状态&#xff0c;保障网络数据的完整性、保密性、可用性的能力(CIA)。 举例…...

Python设计模式:克隆模式

1. 什么是克隆模式 克隆模式的核心思想是通过复制一个已有的对象&#xff08;原型&#xff09;来创建一个新的对象&#xff08;克隆&#xff09;。这种方式可以避免重复的初始化过程&#xff0c;从而提高效率。克隆模式通常涉及以下几个方面&#xff1a; 原型对象&#xff1a…...

【工具】在 Visual Studio 中使用 Dotfuscator 对“C# 类库(DLL)或应用程序(EXE)”进行混淆

在 Visual Studio 中使用 Dotfuscator 进行混淆 Dotfuscator 是 Visual Studio 自带的混淆工具&#xff08;Dotfuscator Community Edition&#xff0c;简称 CE&#xff09;。它可以混淆 C# 类库&#xff08;DLL&#xff09;或应用程序&#xff08;EXE&#xff09;&#xff0c…...

积分赛——获取环境温度

设计要求 从DS18B20温度传感器上获取环境温度&#xff0c;并将其温度值显示到数码管上&#xff08;保留两位小数&#xff09;。 当“S4”定义为发送按键&#xff0c;按键S4按下时&#xff0c;串口向PC端发送当前采集的温度值&#xff1b; 串口发送格式&#xff1a; Temp:26.…...

LogicFlow获取锚点数据的自定义key并添加的连接的Edge边数据中

1、重写 PolylineEdgeModel 类&#xff08;其它 EdgeModel 都可以&#xff09; class CustomNetWorkNodeEdge extends PolylineEdge { } class CustomNetWorkNodeEdgeModel extends PolylineEdgeModel {getData() {const data super.getData();//获取开始锚点自定义属性添加到…...

【python中级】解压whl文件内容

【python中级】解压whl文件内容 1.背景2.解压1.背景 【python中级】关于whl文件的说明 https://blog.csdn.net/jn10010537/article/details/146979236 补充以上博客: 在 旧版 setuptools 中(< v58),如果想生成 .whl,必须先pip install 安装 wheel 三方包! pip inst…...

Xilinx系列FPGA实现HDMI2.1视频收发,支持8K@60Hz分辨率,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我已有的4K/8K视频处理解决方案我已有的FPGA图像处理方案 3、详细设计方案设计框图硬件设计架构本HDMI2.1性能参数8K视频输入源Video PHY ControllerHDMI 2.1 Receive…...

如何把网页文章转为pdf保存

fnF12调出右边网页端的控制台 在下面输入代码 1、转CSDN上的文章 (function(){ use strict;var articleBox $("div.article_content");articleBox.removeAttr("style");var head_str ""; var foot_str ""; var olde…...

开源可视化大屏go-view前后端安装

一、后端安装 下载代码 git clone https://gitee.com/MTrun/go-view-serve修改配置 cd go-view-serve/ # 修改application-dev.yml的数据库文件地址 vi ./src/main/resources/application-dev.ymlapplication-dev.yml spring:datasource:driver-class-name: org.sqlite.JDB…...

eventEmitter实现

没有做任何异常处理,简单模拟实现 事件对象的每一个事件都对应一个数组 /*__events {"事件1":[cb1,cb2],"事件2":[cb3,cb4],"事件3":[...],"事件4":[...],};*/class E{__events {};constructor(){}//注册监听回调on(type , callbac…...

自然语言处理|如何用少样本技术提升低资源语言处理?

一、引言 在全球化的背景下&#xff0c;自然语言处理&#xff08;NLP&#xff09;技术取得了显著进展&#xff0c;为人们的生活和工作提供了便利。然而&#xff0c;大多数 NLP 研究和应用集中在少数高资源语言上&#xff0c;如英语和中文。据统计&#xff0c;全球存在超过 700…...

系统安全——文件监控-FileMonitor

namespace FileSystemWatcherDemo {public partial class Form1 : Form{ public Form1(){InitializeComponent();UsingFileSystemWatcher();} /// <summary>/// 使用FileSystemWatcher方法/// </summary>void UsingFileSystemWatcher(){//6.2//FileSystemWa…...

07-01-自考数据结构(20331)- 排序-内部排序知识点

内部排序算法是数据结构核心内容,主要包括插入类(直接插入、希尔)、交换类(冒泡、快速)、选择类(简单选择、堆)、归并和基数五大类排序方法。 知识拓扑 知识点介绍 直接插入排序 定义:将每个待排序元素插入到已排序序列的适当位置 算法步骤: 从第二个元素开始遍历…...

Unity:平滑输入(Input.GetAxis)

目录 1.为什么需要Input.GetAxis&#xff1f; 2. Input.GetAxis的基本功能 3. Input.GetAxis的工作原理 4. 常用参数和设置 5. 代码示例&#xff1a;用GetAxis控制角色移动 6. 与Input.GetAxisRaw的区别 7.如何优化GetAxis&#xff1f; 1.为什么需要Input.GetAxis&…...

【AI学习】MCP的简单快速理解

最近&#xff0c;AI界最火热的恐怕就是MCP了。作为一个新的知识点&#xff0c;学习的开始&#xff0c;先摘录一些信息&#xff0c;从发展历程、通俗介绍到具体案例&#xff0c;这样可以快速理解MCP。 MCP发展历程 来自i陆三金 Anthropic 开发者关系负责人 Alex Albert&#…...

单机快速部署开源、免费的分布式任务调度系统——DolphinScheduler

看了DolphinScheduler的介绍&#xff0c;不知道有没有引起你的兴趣&#xff0c;有没有想要上手体验一番呢。本文则主要为大家介绍DolphinScheduler的单机部署方式&#xff0c;方便大家快速体验。 环境准备 需要Java环境&#xff0c;这是一个老生常谈的问题&#xff0c;关于Ja…...

Vue3命名规范指南

在 Vue 3 中&#xff0c;遵循一致的命名规范可以提高代码的可读性和维护性。以下是常见的命名规范和实践建议&#xff1a; 1. 组件命名 PascalCase&#xff08;大驼峰式&#xff09; 单文件组件&#xff08;.vue 文件&#xff09;和组件引用时推荐使用 PascalCase&#xff0c;便…...

【大模型系列篇】大模型基建工程:基于 FastAPI 自动构建 SSE MCP 服务器

今天我们将使用FastAPI来构建 MCP 服务器&#xff0c;Anthropic 推出的这个MCP 协议&#xff0c;目的是让 AI 代理和你的应用程序之间的对话变得更顺畅、更清晰。FastAPI 基于 Starlette 和 Uvicorn&#xff0c;采用异步编程模型&#xff0c;可轻松处理高并发请求&#xff0c;尤…...