[Leetcode 215][Medium]-数组中的第K个最大元素-快排/小根堆/堆排序

news2024/9/25 6:28:35

一、题目描述

原题地址

二、整体思路

(1)快排

                对于SELECT K问题,可以通过三路快排解决,快排可以把一个元素放至按升序排序的数组正确的位置,左边为小于该元素的元素集合,右边为大于该元素的元素集合。

三路快排把数组分成三部分:

 [l,l2-1]:<nums[l]的元素区间;[l2,r2-1]:=nums[l]的元素区间;[r2,r]:>nums[l]的元素区间;

当k位于[l2,r2-1]区间时,说明数组中[l2,r2-1]区间的元素都放置到正确的位置,k在其中说明k所指的元素已经排序至正确位置,可以返回。

当k位于[l,l2-1]区间时,说明还需要对[l,l2-1]进行快速排序,同理当k位于[r2,r]区间时,说明要对[r2,r]进行快速排序。

(2)小根堆

                要选择第K个最大元素,则可以把该数组[0,k-1]的部分转化为小根堆,遍历[k,nums.length-1]的部分,若遍历到的元素大于堆顶元素,则可以交换此两元素,然后对新的堆顶元素进行siftDown下沉操作,重复上述步骤,最终可以得到前K个最大元素组成的小根堆,堆顶即为第K大的元素。

(3)原地堆排序、大根堆

                对整个数组进行堆排序形成大根堆。把堆顶元素与数组末尾元素进行交换,这样可以得到第一大的元素。把新的堆顶元素在[0,nums.length-1)处进行siftdown()下沉操作,此时的堆顶元素即为数组第二大的元素,那么又可以与数组的[nums.length-2]元素进行交换得到新的堆。重复上述步骤直到得到数组第K大的元素。

三、代码

//快排
class Solution {
    public int findKthLargest(int[] nums, int k) {
        Random random=new Random();
        quicksort(nums,0,nums.length-1,nums.length-k,random);
        return nums[nums.length-k];
    }
    public void quicksort(int[] nums,int l,int r,int k2,Random random){
        if(l>=r) return;

        //把nums[l]与数组中随机位置的元素进行交换
        //防止快排退化导致时间复杂度增加
        int p=random.nextInt(r-l+1)+l;
        int temp3=nums[l];
        nums[l]=nums[p];
        nums[p]=temp3;

        int i=l+1,l2=l,r2=r+1;
        while(i<r2){
//[l,l2]:<nums[l]元素区间;[l2+1,i-1]:=nums[l]元素区间;[r2,r]:>nums[l]//元素区间
            if(nums[i]<nums[l]){
                int temp=nums[++l2];
                nums[l2]=nums[i];
                nums[i]=temp;
                i++;
            }else if(nums[i]>nums[l]){
                int temp4=nums[--r2];
                nums[r2]=nums[i];
                nums[i]=temp4;
            }else{
                i++;
            }
        }
//最后把nums[l]与nums[l2]交换
        int temp2=nums[l];
        nums[l]=nums[l2];
        nums[l2]=temp2;
//区间发生变化。[l,l2-1]:<;[l2,r2-1]:=;[r2,r]:>
        if(k2>=l2 && k2<=r2-1) return;
        else if(r2<=k2) quicksort(nums,r2,r,k2,random);
        else quicksort(nums,l,l2-1,k2,random);
    }
}
//小根堆
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //把数组[0,k-1]的部分化为小根堆
        for(int i=(k-2)/2;i>=0;i--){
            siftDown(nums,i,k);
        }
        //遍历数组剩下部分,同时把小根堆堆顶替换为更大的数组元素并对新堆顶执行下沉操作
        for(int i=k;i<nums.length;i++){
            if(nums[i]>nums[0]){
                int temp=nums[i];
                nums[i]=nums[0];
                nums[0]=temp;
                siftDown(nums,0,k);
            }
        }
        return nums[0];//此时小根堆代表前K大的元素,堆顶表示第K大的元素
    }
    public void siftDown(int[] arr,int i,int n){
        while((i*2+1)<n){
            int j=i*2+1;
            if(j+1<n && arr[j+1]<arr[j]) j++;
            if(arr[i]>arr[j]){
                int temp2=arr[i];
                arr[i]=arr[j];
                arr[j]=temp2;
                i=j;
            }else break;
        }
    }
}
//堆排序,大根堆
class Solution {
    public int findKthLargest(int[] nums, int k) {\
        //把整个数组化为大根堆
        for(int i=(nums.length-2)/2;i>=0;i--){
            siftDown(nums,i,nums.length);
        }
        int ret=0;
        //从数组末尾由后到前遍历,交换堆顶元素,把元素从最大元素到最小元素的顺序排序
        for(int i=nums.length-1,k2=0;i>=0;i--){
            swap(nums,0,i);
            siftDown(nums,0,i);
            k2++;
            if(k2==k) return nums[i];
        }
        return ret;
    }
    public void swap(int[] nums,int i,int j){
        int temp=nums[i];
        nums[i]=nums[j];
        nums[j]=temp;
    }
    public void siftDown(int[] nums,int i,int n){
        while((i*2+1)<n){//存在左子结点时
            int j=i*2+1;
            if(j+1<n && nums[j+1]>nums[j]) j++;//存在右子节点且右子节点比左子节点更大时
            if(nums[i]<nums[j]){//较小的儿子节点比当前节点大时,要下沉当前节点
                swap(nums,i,j);
                i=j;
            }else break;
        }
    }
}

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

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

相关文章

朋克养生,现代男人为何对生可乐泡枸杞情有独钟

在当今快节奏、高压力的社会环境中&#xff0c;朋克养生这一独特的养生方式悄然兴起&#xff0c;尤其在年轻男性群体中备受青睐。其中&#xff0c;生可乐泡枸杞这一不乏创意的养生方法&#xff0c;更是成为不少现代男人追求健康与乐趣并存的象征。朋克养生不仅仅是一种外在的行…

lvs的防火墙标记解决轮询调度问题

错误示范 ipvsadm -A -t 192.168.0.200:80 -s rr ipvsadm -a -t 192.168.0.200:80 -r 192.168.0.10:80 -g ipvsadm -a -t 192.168.0.200:80 -r 192.168.0.20:80 -g ipvsadm -A -t 192.168.0.200:443 -s rr ipvsadm -a -t 192.168.0.200:443 -r 192.168.0.10:80 -g ipvsadm -a …

CVE-2024-39877:Apache Airflow 任意代码执行

Apache Airflow 是一个开源平台&#xff0c;用于以编程方式编写、调度和监控工作流。虽然它提供了管理复杂工作流的强大功能&#xff0c;但它也存在安全漏洞。一个值得注意的漏洞 CVE-2024-39877 是 DAG&#xff08;有向无环图&#xff09;代码执行漏洞。这允许经过身份验证的 …

游戏ttf字体瘦身脚本

游戏中通常会用到某种特定字体&#xff0c;而某些字体动则10M&#xff0c;对某些游戏(尤其是小游戏)来讲是无法忍受的&#xff0c;此文章主要讲述上个项目中制作的字体裁剪脚本 工具git地址 配置信息(config.json) { // 文本内容(可能为多语言表导出的内容)"txtFile&qu…

常用API(三)

对于常见API的学习&#xff0c;主要学习了关于时间和日期的传统和新增APi 目录 1.Math 2.System 3.Runtime 4.日期和时间 &#xff08;1&#xff09;JDK8前传统时间API [1] Date [2] SimpledateFormat [3]Calendar &#xff08;2&#xff09;JDK8后新增时间API [1]代替…

JeecgBoot低代码平台简单记录

BasicModal弹窗 Usage 由于弹窗内代码一般作为单文件组件存在&#xff0c;也推荐这样做&#xff0c;所以示例都为单文件组件形式 注意v-bind"$attrs"记得写&#xff0c;用于将弹窗组件的attribute传入BasicModal组件 attribute&#xff1a;是属性的意思&#xff0c;…

嵌入式学习之线程和同步互斥机制

一. 线程的概念 进程的上下文切换&#xff1a; a.上下文&#xff1a;运行一个进程所需要的所有资源。 b.上下文切换&#xff1a;替换原有内容&#xff0c;从访问进程A的所有资源切换到访问进程B的所有资源&#xff0c;是一个耗时操作。 c.为了提高系统性能&#xff0c;引入了一…

HCIP | 实验一

实验内容 配置 1.合理规划IP地址&#xff0c;启用OSPF单区域 R1 R2 R3 R4 R5 R6 2.R1-R2之间启用PPP的pap单向认证 R1 R2 3.R2-R3之间启用PPP的chap双向认证 R2 R3 4.R3-R5-R6之间使用MGRE&#xff0c;R3为hub端&#xff0c;R5&#xff0c;R6为spoke端&#xff0c;要求MGRE…

DS1302实时时钟(51单片机)

一、DS1302时钟 1.DS1302时钟介绍 2.芯片使用 使用芯片时首先要通过数据手册知道芯片功能&#xff0c;根据芯片功能应用。 3.实现DS1302功能 通过对配置寄存器使用DS1302的读写功能 二、实现DS1302读写 1.模块化编程框架 首先对DS1302端口重新定义&#xff08;换端口名字…

汽车电子推拉力测试机测试流程的详细步骤

在现代汽车技术迅猛发展的今天&#xff0c;汽车电子产品的可靠性已成为确保车辆性能和乘客安全的关键因素。标准下的键合线剪切试验&#xff0c;作为评估这些产品中关键连接点强度的一项测试&#xff0c;扮演着至关重要的角色。本文旨在深入探讨这一测试的重要性、实施流程及其…

Java学习Day20

Vue学习 nodejs的安装与环境配置 1.直接去官网下载合适版本的nodejs( https://nodejs.org/zh-cn/download/prebuilt-installer) 2.解压下载的安装包&#xff0c;将文件路径配置到系统变量的path中&#xff0c;然后确认后退出。可以使用终端来查看安装的nodejs版本。使用winR…

智能合约的未来:解析Web3在智能合约领域的创新

随着区块链技术的不断发展&#xff0c;智能合约已成为Web3生态系统中的核心组成部分。智能合约通过在区块链上自动执行合约条款&#xff0c;推动了去中心化应用&#xff08;DApp&#xff09;的广泛应用。它们的核心优势在于去中心化、透明和不可篡改&#xff0c;这使得合同执行…

uniapp切换同一个子组件时,钩子函数只进了一次

给子组件添加不同的 “key” 值&#xff0c;当 key 值改变时&#xff0c;Vue 会认为这是一个不同的组件&#xff0c;并重新创建它 props: ["L1Id"],// 方式1: 使用keycomputed: {// 切换子组件时,发现created、mounted等钩子函数只会进一次,或者用 keykey(){this.ref…

RAG私域问答场景超级详细方案(第一期方案)[1]:工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块)

RAG私域问答场景整体夏详细方案(第一期方案):工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块) 大模型性能的跳阶式增长给文本摘要、信息检索、信息抽取、语义问答等自然语言处理任务带来了卓越的性能提升。同时,LangChain 作为一种基于 LLM 的框架,能够快速…

【autoware】安装ros2 密匙gpg报错,443连接失败

443连接问题解决方法&#xff1a; 访问该网站 https://ping.chinaz.com 输入raw.githubusercontent.com可以得到解析出来的IP sudo vim /etc/hosts

Unity射击游戏开发教程:(31)制造一定追踪行为的敌人

在本文中,我们将介绍如何在两种敌人行为之间切换。本文是前两篇文章的延续,分别介绍了敌人躲避玩家射击以及敌人不断旋转并向玩家射击的情况。我只是介绍如何在这两种行为之间进行转换。 这种新的敌人行为的目标: 当不开火时,敌人可以躲避玩家的射击。射击时,敌人无法躲避…

lvs实战项目-dr模式实现

一、环境准备 主机名IP地址router eth0&#xff1a;172.25.254.100 eth1&#xff1a;192.168.0.100 clienteth0&#xff1a;172.25.254.200lvseth1&#xff1a;192.168.0.50web1web2 1、client配置 [rootclient ~]# cat /etc/NetworkManager/system-connections/eth0.nmconne…

使用FFmpeg实现摄像头RTMP实时推流

在当今的数字时代,视频直播已成为连接人与人之间的重要桥梁,广泛应用于在线教育、远程会议、娱乐直播等多个领域。随着技术的不断进步,人们对于直播的实时性、稳定性和高质量需求日益增加。为了实现高效的视频直播,选择合适的工具和协议至关重要。 RTMP(Real-Time Messagi…

Kotlin OpenCV 视频分析和对象跟踪60 MIL 对象跟踪

Kotlin OpenCV 视频分析和对象跟踪60 MIL 对象跟踪 1 OpenCV 对象跟踪算法2 Kotlin 引入依赖3 OpenCV 下载4 Kotlin OpenCV MIL 对象跟踪 1 OpenCV 对象跟踪算法 算法算法特点1 BOOSTING Tracker基于 AdaBoost 算法。适合于简单的对象跟踪任务。算法较老&#xff0c;在复杂场景…

LLVM理论篇之编译器结构

1、概述 编译器完成源程序到目标程序的翻译工作&#xff0c;这是一个复杂的整体过程。从概念上讲&#xff0c;一个编译程序的整体过程可以分为3个阶段&#xff0c;每个阶段将程序的一种语言表示形式转换成另一种语言表示形式&#xff0c;并且各个阶段在逻辑上是紧密相连的。典…