-
2019-10-24 21:04:22更多相关内容
-
五分钟学算法:Top K 问题的两种经典解法
2020-04-15 13:30:00点击关注上方“五分钟学算法”,设为“置顶或星标”,第一时间送达干货。转自面向大象编程,作者nettee题目描述这是一道经典的面试题了,也叫做 “Top K 问题”。解决这个问题有两种常用...点击关注上方“五分钟学算法”,
设为“置顶或星标”,第一时间送达干货。
转自面向大象编程,作者nettee
题目描述
这是一道经典的面试题了,也叫做 “Top K 问题”。解决这个问题有两种常用的方法:堆方法、分治法。分治法的思想和快速排序相同。
本篇题解分别讲解了两种方法,并比较了它们的适用场景。
题目解析
这道题是一个经典的 Top K 问题,是面试中的常客。Top K 问题有两种不同的解法,一种解法使用堆(优先队列),另一种解法使用类似快速排序的分治法。这两种方法各有优劣,最好都掌握。本文用图解的形式讲解这道问题的两种解法,包括三个部分:
方法一:堆,时间复杂度
方法二:快排变形,(平均)时间复杂度
两种方法的优劣比较
方法一:堆
比较直观的想法是使用堆数据结构来辅助得到最小的 k 个数。堆的性质是每次可以找出最大或最小的元素。我们可以使用一个大小为 k 的最大堆(大顶堆),将数组中的元素依次入堆,当堆的大小超过 k 时,便将多出的元素从堆顶弹出。我们以数组 , 为例展示元素入堆的过程,如下面动图所示:
入堆出堆的过程 这样,由于每次从堆顶弹出的数都是堆中最大的,最小的 k 个元素一定会留在堆里。这样,把数组中的元素全部入堆之后,堆中剩下的 k 个元素就是最大的 k 个数了。
注意在动画中,我们并没有画出堆的内部结构,因为这部分内容并不重要。我们只需要知道堆每次会弹出最大的元素即可。在写代码的时候,我们使用的也是库函数中的优先队列数据结构,如 Java 中的
PriorityQueue
。在面试中,我们不需要实现堆的内部结构,把数据结构使用好,会分析其复杂度即可。以下是题解代码。感谢评论区提醒,这里的代码可以做一些优化,如果当前数字不小于堆顶元素,数字可以直接丢掉,不入堆。下方的代码已更新:
public int[] getLeastNumbers(int[] arr, int k) { if (k == 0) { return new int[0]; } // 使用一个最大堆(大顶堆) // Java 的 PriorityQueue 默认是小顶堆,添加 comparator 参数使其变成最大堆 Queue<Integer> heap = new PriorityQueue<>(k, (i1, i2) -> Integer.compare(i2, i1)); for (int e : arr) { // 当前数字小于堆顶元素才会入堆 if (heap.isEmpty() || heap.size() < k || e < heap.peek()) { heap.offer(e); } if (heap.size() > k) { heap.poll(); // 删除堆顶最大元素 } } // 将堆中的元素存入数组 int[] res = new int[heap.size()]; int j = 0; for (int e : heap) { res[j++] = e; } return res; }
算法的复杂度分析:
由于使用了一个大小为 k 的堆,空间复杂度为 ;
入堆和出堆操作的时间复杂度均为 ,每个元素都需要进行一次入堆操作,故算法的时间复杂度为 。
方法二:快排变形
Top K 问题的另一个解法就比较难想到,需要在平时有算法的积累。实际上,“查找第 k 大的元素”是一类算法问题,称为选择问题。找第 k 大的数,或者找前 k 大的数,有一个经典的 quick select(快速选择)算法。这个名字和 quick sort(快速排序)看起来很像,算法的思想也和快速排序类似,都是分治法的思想。
让我们回顾快速排序的思路。快速排序中有一步很重要的操作是 partition(划分),从数组中随机选取一个枢纽元素 v,然后原地移动数组中的元素,使得比 v 小的元素在 v 的左边,比 v 大的元素在 v 的右边,如下图所示:
partition 示意图 这个 partition 操作是原地进行的,需要 的时间,接下来,快速排序会递归地排序左右两侧的数组。而快速选择(quick select)算法的不同之处在于,接下来只需要递归地选择一侧的数组。快速选择算法想当于一个“不完全”的快速排序,因为我们只需要知道最小的 k 个数是哪些,并不需要知道它们的顺序。
我们的目的是寻找最小的 个数。假设经过一次 partition 操作,枢纽元素位于下标 ,也就是说,左侧的数组有 个元素,是原数组中最小的 个数。那么:
若 ,我们就找到了最小的 个数,就是左侧的数组;
若 ,则最小的 个数一定都在左侧数组中,我们只需要对左侧数组递归地 partition 即可;
若 ,则左侧数组中的 个数都属于最小的 个数,我们还需要在右侧数组中寻找最小的 个数,对右侧数组递归地 partition 即可。
这种方法需要多加领会思想,如果你对快速排序掌握得很好,那么稍加推导应该不难掌握 quick select 的要领。
以下是题解代码:
public int[] getLeastNumbers(int[] arr, int k) { if (k == 0) { return new int[0]; } else if (arr.length <= k) { return arr; } // 原地不断划分数组 partitionArray(arr, 0, arr.length - 1, k); // 数组的前 k 个数此时就是最小的 k 个数,将其存入结果 int[] res = new int[k]; for (int i = 0; i < k; i++) { res[i] = arr[i]; } return res; } void partitionArray(int[] arr, int lo, int hi, int k) { // 做一次 partition 操作 int m = partition(arr, lo, hi); // 此时数组前 m 个数,就是最小的 m 个数 if (k == m) { // 正好找到最小的 k(m) 个数 return; } else if (k < m) { // 最小的 k 个数一定在前 m 个数中,递归划分 partitionArray(arr, lo, m-1, k); } else { // 在右侧数组中寻找最小的 k-m 个数 partitionArray(arr, m+1, hi, k); } } // partition 函数和快速排序中相同,具体可参考快速排序相关的资料 // 代码参考 Sedgewick 的《算法4》 int partition(int[] a, int lo, int hi) { int i = lo; int j = hi + 1; int v = a[lo]; while (true) { while (a[++i] < v) { if (i == hi) { break; } } while (a[--j] > v) { if (j == lo) { break; } } if (i >= j) { break; } swap(a, i, j); } swap(a, lo, j); // a[lo .. j-1] <= a[j] <= a[j+1 .. hi] return j; } void swap(int[] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; }
上述代码中需要注意一个细节(评论区有好几个小伙伴问到,这里补充说明一下):
partitionArray
函数中,两次递归调用传入的参数为什么都是 k?特别是第二个调用,我们在右侧数组中寻找最小的 k-m 个数,但是对于整个数组而言,这是最小的 k 个数。所以说,函数调用传入的参数应该为 k。该代码的成绩还是非常好的:
算法的复杂度分析:
空间复杂度 ,不需要额外空间。
时间复杂度的分析方法和快速排序类似。由于快速选择只需要递归一边的数组,时间复杂度小于快速排序,期望时间复杂度为 ,最坏情况下的时间复杂度为 。
两种方法的优劣性比较
在面试中,另一个常常问的问题就是这两种方法有何优劣。看起来分治法的快速选择算法的时间、空间复杂度都优于使用堆的方法,但是要注意到快速选择算法的几点局限性:
第一,算法需要修改原数组,如果原数组不能修改的话,还需要拷贝一份数组,空间复杂度就上去了。
第二,算法需要保存所有的数据。如果把数据看成输入流的话,使用堆的方法是来一个处理一个,不需要保存数据,只需要保存 k 个元素的最大堆。而快速选择的方法需要先保存下来所有的数据,再运行算法。当数据量非常大的时候,甚至内存都放不下的时候,就麻烦了。
所以当数据量大的时候还是用基于堆的方法比较好。
END
点“在看”你懂得
-
五分钟学算法之经典算法题:二分查找
2019-10-22 12:15:00点击蓝色“五分钟学算法”关注我哟加个“星标”,天天中午 12:15,一起学算法作者 | 程序员小吴来源 | 五分钟学算法今天分享一道简单的笔试题,题目来源于京东校园招聘笔...点击蓝色“五分钟学算法”关注我哟
加个“星标”,天天中午 12:15,一起学算法
作者 | 程序员小吴
来源 | 五分钟学算法
今天分享一道简单的笔试题,题目来源于京东校园招聘笔试真题。你做出这道简单的题目需要花费多少东分钟呢?
题目描述
有一个有序表为 {1,5,8,11,19,22,31,35,40,45,48,49,50} ,当二分查找值为 48 的结点时,查找成功需要比较的次数( )
A、4
B、3
C、2
D、1
题目分析
一道送分题。
有序表的长度为 13,根据 二分查找法 查找数的特性,每次都 n/2 进行折半查找。
13 / 2 = 6
6 / 2 = 3
3 / 2 = 1
1 / 2 = 0
最多需要 4 次就能得出结果。
这道题目需要查找的是 48 ,列表下标索引从零开始标记。
第一次,求出 [0,12] 中间节点。
0 + (12 - 0) / 2 = 6 a[6] = 31 < 48
区间变为 [7,12]。
第二次,求出 [7,12] 中间节点。
7 + (12 - 7) / 2 = 9 a[9] = 45 < 48
区间变为 [10,12]。
第三次,求出 [10,12] 中间节点。
10 + (12 - 10) / 2 = 11 a[11] = 49 > 48
区间变为 [10,11]。
第四次,求出 [10,11] 中间节点。
10 + (11 - 10) / 2 = 10 a[10] = 48 = 48
找到啦!
---以上,便是今日分享,觉得不错,还请点个在看,谢谢~推荐阅读:
啦,欢迎点击阅读原文进行访问~
-
密码学算法
2018-04-03 15:18:32该内容为AES密码算法、MD5和背包公钥密码算法等文档内容,希望对大家在密码学方面有所帮助 -
学算法必备的一个网站与 app
2020-12-05 16:00:00公众号关注“五分钟学算法”设为 “星标”,带你挖掘更多开发神器!大家好,我是小 G。众所周知,程序员都需要翻越数据结构与算法这座大山,有的大神可以到达山顶领略更美好的风景,有的则在半山...公众号关注 “五分钟学算法”
设为 “星标”,带你挖掘更多开发神器!
大家好,我是小 G。
众所周知,程序员都需要翻越数据结构与算法这座大山,有的大神可以到达山顶领略更美好的风景,有的则在半山腰停下了绝不,而有的则在山底徘徊找不到上山的路径。
如果想要舒服的入门学习数据结构与算法,那我这两个辅助工具,一个是网站,一个是 app,它们的作用都是可以帮助你更加形象的理解数据结构与算法,因为它们都具备可视化的功能,再也不用担心在山底无助的摸索。
网站是 VisuAlgo,app 是算法动画图解。
1、VisuAlgo
VisuAlgo 是由 Steven Halim 博士在 2011 年发布的一款可视化学习算法的工具,用于帮助其学生更好地理解数据结构和算法, 它具备暂停、单步、回退等功能,可以实时的帮助学生更直观的理解代码的执行过程。
打开 https://visualgo.net/zh 一探究竟(是的,这个网站支持中文)。
直接映入眼帘的便是程序员常用的数据结构与算法,像排序、位掩码、链表、哈希表、二叉堆、二叉搜索树等常见算法动画应有尽有,就连高级的数据结构比如图、并查集、线段树等也安排的明明白白。
以我们第一个接触的算法冒泡排序为例,看看 VisuAlgo 是如何帮助学生更直观的理解数据结构与算法的。
首先进入冒泡排序的页面,在左下角创建模拟的排序数组,根据需要,可以将排序数组的数据具备一定的特殊性,比如是否近乎有序(一般情况下,近乎有序的时候采取插入排序效果最好)。
创建好数组好,点击 排序 --> 执行,可以看到上方的矩形开始运动交换,相应的,右下角也在跳动着代码,期间,你可以随时的暂停仔细琢磨代码与动画对应的逻辑。
2、算法动画图解
「算法动画图解」是一款可以在手机上运行的应用,可以在 App Store 以及各大应用市场下载,它用动画的形式帮你把基础算法捋一遍,每个算法动画的操作都是可以暂停、回退。
依旧以我们第一个接触的算法冒泡排序为例,看看 「算法动画图解」 是如何帮助学生更好的理解数据结构与算法的。
以上,便是今天的分享,觉得内容对你有所帮助的,还请点个「在看」支持,谢谢各位啦~
推荐阅读:
他,生物系毕业,刚入职连 Java 都没听过,却在马云的要求下,三周写出淘宝网雏形
-
信息学奥赛一本通-教程PPT课件(第五版)算法部分 第三章 递推算法.pdf
2021-03-03 21:41:11信息学奥赛一本通-教程PPT课件(第五版)算法部分 -
零基础学算法戴艳源码
2018-02-19 22:43:59零基础学算法(第2版)(零基础学编程)(光盘源码) 本书分为上、下两篇,共10章,上篇用5章的篇幅介绍了算法和数据结构的基础知识,包括基础算法思想、简单数据结构、复杂数据结构、排序和查找算法等内容;下篇用5... -
信息学奥赛一本通-教程PPT课件(第五版)算法部分 第六章 贪心算法.pdf
2021-03-03 21:43:14信息学奥赛一本通-教程PPT课件(第五版)算法部分 -
《算法和数据结构》学习路线指引
2021-07-01 11:16:15前 WorldFinal 选手对学习算法的一点总结。五张思维导图解决你的困惑 -
零基础学算法第3版零基础学编程 pdf
2018-04-17 11:04:52《零基础学编程:零基础学算法(第3版)》分为上、下两篇,共10章,上篇用5章的篇幅介绍了算法和数据结构的基础知识,包括基础算法思想、简单数据结构、复杂数据结构、排序和查找算法等内容;下篇用4章的篇幅介绍了用... -
运筹学大作业,用五种算法解决经典tsp问题
2017-04-16 16:33:18运筹学大作业,用五种算法解决经典tsp问题,选取某地一次定向越野比赛,使用模拟退火算法,蚁群算法,遗传算法,hopfiled神经网络,和lingo来解决问题。包括源代码和课程大报告 -
常见密码学算法
2020-06-22 16:15:21常见密码学算法:DES,AES; RSA, ECC; Hash; Signature等。 分类 对称密码 流密码 分组密码 非对称密码 不同阶段 古典/经典密码(凯撒密码),(1949 Shannon)近代密码(DES/AES),(1976 Diffie-Hellman, ... -
机器学习算法 无监督学习 算法
2019-06-26 14:51:22本文介绍无监督学习算法: 聚类算法 Apriori算法 FP-growth 算法 1 聚类算法 2 Apriori算法 3 FP-growth 算法 -
信息学奥赛一本通-教程PPT课件(第五版)算法部分 第四章 递归算法.pdf
2021-03-03 21:41:55信息学奥赛一本通-教程PPT课件(第五版)算法部分 -
如何学习算法?
2019-09-14 15:34:26今天在群里刚好看到有人在讨论算法的问题,刚好自己曾经也有一个算法大神的梦,来说说自己对算法的理解。算法怎么学?什么样程度才算把算法学透?算法学会了有什么用?算法的学习是非... -
计算机图形学算法总结
2020-12-16 23:49:40图形学算法总结 文章目录图形学算法总结直线生成算法数值微分法(DDA)中点画线法Bresenham算法圆弧生成算法中点Bresenham画圆法多边形填充算法逐点判断法1)射线法2)累计角度法扫描线算法(YX)改进的扫描线算法(Y-... -
算法如何学习?别想太多,两个字
2022-02-24 07:20:59当然,如果直接开始学算法,也不是不可以,只要坚持把该专栏的 100 讲 攻克完毕,基本上语言那关也可以过了。 五、算法进阶 1、「 画解数据结构 」 以动图的形式,更加生动形象的阐释每个数据结构的思想和实现。... -
运筹学中的tsp问题,利用蚁群算法,遗传算法等解决
2017-04-24 20:58:53运筹学中的tsp问题,利用蚁群算法,遗传算法,hopfield神经网络, lingo软件等五种方案解决 -
新手如何学习算法?算法如何入门以及零基础入门算法应该学些什么?
2018-08-18 20:08:42严格来说,本文题目应该是我的数据结构和算法学习之路,但这个写法实在太绕口——况且CS中的算法往往暗指数据结构和算法(例如算法导论指的实际上是数据结构和算法导论),所以我认为本文题目是合理的。 原文作者:... -
从大一到如今肝了四年算法,如何系统且全面着学习算法?
2021-04-14 15:37:53如何学习算法的相关文章,大家估计也见过不少,每个人的学习方法都不尽相同,这很正常,并且,对于不同的选手,例如打 ACM 的玩家和不打比赛的玩家来说,训练的方式也会有所差异,所以别人所说的学习方式,更多的是... -
如何入门学算法?
2017-11-23 17:13:20随着科学技术的发展,人工智能已渗透到各个行业,算法工程师非常火爆,急缺大量人才,年薪也越来越高。很多人想入手学习算法,那么多算法,究竟该如何下手呢? -
现代密码学(DES算法、RSA算法以及MD5算法)
2022-04-28 15:47:33DES算法 DES算法为密码体制中的对称密码体制,又被称为美国数据加密标准,是1972年美国IBM公司研制的对称密码体制加密算法。 明文按64位进行分组,密钥长64位,密钥事实上是56位参与DES运算(第8、16、24、32、40、... -
尹成老师带你学算法
2019-07-09 14:14:59语言有很多,开发框架更是日新月异3个月不学就落后 我们可以学习很多语言,很多框架,但招聘不会考你用5种语言10种框架实现同一个功能。真正让程序员有区分度,企业招聘万年不变的重点 —— 算法与数据结构。本课程... -
五大经典算法
2018-04-22 19:27:31主要讲解分治法、动态规划、贪心法、回溯法以及分支限界法等几种种经典的算法, 让同学们能够更进步了解算法的魅力所在。 在本章对每一种算法都是从...让读者能够综合运用前面几章所学知识,进一步加深对算法的理解。 -
大一新生先学C语言编程还是先学C语言的数据结构和算法?
2021-10-22 13:00:01先说答案建议先学C语言,掌握基本的语法基础后,再学数据结构与算法,C语言编程与数据结构和算法这两个完全是两个东西了。 学习数据结构和算法有一个很重要的前提,就是至少熟练掌握一门编程语言,编程语言掌握肯定... -
【数据结构和算法】如何学习数据结构与算法 ?过来人的建议(一)【方法篇】
2021-06-30 22:46:45按照我下面整理的思路学习,保证能让你大幅提升数据结构与算法实践能力! 许多人有这样的疑问,《数据结构与算法》理论学习完了,但是做题还是不会;有的同学感觉数据结构与算法不知道怎么学习。那看这篇文章就对了... -
看动画学算法之:环检测算法-弗洛伊德的兔子和乌龟
2020-09-23 10:01:52环检测应该是一个非常常见的算法问题,怎么判断是否有环的问题呢? 一个很简单的做法就是用HashSet来保存要遍历的数据,如果出现了重复就知道这个链表是有环的。但是这个方法需要保存遍历过的所有的元素,所以其... -
为什么要学习算法?
2018-02-11 09:58:53在我学习JVM的时候,有一个5~6年开发经验的coder说:学习这个有什么用啊,在工作中又用不到,还这么难。 这句话引起了我的思考,这个问题和我今天想说的,为什么要学习算法其实是同一个问题,也是不少IT从业者不解... -
机器学习必学十大算法
2021-10-03 01:08:08点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达本文介绍了 10 大常用机器学习算法,包括线性回归、Logistic 回归、线性判别分析、朴素贝叶斯、KNN、随机森林...