输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。html
显然最简答作法就是对原数组排序,取前k个就行。java
Note: 这里能够分状况的:面试
/** * 排序的作法 * @param input * @param k * @return */
public static ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input == null || k <= 0 || k > input.length) {
return result;
}
Arrays.sort(input);
for(int i = 0; i < k; i++) {
result.add(input[i]);
}
return result;
}
复制代码
咱们注意到题目并无要求输出的最小k个数必须是有序的,因此咱们能够利用快排中partion函数的思想来作作题。 由于partion可使得序列分为2部分:左边的值都小于哨兵,右边的值都大于哨兵。因此咱们只要找处处于第k位置的哨兵便可,也就是说找到第k大的值所在的位置便可,那么它的左边的k-1值都小于等于第k大值。显然,前k个值即为咱们所求的最小k个数。在咱们的划分过程有3种状况:算法
/** * 基于快排的划分函数的思想来作的。 * @param input * @param k * @return */
public static ArrayList<Integer> GetLeastNumbers_Solution2(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input == null || k <= 0 || k > input.length) {
return result;
}
findKthValue(input, 0, input.length - 1, k - 1);
for(int i = 0; i < k; i++) {
result.add(input[i]);
}
return result;
}
public static void findKthValue(int[] input, int low, int high, int k) {
if(low < high) {
int pivot = new Random().nextInt(high - low + 1) + low;
swap(input, pivot, high);
int index = low;
for(int i = low; i < high; i++) {
if(input[i] < input[high]) {
swap(input, i, index);
index++;
}
}
swap(input, index, high);
if(index > k) {
findKthValue(input, low, index - 1, k);
}else if(index < k) {
findKthValue(input, index + 1, high, k);
}
}
}
复制代码
这是典型的Top-K问题,即从n个数中找出最小的k个数或者最大的k个数问题。
咱们一般的作法用一个容量为k的容器来存放这k个最小的值。咱们只需遍历一遍原数组,就能获得最小的k个数。数组
问题转化为如何高效率获得容器中的最大值。一个优雅的数据结构完美的解决此题,即堆结构,分为大根堆或者小根堆。显然这里应该选择大根堆。在大根堆中,根节点大于左子树和右子树中全部点,因此咱们只需访问根节点便可获得k容量的最大值,且数据结构能够对插入的值进行动态调整堆结构,使得知足大根堆。关于堆的具体代码,之后我单独写一个博客,这里再也不累述了。
在Java中,没有专门的堆数据结果,不过有基于堆结构的优先队列,因此这里采用优先队列并自定义比较器,来知足大根堆的需求。数据结构
/** * Topk问题 * @param input * @param k * @return */
public static ArrayList<Integer> GetLeastNumbers_Solution3(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if(input == null || k <= 0 || k > input.length) {
return result;
}
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, new Comparator<Integer>() {
//由于要知足大根堆需求,因此使用自定义比较器,比较策略为o1大于o2时,o1放o2的前面
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for(int i = 0; i < input.length; i++) {
if(i < k) {
priorityQueue.add(input[i]);
} else if(input[i] < priorityQueue.peek()) {
priorityQueue.poll();
priorityQueue.add(input[i]);
}
}
result.addAll(priorityQueue);
return result;
}
复制代码
多结合排序算法和常见的数据结构来简化题目。dom
文章收录在[我的专栏(upadating)](https://blog.csdn.net/column/details/23876.html),期待与你一块儿KO常见面试题。