leetcode热题HOT 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

二、基于堆排序的选择方法:建立一个大根堆,堆顶元素nums[0]为最大值,做 k−1次删除操作后的堆顶元素就是数组中第 k 个最大的元素。

1、堆是什么?

堆是一种特殊的完全二叉树,具有堆化的特性,可以用数组实现。与一般的排序方式所定义的有序不同,看似堆数组中的数字并未按照升序 或 降序排列,但其实这棵树是有序的状态。按照元素升序和降序排序分为大根堆和小根堆。
大根堆: 父结点的值 大于或等于 其子结点的值。
小根堆: 父结点的值 小于或等于 其子结点的值。
与二叉树类似,堆能以 O(logN) 的时间复杂度完成插入、删除和查找操作,通过调整数组中元素的顺序,维护堆的结构。

2、构建大根堆

在最大堆中,每个节点的值都大于或等于其子节点的值。由于叶子节点没有子节点,它们已经满足了这个性质,因此不需要进行任何调整。而最后一个非叶子节点的索引是数组长度的一半减一。因此,从数组长度的一半 n / 2 向前遍历,可以确保每个非叶子节点都能被正确处理。

    public void buildMaxHeap(int[] a, int n) {
        //从数组长度的一半处向前遍历,可以确保每个非叶子节点都能被正确处理。
        for(int i = n / 2; i >= 0; --i) {
            maxHeapify(a, i, n);
        }
    }

3、调整大根堆

从最后一个非叶子节点开始向前遍历,比较自己与左右子结点的大小,如果小于子结点就交换(即:下调 )。下调之后继续与新的左右孩子结点进行比较,能够下调就下调,直到不能下调为止。向前继续移动,直到所有结点均遍历一遍,所有父结点均大于其孩子结点,便成为了一棵大根堆。

    //和左右子节点比较,若大于子节点,向下调
    public void maxHeapify(int[] a, int i, int n) {
        int l = i * 2 + 1, r = i * 2 + 2, max = i;
        if (l < n && a[l] > a[max]) {
            max = l;
        }
        if (r < n && a[r] > a[max]) {
            max = r;
        }
        if (max != i) {
            swap(a, i, max);
            maxHeapify(a, max, n);//递归调用,直到满足根节点大于左右节点的值
        }
    }

4、删除操作

我们要找到第K大的元素,那么需要从堆中删除K次最大元素nums[0]。每次将堆顶元素与最后一个元素进行交换,这样最大值就来到了数组的最后。由于堆顶发生了变化,可能不再是一个大根堆,因此需要对大根堆进行调整。

        for(int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            swap(nums, 0, i);//堆顶元素与数组最后一位元素交换
            --n;//长度减一
            maxHeapify(nums, 0, n);//删除堆顶元素,无序的部分被放到末尾
        }

5、完整代码:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int n = nums.length;
        //数组中的第K个最大元素就是第n - k大的元素,对应排序后的数组的第n - k个元素
        buildMaxHeap(nums, n);
        for(int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            swap(nums, 0, i);//堆顶元素与数组最后一位元素交换
            --n;//长度减一
            maxHeapify(nums, 0, n);//删除堆顶元素,无序的部分被放到末尾
        }
        return nums[0];//删除K次,返回堆顶元素,即为我们要找的值
    }  
    public void buildMaxHeap(int[] a, int n) {
        //从数组长度的一半处向前遍历,可以确保每个非叶子节点都能被正确处理。
        for(int i = n / 2; i >= 0; --i) {
            maxHeapify(a, i, n);
        }
    }
    //和左右子节点比较,若大于子节点,向下调
    public void maxHeapify(int[] a, int i, int n) {
        int l = i * 2 + 1, r = i * 2 + 2, max = i;
        if (l < n && a[l] > a[max]) {
            max = l;
        }
        if (r < n && a[r] > a[max]) {
            max = r;
        }
        if (max != i) {
            swap(a, i, max);
            maxHeapify(a, max, n);//递归调用,直到满足根节点大于左右节点的值
        }
    }
    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}
  • 时间复杂度分析:总体而言,这段代码使用了堆排序的思想,在构建最大堆后,通过反复交换堆顶元素和末尾元素,并对堆进行调整,最终得到第 k 个最大的元素。时间复杂度为 O(nlogn),空间复杂度为 O(1)。

三、基于快速排序的选择方法:先对原数组升序排序,再返回倒数第 k个位置。

快速排序是一种常用的排序算法,其基本思想是通过选取一个基准值,将数组分割成两部分,一部分小于基准值,另一部分大于基准值,然后对这两部分分别进行递归排序。具体步骤如下:

  1. 选择一个基准值(通常是数组的第一个元素)。
  2. 将数组中小于基准值的元素移到基准值的左边,大于基准值的元素移到基准值的右边。
  3. 继续对基准值左右两部分分别递归进行快速排序。
  4. 这是一个递归过程,直到每个子数组的大小为 1 或 0 时,排序完成。

代码示例:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int n = nums.length;
        // 数组中的第 k 个最大元素就是第 n - k 大的元素,对应排序后的数组的第 n - k 个元素
        return quickselect(nums, 0, n - 1, n - k);
    }
    
    // 快速选择算法
    int quickselect(int[] nums, int l, int r, int k) {
        // 如果左右指针相遇,说明排序完成
        if (l == r) return nums[k];
        
        // 以 nums[l] 为基准,将数组中小于基准值的元素移到基准值的左边,大于基准值的元素移到基准值的右边
        int x = nums[l], i = l - 1, j = r + 1;
        while (i < j) {
            i++; while (nums[i] < x) i++;  // 找到第一个大于等于基准值的元素
            j--; while (nums[j] > x) j--;  // 找到第一个小于等于基准值的元素
            // 如果 i < j,交换这两个元素
            if (i < j) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
            }
        }
        
        // 根据 j 的位置确定下一步递归的范围
        if (k <= j) return quickselect(nums, l, j, k);  // 第 k 个最大的元素在左边部分
        else return quickselect(nums, j + 1, r, k);     // 第 k 个最大的元素在右边部分
    }  
}

  • 时间复杂度分析:这种方法的时间复杂度平均情况下为 O(n),最坏情况下为 O(n^2)。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java中的Set集合和Hash值和TreeSet的使用

Set集合的特点 不包含重复元素的集合 没有带索引的方法&#xff0c;所以不能使用普通for循环遍历 HashSet对集合的迭代顺序不作任何保证 Set集合的遍历 有两种方式遍历一种是迭代器一种是增强for package dayhou40.day49; ​ import java.util.HashSet; import java.util…

C语言程序设计基础(跟知乎学)

1、C语言是一种结构化程序设计语言。三种基本结构&#xff1a;顺序、选择、循环。 2、在C语言中&#xff0c;程序的模块化是利用函数实现的。 3、计算机不能直接理解高级语言&#xff0c;只能直接理解机器语言&#xff0c;所以必须要把高级语言翻译成机器语言&#xff0c;计算机…

C++ | Leetcode C++题解之第51题N皇后

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<vector<string>> solveNQueens(int n) {auto solutions vector<vector<string>>();auto queens vector<int>(n, -1);auto columns unordered_set<int>();auto diag…

Error: The project seems to require yarn but it‘s not installed.

在使用yarn serve启动vue2项目是发生报错信息 报错信息&#xff1a; 解决方法&#xff1a; 1、 红色箭头行&#xff0c;ctrl单击 进入env.js文件 2、注释行 if (result && !exports.hasYarn()) throw new Error(The project seems to require yarn but its not ins…

Oracle VM virtual Box 安装虚拟机并网络连接宿主机且能ping通外网

新建虚拟机 参考镜像下载连接&#xff1a;支持centos7.8及其以上版本&#xff1a;​ ​http://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1908.iosOracle VM virtual Box新建虚拟机&#xff0c;按照下图所示新建虚拟机&#xff1a; 1.新建虚拟机 2.配…

IDEA安装插件Apipost方便调试

进入IDEA的File->sttings->plugins 进入搜索Apipost即可安装插件 使用小箭头即可得到URL&#xff0c;点击进入操作即可

C# Onnx yolov8 pig detection

C# Onnx yolov8 pig detection 目录 效果 项目 模型 代码 数据集 下载 效果 项目 模型 Model Properties ------------------------- date&#xff1a;2024-04-28T15:13:10.750689 description&#xff1a;Ultralytics YOLOv8n model trained on C:\Work\yolov8\datas…

深入探索计算机视觉:高级主题与前沿应用的全面解析

引言 计算机视觉&#xff0c;作为人工智能领域的一个重要分支&#xff0c;旨在让计算机能够“看”懂世界&#xff0c;理解和解释视觉场景。随着深度学习技术的迅猛发展&#xff0c;计算机视觉已经在许多领域取得了显著的进展&#xff0c;如自动驾驶、安防监控、医疗诊断等。在…

NodeJs[黑马笔记简洁版]

是什么 怎么用 模块 模块化标准 CommonJs(标准语法)默认 ECMAscript 内置模块 fs模块 path模块 http模块 自定义模块 第三方包 包概念 npm 包管理器 总结

正点原子[第二期]ARM(I.MX6U)裸机篇学习笔记-1.2

前言&#xff1a; 本文是来自哔哩哔哩网站上视频“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”的学习笔记&#xff0c;在这里会记录下正点原子Linux ARM MX6ULL 开发板根据配套的哔哩哔哩学习视频所作的实验和笔记内容。本文大量的引用了正点原子哔哔哩网…

【C++】二叉树的进阶

二叉树的进阶 二叉搜索树概念操作实现创建树形结构拷贝构造函数构造函数析构函数赋值运算符重载循环版本查找插入删除 递归版本查找插入删除 应用K模型KV模型性能分析 二叉树进阶面试题二叉树创建字符串二叉树的分层遍历I最近公共祖先二叉搜索树与双向链表前序遍历与中序遍历构…

数据结构:实验八:数据排序

一、 实验目的 &#xff08;1&#xff09;掌握各种排序算法的过程和算法设计。 &#xff08;2&#xff09;体会各种排序算法的性能。 二、 实验要求 编写程序分别采用直接插入排序算法 、折半插入排序算法、冒泡排序算法、快速排序算法&#xff0c;实现对任意一组整型数据…

WEB攻防-.NET特性常见漏洞

目录 前置知识&#xff1a; DLL文件 .NET和DLL文件 C#和DLL文件 关系总结 .NET 配置调试-信息泄露 .NET 源码反编译-DLL 反编译与未授权访问 编译DLL文件 反编译DLL文件 注意事项 案例&#xff1a; 验证代码文件有没有可以绕过&#xff08;Cookie&Session&…

免费调用阿里云通义千问(qwen-1.8b-chat)大模型API

目录 前言通义千问开通注意 APi接口最后 前言 免费的GPT接口国内的使用一段实践就会失效&#xff0c;阿里云的qwen-1.8b-chat限时免费&#xff0c;可对接&#xff01;目前本账号小助手也是对接了该模型 通义千问 通义千问&#xff0c;是基于阿里巴巴达摩院在自然语言处理领域…

pytest测试基础

assert 验证关键字 需要pahton版本大于3.6&#xff0c;因为有个工具pip3;因为做了映射&#xff0c;所以下面命令pip3即pip pip install -U pytest -U参数可选&#xff0c;是如果已安装可更新。 如果上述demo变化 通过验证代码&#xff0c;测试环境没问题。…

服务器数据恢复—存储硬盘坏道,指示灯亮黄色的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 一台某品牌EqualLogic PS系列某型号存储&#xff0c;存储中有一组由16块SAS硬盘组建的RAID5磁盘阵列&#xff0c;RAID5上划分VMFS文件系统存放虚拟机文件。存储系统上层一共分了4个卷。 raid5阵列中磁盘出现故障&#xff0c;有2块硬盘…

关于远程桌面端口的优化措施的建议

在信息技术的世界中&#xff0c;远程桌面连接已成为企业、教育和个人用户之间共享信息、协作工作的重要工具。而这一切的背后&#xff0c;都离不开远程桌面端口&#xff08;RDP&#xff0c;Remote Desktop Protocol Port&#xff09;的支持。RDP端口不仅关乎到远程访问的顺畅性…

RK3568 学习笔记 : busybox 制作 ext4最小根文件系统

前言 开发板型号&#xff1a; 【正点原子】 的 RK3568 开发板 AtomPi-CA1 使用 VMware 虚拟机 ubuntu 20.04 编译 busybox&#xff0c;并制作 emmc 中的 ext4 根文件系统 rootfs 下载 busybox 可以在 https://busybox.net/downloads/snapshots/ 下载最新的 busybox&#xff…

蓝桥杯——分巧克力

思路非常简单&#xff0c;就是一个二分法。 注意一下l和r的取值&#xff0c;就可以了。 // 如何进行切分巧克力&#xff1a;横纵除法。例如&#xff1a;一块6*5的&#xff0c;欲切为3*3的小块&#xff0c;横&#xff1a;6/2 3&#xff1b;纵&#xff1a;5/31.所以可以切成3*…

学习100个Unity Shader (15) ---透明+双面渲染

文章目录 效果shader理解参考 效果 shader Shader "Example/AlphaBlendBothSided" {Properties{_Color ("Main Tint", Color) (1, 1, 1, 1)_MainTex ("Texture", 2D) "white" {}_AlphaScale ("Alpha Scale", Range(0, 1)…
最新文章