算法:前K个最大的元素

前几天,阮一峰 和 winter 在前端九部组织了一个互面小组,目的是为了分享和解答面试遇到的面试题,感兴趣的可以了解一下。

下面我就把我回答的一个问题整理出来分享给大家。

问题描述

题目是:算法,前 K 个最大的元素。

这个题目非常简短,第一眼看上去可能不知道是什么意思。翻译一下:

给定一个数字类型的数组和一个正整数 K,找出数组中前 K 个最大的元素。

这个题目网速也有很多的讲解,我也是根据网上提供的一些思路来实现的,下面就是我根据其中三种方法的实现:

解答

解法一:

思路

最简单的方法就是对数组进行排序,然后取前 K 位就可以了。

实现

/*** 查找前 K 个最大的元素* * @param {number[]} arr - 要查询的数组* @param {number} k - 最大个数* * @return {number[]}*/
const findKMax = (arr, k) => {return arr.sort((a, b) => b - a).slice(0, k);
}
复制代码

解法二

思路

解法一用了 js 的 sort 来实现排序,但是复杂度比较高,数据量大的话会比较慢。仔细分析一下题目,找出前 K 个最大的元素,但并没有要求对其排序,所以不用对所有的数都进行排序。分治法就会快很多:

假设有 n 个数存在数组 S 中,从数组 S 中随机找一个元素 X,遍历数组,比 X 大的放在 S1 中,比 X 小的放在 S2 中,那么会出现以下三种情况:

S1 的数字个数等于 K,结束查找,返回 S1; S1 的数字个数大于 K,继续在 S1 中找取最大的K个数字; S1 的数字个数小于 K,继续在 S2 中找取最大的 K-S1.length 个数字,拼接在 S1 后; 这样递归下去,就可以找出答案来了。下面看具体的实现:

实现

/*** 分割数组* * @typedef {Object} Partition* @property {number[]} Partition.maxarr* @property {number[]} Partition.minarr* * @param {number[]} arr - 要分割的数组* * @returns {Partition} res - 返回结果*/
const partition = (arr) => {const length = arr.length; // 数组长度const mid = ~~(length / 2); // 取数组中间的位置,可随机const middle = arr[mid]; // 数组中间的值const maxarr = []; // 比中间值大const minarr = []; // 比中间值小// 数组长度为 2 的要特殊处理if (length === 2) {maxarr.push(Math.max(arr[0], arr[1]));minarr.push(Math.min(arr[0], arr[1]));} else {arr.forEach((v, i) => {if (i !== mid) {if (v >= middle) {maxarr.push(v);} else {minarr.push(v);}}})// 将中间值放到 maxarr 的最后一位maxarr.push(middle);}return { maxarr, minarr }
}/*** 查找前 K 个最大的元素* * @param {number[]} arr - 要查询的数组* @param {number} k - 最大个数* * @return {number[]}*/
const findKMax = (arr, k) => {if (arr.length < k) {return arr;}// 分割数组const { maxarr, minarr } = partition(arr);if (maxarr.length === k) {return maxarr;}if (maxarr.length > k) {return findKMax(maxarr, k);}if (maxarr.length < k) {return maxarr.concat(findKMax(minarr, k - maxarr.length));}
}
复制代码

解法三

思路

可以取数组的前 K 位构建一个小顶堆(也叫最小堆),这么堆顶就是前 K 位最小的值,然后从 K+1 遍历数组,如果小于堆顶,则将其交换,并重新构建堆,使堆顶最小,这么遍历结束后,堆就是最大的 K 位,堆顶是前 K 位的最小值。

实现

/*** 小顶堆叶子节点排序* @param {number[]} arr - 堆* @param {number} i = 父节点* @param {length} i - 堆大小*/
const heapify = (arr, i, length) => {const left = 2 * i + 1; // 左孩子节点const right = 2 * i + 2; // 右孩子节点let minimum = i; // 假设最小的节点为父结点// 确定三个节点的最小节点if (left < length && arr[left] < arr[minimum]) {minimum = left;}if (right < length && arr[right] < arr[minimum]) {minimum = right;}// 如果父节点不是最小节点if (minimum !== i) {// 最小节点和父节点交换const tmp = arr[minimum];arr[minimum] = arr[i];arr[i] = tmp;// 对调整的结点做同样的交换heapify(arr, minimum, length);}}/*** 构建小顶堆* 从 n/2 个节点开始,依次构建堆,直到第一个节点* * @param {number[]} arr */
const buildMinHeap = (arr) => {for (let i = Math.floor(arr.length / 2); i >= 0; i--) {heapify(arr, i, arr.length)}return arr;
}/**·* 查找前 K 个最大的元素* * @param {number[]} arr - 要查询的数组* @param {number} k - 最大个数* * @return {number[]}*/
const findKMax = (arr, k) => {// 取数组的前 K 位构建小顶堆const newArr = [...arr];const kMax = arr.slice(0, k)buildMinHeap(kMax);// 堆后面的进行遍历,如果比堆顶大,则交换并重新构建堆for (let i = k; i < newArr.length; i++) {if (newArr[i] > kMax[0]) {const tmp = kMax[0];kMax[0] = newArr[i];newArr[i] = tmp;buildMinHeap(kMax);}}return kMax;
}
复制代码

总结

上面就是我对这个题目的三种解法,其实还有几种解法,因为精力原因没有探究,大家可以自己去网上了解一下。

上述解法如果有问题还请指正。

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

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

相关文章

php表单提交完返回,表单内容不清空解决方法

2019独角兽企业重金招聘Python工程师标准>>> 我们经常在注册的时候&#xff0c;填写一大推信息以后在提交注册的时候&#xff0c;因为某一项信息不正确&#xff0c;在返回的时候之前的填写的内容全部没有了&#xff0c;这样会导致用户丧失再次填写的信息&#xff0c…

es6拼接字符串的方式。

文章&#xff1a;es6拼接字符串的方式。转载于:https://www.cnblogs.com/Tpf386/p/9519007.html

word标尺灰色_如何在Microsoft Word中使用标尺

word标尺灰色Word’s rulers let you control the margins of your page and the indentation of paragraphs. They’re great for precisely lining up images, text, and other elements. If you’re printing a document, the rulers can help ensure that what you see on …

drools简单应用

当某个服务的需求经常变的时候&#xff0c;如果使用了硬编码的方式进行开发会是一件非常麻烦的事。 最近在对项目的积分模块进行改造的时候想到了规则引擎&#xff0c;使用规则引擎处理复杂而且多变的业务逻辑有其非常大的优势&#xff0c;包括实时更新、性能等方面。 不多说&a…

31 天重构学习笔记28. 为布尔方法命名

摘要&#xff1a;由于最近在做重构的项目&#xff0c;所以对重构又重新进行了一遍学习和整理&#xff0c;对31天重构最早接触是在2009年 10月份&#xff0c;由于当时没有订阅Sean Chambers的blog&#xff0c;所以是在国外的社区上闲逛的时候链接过去的。记得当时一口气看完了整…

Matplotlib学习---用matplotlib画误差线(errorbar)

误差线用于显示数据的不确定程度&#xff0c;误差一般使用标准差&#xff08;Standard Deviation&#xff09;或标准误差&#xff08;Standard Error&#xff09;。 标准差&#xff08;SD&#xff09;&#xff1a;是方差的算术平方根。如果是总体标准差&#xff0c;那么用σ表示…

关于自增id 你可能还不知道

导读&#xff1a;在使用MySQL建表时&#xff0c;我们通常会创建一个自增字段(AUTO_INCREMENT)&#xff0c;并以此字段作为主键。本篇文章将以问答的形式讲述关于自增id的一切。 注&#xff1a; 本文所讲的都是基于Innodb存储引擎。 1.MySQL为什么建议将自增列id设为主键&#x…

Android One和Android Go有什么区别?

In 2014, Google announced a lineup of low-cost, low-spec phones called Android One. In 2017, they announced Android Go, specifically designed for low-cost, low-spec phones. So…what’s the difference? 2014年&#xff0c;Google宣布了一系列名为Android One的低…

outlook advanced find 快捷键不起作用

症状&#xff1a;用户反应按outlook advanced find的快捷键时无效&#xff0c;快捷键为CtrlShiftF。第一感觉是肯定跟别的软件有冲突了&#xff0c;观察了下&#xff0c;发现用户正在使用sougou拼音输入法&#xff0c;于是点其属性查看&#xff0c;果然发现与其的简繁切换冲突了…

vue1.0和vue2.0生命周期----整理一

## 1. 作用域区别   1.x 随意的定义作用域   2.x 不允许body 或者html 元素 ## 2. 生命周期   1.x:     created 实例已经创建     beforeCompile 在编译之前     compiled 编译之后     ready 实例已经插入到文档之中     beforeDetroy 在销毁之前 …

21-while里的break简单用法

break是结束循环&#xff0c;break之后、循环体内代码不再执行。 while True:yn input(Continue(y/n): )if yn in [n,N]:breakprint(running......) 结果输出&#xff1a; 转载于:https://www.cnblogs.com/hejianping/p/10861816.html

视频造假_如何发现“深造假”面部切换视频

视频造假Recently, Reddit has been making news again with a subreddit in w hich people use a machine learning tool called “Deep Fake” to automatically replace one person’s face with another in a video. Obviously, since this is the internet, people are us…

C#实现MD5加密

C#实现MD5加密。 1、创建MD5Str.cs加密处理类 [csharp] view plaincopy public class MD5Str { /// <summary> /// 字符串MD5加密 /// </summary> /// <param name"Text">要加密的字符串</param> /// <returns…

【agc004f】Namori Grundy

那个问一下有人可以解释以下这个做法嘛&#xff0c;看不太懂QwQ~ Description 有一个n个点n条边的有向图&#xff0c;点的编号为从1到n。 给出一个数组p&#xff0c;表明有&#xff08;p1&#xff0c;1&#xff09;&#xff0c;&#xff08;p2&#xff0c;2&#xff09;&#x…

找到特定ip地址 修改ip_您如何找到网站的IP地址?

找到特定ip地址 修改ipWhether you are in it just for a bit of geeky fun, or are seriously wanting to know the answer, how do you find out the IP address for a website? Today’s SuperUser Q&A post looks at the answer, and how to know if more than one we…

Rational Rose 2003 下载、破解及安装方法(图文)

方法一&#xff1a; 1、安装Rational Rose2003时&#xff0c;在需选择安装项的时候&#xff0c;只选择Rational Rose EnterPrise Edition即可&#xff0c;不需选择其他项&#xff0c;之后选择“DeskTop Installation from CD Image“&#xff0c;一路下一步。出现Mem_pointer_B…

数据结构:莫队

莫队算法是用来处理一类无修改的离线区间询问问题 莫队的精髓就在于&#xff0c;离线得到了一堆需要处理的区间后&#xff0c;合理的安排这些区间计算的次序以得到一个较优的复杂度 代表题目是BZOJ2038这道题 进行区间询问[l,r]&#xff0c;输出该区间内随机抽两次抽到相同颜色…

【学习笔记】第三章 python3核心技术与实践--Jupyter Notebook

可能你已经知道&#xff0c;Python 在 14 年后的“崛起”&#xff0c;得益于机器学习和数学统计应用的兴起。那为什么 Python 如此适合数学统计和机器学习呢&#xff1f;作为“老司机”的我可以肯定地告诉你&#xff0c;Jupyter Notebook &#xff08;https://jupyter.org/&…

二进制安位处理_处理器与安​​全性之间的联系是什么?

二进制安位处理Newer processors are able to contribute to the security of your system, but what exactly do they do to help? Today’s Super User Q&A post looks at the link between processors and system security. 较新的处理器能够为您的系统安全做出贡献&am…

李开复现身说法成功的十个启发

http://blog.sina.com.cn/kaifulee自信不失谦虚&#xff0c;谦虚不失自信天赋就是兴趣 兴趣就是天赋思考比传道重要 观点比解惑重要我不同意你 但我支持你挫折不是惩罚 而是学习的机会创新不重要 有用的创新才重要完美的工作 成长兴趣 影响力用勇气改变可以改变的事情做最好的领…