题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路:
方法一:和上一个题目“数组中次数超过一半的数字”类似,平均时间复杂度是O(n),找出来的k个数字不一定是排序的,而且会修改输入的数组
注意:每次去一半的数组去寻找就好
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<Integer>();
int len = input.length;
//边界的问题
if(k>len || k<=0 || len ==0) return res;
kuaipai(res , input , k , 0 , len-1);
return res;
}
public void kuaipai(ArrayList<Integer> res , int [] input , int k, int i , int j){
int pos = getPos(input , i , j );
if(pos ==(k-1)){
//把前k个放到res中,然后返回
for(int t = i;t<k;t++){
res.add(input[t]);
}
return ;
}
else if(pos <(k-1)){
//把前pos个数放到res中,然后再去后半部分找
for(int t = i;t<=pos;t++){
res.add(input[t]);
}
kuaipai(res , input , k , pos+1,j);
}else if(pos >(k-1)){
//去前半部分找
kuaipai(res , input , k , i , pos-1);
}
}
public int getPos(int [] input , int low , int high){
int i = low ;
int j = high;
int flag = input[low];
while(i<j){
while(i<j && input[i]<flag) i++;
while(i<j && input[j]>flag) j--;
swap(input , i , j);
}
if(input[i]>input[low]) i--;//就这里错了,这里一定要是> , 而不是>=
swap(input , i , low);
return i ;
}
public void swap(int [] input , int i , int j ){
int t = input[i];
input[i] = input[j];
input[j] = t;
}
}
方法二:O(nlogk)的算法,特别适合处理海量数据
用最大堆保存这k个数,如果新的数字比堆顶小,新数入堆。
注意:java中的优先队列是基于堆实现的
抄一下别人的代码实现:
链接:https://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf
来源:牛客网
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> result = new ArrayList<Integer>();
int length = input.length;
if(k > length || k == 0){
return result;
}
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
for (int i = 0; i < length; i++) {
if (maxHeap.size() != k) {
maxHeap.offer(input[i]);
} else if (maxHeap.peek() > input[i]) {
Integer temp = maxHeap.poll();
temp = null;
maxHeap.offer(input[i]);
}
}
for (Integer integer : maxHeap) {
result.add(integer);
}
return result;
}
}
答主的方法时间复杂度只有O(nlogk), 因为堆中总共k个数,每插入或删除一个数,都要调整一次堆,即时间复杂度为O(logk), 共循环n次,插入的次数最坏情况为n次,删除为n-k次,而平均下来插入次数也不少,所以大致估计时间复杂度就在nlogk
因为compareTo方法代表的是指定数如果大于参数的话那么则返回1,实现了comparator中重写compare方法,如果返回值是正数的话,那么就让第二个参数在前面,如果是负数的话,就让第一个数在前面。在这个方法中,如果o2大于o1那么返回1,就让o2在前面,就成为了最大堆
https://www.cnblogs.com/yongh/p/9945539.html
这个博客写的很清楚:
【Java】 用PriorityQueue实现最大最小堆
PriorityQueue(优先队列),一个基于优先级堆的无界优先级队列。
实际上是一个堆(不指定Comparator时默认为最小堆),通过传入自定义的Comparator函数可以实现大顶堆。
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(); //小顶堆,默认容量为11
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){ //大顶堆,容量11
@Override
public int compare(Integer i1,Integer i2){
return i2-i1;
}
});
PriorityQueue的常用方法有:poll(),offer(Object),size(),peek()等。
- 插入方法(offer()、poll()、remove() 、add() 方法)时间复杂度为O(log(n)) ;
- remove(Object) 和 contains(Object) 时间复杂度为O(n);
- 检索方法(peek、element 和 size)时间复杂度为常量。
PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
使用指定的初始容量创建一个 PriorityQueue
,并根据指定的比较器对元素进行排序。