《剑指 Offer》专项突破版 - 面试题 76 : 数组中第 k 大的数字(C++ 实现)

目录

详解快速排序

面试题 76 : 数组中第 k 大的数字


 


详解快速排序

快速排序是一种非常高效的算法,从其名字可以看出这种排序算法最大的特点是快。当表现良好时,快速排序的速度比其他主要对手(如归并排序)快 2 ~ 3 倍。

快速排序的基本思想是分治法,排序过程如下所示:在输入数组中随机选取一个元素作为中间值(pivot),然后对数组进行分区(partition),使所有比中间值小的数据移到数组的左边,所有比中间值大的数据移到数组的右边。接下来对中间值左右两侧的子数组用相同的步骤顺序,直到子数组中只有一个数字为止

理解快速排序的关键在于理解它的分区的过程。下面以数组 [4, 1, 5, 3, 6, 2, 7, 8] 为例分析分区的过程。假设数字 3 被随机选中为中间值,该数字被交换到数组的尾部。接下来初始化两个指针,指针 P1 初始化至下标为 -1 的位置,指针 P2 初始化至下标为 0 的位置,如下图 (a) 所示。始终将指针 P1 指向已经发现的最后一个小于 3 的数字。此时尚未发现任何一个小于 3 的数字,因此将指针 P1 指向一个无效的位置。将指针 P2 从下标为 0 的位置开始向右扫描数组中的每个数字。当指针 P2 指向第 1 个小于 3 的数字 1 时,指针 P1 向右移动一格,然后交换两个指针指向的数字,此时数组(即两个指针)的状态如下图 (b) 所示。继续向右移动指针 P2 直到遇到下一个小于 3 的数字 2,指针 P1 再次向右移动一格,然后交换两个指针指向的数字,此时数组(即两个指针)的状态如下图 (c) 所示。继续向右移动指针 P2 直到指向数字 3 也没有遇到新的小于 3 的数字,此时整个数组已经扫描完毕。再次将指针 P1 向右移动一格,然后交换指针 P1 和 P2 指向的数字,于是所有小于 3 的数字都位于 3 的左边,所有大于 3 的数字都位于 3 的右边,如下图 (d) 所示。

class Solution {
public:vector<int> sortArray(vector<int>& nums) {srand((unsigned int)time(0));quickSort(nums, 0, nums.size() - 1);return nums;}
private:void quickSort(vector<int>& nums, int left, int right) {if (left >= right)return;int pivotLoc = partition(nums, left, right);quickSort(nums, left, pivotLoc - 1);quickSort(nums, pivotLoc + 1, right);}
​int partition(vector<int>& nums, int left, int right) {int random = left + rand() % (right - left + 1);swap(nums[random], nums[right]);
​int prev = left - 1, cur = left;while (cur < right){if (nums[cur] < nums[right] && ++prev != cur)swap(nums[prev], nums[cur]);++cur;}++prev;swap(nums[prev], nums[right]);return prev;}
};

快速排序的时间复杂度取决于所选取的中间值在数组中的位置。如果每次选取的中间值在排序数组中都接近于数组中间的位置,那么快速排序的时间复杂度是 O(nlogn)。如果每次选取的中间值都位于排序数组的头部或尾部,那么快速排序的时间复杂度是 O(n^2)。这也是随机选取中间值的原因,避免在某些情况下快速排序退化成时间复杂度为 O(n^2) 的算法。由此可知,在随机选取中间值的前提下,快速排序的平均复杂度是 O(nlogn),是非常高效的排序算法

很多面试官喜欢要求应聘者手写快速排序算法的代码,因此应聘者需要深刻理解快速排序的思想及分区的过程,这样在遇到要求手写快速排序的代码时也能心中有底。另外,快速排序中的 partition 函数还经常被用来选中数组中第 k 大的数字,而这也是一道非常经典的算法面试题


面试题 76 : 数组中第 k 大的数字

题目

请从一个乱序数组中找出第 k 大的数字。例如,数组 [3, 1, 2, 4, 5, 5, 6] 中第 3 大的数字是 5。

分析

面试题 59 中介绍过一种基于最小堆的解法,该解法的时间复杂度是 O(nlogk)。下面介绍一种更快的解法。

在长度为 n 的排序数组中,第 k 大的数字的下标是 n - k。下面用快速排序的函数 partition 对数组进行分区。

  1. 如果函数 partition 选取的中间值在分区之后的下标正好是 n - k,分区后左边的值都比中间值小,右边的值都比中间值大,即使整个数组不是排序的,中间值也肯定是第 k 大的数字

  2. 如果函数 partition 选取的中间值在分区之后的下标大于 n - k,那么第 k 大的数字一定位于中间值的左侧,于是再对中间值左侧的子数组分区

  3. 如果函数 partition 选取的中间值在分区之后的下标小于 n - k,那么第 k 大的数字一定位于中间值的右侧,于是再对中间值右侧的子数组分区

代码实现

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {srand((unsigned int)time(0));
​int target = nums.size() - k;int left = 0, right = nums.size() - 1;int pivotLoc = partition(nums, left, right);while (pivotLoc != target){if (pivotLoc < target)left = pivotLoc + 1;elseright = pivotLoc - 1;pivotLoc = partition(nums, left, right);}return nums[pivotLoc];}
private:int partition(vector<int>& nums, int left, int right) {int random = left + rand() % (right - left + 1);swap(nums[random], nums[right]);
​int prev = left - 1, cur = left;while (cur < right){if (nums[cur] < nums[right] && ++prev != cur)swap(nums[prev], nums[cur]);++cur;}++prev;swap(nums[prev], nums[right]);return prev;}
};

由于函数 partition 随机选择中间值,因此它的返回值也具有随机性,计算这种算法的时间复杂度需要运用概率相关的知识。此处仅计算一种特定场合下的时间复杂度。假设函数 partition 每次选择的中间值都位于分区后的数组的中间的位置,那么第 1 次函数 partition 需要扫描长度为 n 的数组,第 2 次需要扫描长度为 n/2 的子数组,第 3 次需要扫描 n/4 的子数组,重复这个过程,直到子数组的长度为 1。由于 n + n/2 + n/4 + ··· + 1 = 2n,因此总的时间复杂度是 O(n)

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

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

相关文章

浏览器与Node.js事件循环:异同点及工作原理

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

记一次项目所学(中间件等)-动态提醒功能(RocketMQ)

记一次项目所学(中间件等&#xff09;–动态提醒功能&#xff08;RocketMQ&#xff09; 订阅发布模式与观察者模式 RocketMQ&#xff1a;纯java编写的开源消息中间件 高性能低延迟分布式事务 Redis : 高性能缓存工具&#xff0c;数据存储在内存中&#xff0c;读写速度非常快 …

Meta正打造一个巨型AI模型,旨在为其“整个视频生态系统”提供动力,一位高管透露

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Swift 入门学习:集合(Collection)类型趣谈-上

概览 集合的概念在任何编程语言中都占有重要的位置&#xff0c;正所谓&#xff1a;“古来聚散地&#xff0c;宿昔长荆棘&#xff1b;游人聚散中&#xff0c;一片湖光里”。把那一片片、一瓣瓣、一粒粒“可耐”的小精灵全部收拢、吸纳的井然有序、条条有理&#xff0c;怎能不让…

tcp流式服务和粘包问题

目录 1.概念 2.流式服务 3.粘包问题 1.概念 套接字是一个全双工的 使用TCP协议通信的双方必须先建立连接,然后才能开始数据的读写,双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输. TCP连接是全双工的,即双方的数据读写可以通过一个连接进行,完成…

【C语言】linux内核ip_local_out函数

一、讲解 这个函数 __ip_local_out 是 Linux 内核网络子系统中的函数&#xff0c;部分与本地出口的 IPv4 数据包发送相关。下面讲解这段代码的每一部分&#xff1a; 1. 函数声明 int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)&#xff1a; -…

react实战——react旅游网

慕课网react实战 搭建项目问题1.按照官网在index.tsx中引入antd出错&#xff1f;2.typescript中如何使用react-router3.react-router3.1 V63.2 V53.3V6实现私有路由 4.函数式组件接收props参数时定义数据接口&#xff1f;5.使用TypeScript开发react项目&#xff1a;6.要使一个组…

SQLite3中的callback回调函数注意的细节

调用 sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)该例程提供了一个执行 SQL 命令的快捷方式&#xff0c; SQL 命令由 sql 参数提供&#xff0c;可以由多个 SQL 命令组成。 在这里&#xff0c; 第一个参数 sqlite3 是打开的数据库对…

代码随想录算法训练营第day41|背包理论基础、416. 分割等和子集

目录 a.背包理论基础——01背包 1.二维数组的01背包表示 2.一维滚动数组表示 b. 416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; a.背包理论基础——01背包 背包问题分类&#xff1a; 对于面试的话&#xff0c;其实掌握01背包&#xff0c;和完全背包&#xff…

Excel F4键的作用

目录 一. 单元格相对/绝对引用转换二. 重复上一步操作 一. 单元格相对/绝对引用转换 ⏹ 使用F4键 如下图所示&#xff0c;B1单元格引用了A1单元格的内容。此时是使用相对引用&#xff0c;可以按下键盘上的F4键进行相对引用和绝对引用的转换。 二. 重复上一步操作 ⏹添加或删除…

SSM框架,MyBatis-Plus的学习(下)

条件构造器 使用MyBatis-Plus的条件构造器&#xff0c;可以构建灵活高效的查询条件&#xff0c;可以通过链式调用来组合多个条件。 条件构造器的继承结构 Wrapper &#xff1a; 条件构造抽象类&#xff0c;最顶端父类 AbstractWrapper &#xff1a; 用于查询条件封装&#xf…

首屏性能优化:提升用户体验的秘籍

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

复盘-excel

excel-选列没有用&#xff0c;选小标题才可以 将簇状柱形图放置在一个新表上##### excel: 添加数据模型时&#xff0c;要通过套用表格格式与外部断开连接 透视分析2010年人数未解决(第四套&#xff09; 通过日期显示星期几 判断星期几 因为前面已经通过星期六&#xff0c…

03_Tomcat

文章目录 Tomcat概念自制简易的服务器JavaEE规范Tomcat安装Tomcat启动Tomcat的资源部署直接部署虚拟映射 Tomcat的设置 Tomcat 概念 服务器&#xff1a;两层含义。 软件层面&#xff1a;软件&#xff0c;可以将本地的资源发布到网络中&#xff0c;供网络上面的其他用户来访问…

grafana table合并查询

注&#xff1a;本文基于Grafana v9.2.8编写 1 问题 默认情况下table展示的是一个查询返回的多个field&#xff0c;但是我想要的数据在不同的metric上&#xff0c;比如我需要显示某个pod的读写IO&#xff0c;但是读和写这两个指标存在于两个不同的metirc&#xff0c;需要分别查…

多种方法求解数组排序

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

C++——string模拟实现

前言&#xff1a;上篇文章我们对string类及其常用的接口方法的使用进行了分享&#xff0c;这篇文章将着重进行对这些常用的接口方法的内部细节进行分享和模拟实现。 目录 一.基础框架 二.遍历字符串 1.[]运算符重载 2.迭代器 3.范围for 三.常用方法 1.增加 2.删除 3.调…

数据库-第十一章 并发控制【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下数据库系统概论中的重点概念&#xff0c;以供大家期末复习和考研复习的时候使用。 参考资料是王珊老师和萨师煊老师的数据库系统概论(第五版)。 数据库系统概论系列文章传送门&#xff1a; 第一章 绪论 第二/…

数据分析-Pandas数据画箱线图

数据分析-Pandas数据画箱线图 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&#xff…

【重新定义matlab强大系列十七】Matlab深入浅出长短期记忆神经网络LSTM

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…