力扣链接:面试题 17.14. 最小k个数 - 力扣(leetcode)
题目描述:
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
<strong>输入:</strong> arr = [1,3,5,7,2,4,6,8], k = 4 <strong>输出:</strong> [1,2,3,4]
思路:
这个问题属于是一类问题中,即top-k问题:n个数据中,前k个最大/最小的元素,一般来说k比较小;或者是需要找到这组数据中 第k大/第k小 的数据。
根据这道的要求,我们可以有以下三种思路:
整体排序
整体建立一个大小为n的小根堆
把前k个元素创建为大根堆,遍历剩下的n-k个元素,和堆顶元素比较,如果比堆顶元素学校,则堆顶元素删除,但前元素入堆
具体实现
整体建立一个大小为n的小根堆
通过创建一个小根堆,把要全部元素都放进去,然后再把前k个元素提出来即可。
class solution { public int[] smallestk(int[] arr, int k) { priorityqueue<integer> priorityqueue = new priorityqueue<>(); for(int i = 0; i < arr.length; i++){ priorityqueue.offer(arr[i]); } int[] ret = new int[k]; for(int i = 0; i < k; i++){ ret[i] = priorityqueue.poll(); } return ret; } }
由priorityqueue创建的堆默认为小根堆,所以把元素直接放进去,priorityqueue会默认成为小根堆,然后再把前k个元素放到ret数字里即可。
通过大根堆实现
这里有一个要做的地方:让priorityqueue可以实现大根堆。
通过 按住crtl 鼠标点击 priorityqueue 可以看到其中实现的方法,再crtl 鼠标点击 comparator,看comparator接口中的方法,
可以看到其中有个 compare方法,这便是通过比较 o1,o2的值来进行小根堆的实现,这里我们可以通过重写compare方法来实现大根堆。这里选择的是创建一个新类来实现。
class intcmp implements comparator<integer> { @override public int compare(integer o1, integer o2) { return o2.compareto(o1); } }
然后把前k个元素放进大根堆,如果根节点的值大于可能要放进来的值,则把根节点删除,把该值放进来,同时priorityqueue会保证该堆一直为大根堆。最后遍历完n-k个值后,再把这些值返回出去。
其中的过程大概如上图所示。
class solution{ public int[] smallestk(int[] arr, int k) { int[] ret = new int[k]; if(arr == null || k == 0) return ret; priorityqueue<integer> priorityqueue = new priorityqueue<>(new intcmp()); for (int i = 0; i < k; i++) { priorityqueue.offer(arr[i]); } for (int i =k; i < arr.length; i++) { int peekval = priorityqueue.peek(); if(peekval > arr[i]) { priorityqueue.peek(); priorityqueue.offer(arr[i]); } } for (int i = 0; i < k; i++) { ret[i] = priorityqueue.poll(); } return ret; } }
完整代码
第一种方法,通过小根堆实现
//时间复杂度为:o((k+1)logn) class solution { public int[] smallestk(int[] arr, int k) { priorityqueue<integer> priorityqueue = new priorityqueue<>(); //时间复杂度为o(n*logn) for (int i = 0; i < arr.length; i++) { priorityqueue.offer(arr[i]); } //时间复杂度为o(k*logn) int[] ret = new int[k]; for (int i = 0; i < k; i++) { ret[i] = priorityqueue.poll(); } return ret; } }
第二种方法,通过大根堆实现
class intcmp implements comparator<integer> { public int compare(integer o1, integer o2) { return o2.compareto(o1); } } class solution{ public int[] smallestk(int[] arr, int k) { int[] ret = new int[k]; if(arr == null || k == 0) return ret; priorityqueue<integer> priorityqueue = new priorityqueue<>(new intcmp()); for (int i = 0; i < k; i++) { priorityqueue.offer(arr[i]); } for (int i =k; i < arr.length; i++) { int peekval = priorityqueue.peek(); if(peekval > arr[i]) { priorityqueue.peek(); priorityqueue.offer(arr[i]); } } for (int i = 0; i < k; i++) { ret[i] = priorityqueue.poll(); } return ret; } }
总结
到此这篇关于java刷题之最小k个数的思路及具体实现的文章就介绍到这了,更多相关java算法题最小k个数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论