剑指offer打卡Day16:最小的k个数(优先队列与快速排序)
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
示例
输入
[4,5,1,6,2,7,3,8],4
返回值
[1,2,3,4]
解析:
-
万物皆可用暴力法破解:
- 对数组进行判断,(判断非空与 k>input.length 或者K==0 等情况)
- 对数组进行排序,默认排序都是从小到大进行排序
- 遍历数组,同时记得维护非重和count计数(以k为标准)
- 将数组元素存入Arraylist并返回
-
暴力法确实可以解决问题,但是老是靠暴力解决不了问题啊,就跟
现在的工作铲屎一样学不了东西 -
查阅资料发现可以用
优先队列
来完成这题 -
优先队列PriorityQueue
-
标准定义:
PriorityQueue类在Java1.5中引入。PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。要求使用Java Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素。
-
作用:
优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)。这里牵涉到了大小关系,元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器(Comparator,类似于C++的仿函数)。
-
举两个例子:
- 作业系统中的调度程序,当一个作业完成后,需要在所有等待调度的作业中选择一个优先级最高的作业来执行,并且也可以添加一个新的作业到作业的优先队列中。
- 每日交易时段生成股票报告的应用程序中,需要处理大量数据并且花费很多处理时间。客户向这个应用程序发送请求时,实际上就进入了队列。我们需要首先处理优先客户再处理普通用户。在这种情况下,Java的PriorityQueue(优先队列)会很有帮助。
-
-
快速排序:
-
快速排序(QuickSort )是常用到的效率比较高的一种排序算法,在面试过程中也经常提及,出了冒泡再复习下这个快排算法。
-
执行步骤总结:
-
从数列中挑出一个元素,称为 “基准”(pivot);
-
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
-
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
十大经典排序算法动画与解析,看我就够了!(配代码完全版)–作者:五分钟学算法
-
-
图解执行步骤:
-
以数列
[6,1,2,7,9,3,4,5,10,8]
为例 -
具体过程如下图:
-
配合图片讲解算法:
-
Step1:
-
以数列首位(index == 0)为基准(pivot),right为最后一位(index == arr.length-1),left为第二位(index == 1),开始遍历;
-
从right开始,向左(index–)开始寻找比pivot小的元素,移动一位后left也开始向右(index++)移动寻找比pivot大的元素,若找到了他们要的便会停止移动,并交换L与R
-
如图:
- 5与7交换,4与9交换
-
当遍历至left == right之后,(L,R)与pivot交换
-
执行完毕后变为:
[3,1,2,5,4,6,9,7,10,8]
此时发现以原pivot(6)为分界线,做左边的数列都小于6,右边的数列都大于6
-
-
Step2:
- 将数列
[3,1,2,5,4,6,9,7,10,8]
以6分成左右两个小数列(命名为A、B) - 并分别对两个小组小数列进行类似Step1的操作
- A =>
[1,2,3,4]
- B =>
[7,8,9,10]
- 将数列
-
Step3:
- 合并起来即可
-
综上,其实就是一直在重复Step1上的步骤:找pivot与遍历,因此可以用递归来实现
-
-
-
代码解析:
//Java 代码实现 public class QuickSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); return
-