您的位置:首页 > Web前端

剑指offer-最小的K个数

2018-03-28 15:24 225 查看

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

地址: 牛客地址

问题分析

方法1:对数组先排序,然后取前K个数。时间复杂度:O(NlogN)

方法2:利用冒泡排序每一趟都能找到最小的元素,然后走K趟便能找到最小的K个数。时间复杂度: O(K*N)

方法3:建立一个容量为K大根堆,先将数组的前K个元素入堆,然后对于数组后面的元素,依次与大根堆的堆顶元素(也就是K个值中的最大值)进行比较,若小于堆顶元素,则将堆顶元素出堆,将当前元素入堆。这样,便会淘汰掉较大的元素,最终,堆中维持的便是最小的K个数字,最差情况下,每个元素都要入堆,时间复杂度:O(NlogK)

方法4:因为 Partition 过程最终能返回 pivot 在数组中的位置 pIndex , pIndex 之前的所有元素都小于等于 pivot ,pIndex之后的所有元素都大于等于 pivot, 所以,如果 pivot能够处于 K - 1处,即 pIndex = K - 1,那么【0~pIndex】便是该数组中最小的K个数。

问题来了,如何能使pivot处于 K - 1处,即 pIndex = K - 1呢?

不断迭代,若pIndex 大于 K - 1,说明 【0~pIndex】中是该数组最小的pIndex + 1(大于 K)个数,已经大于K个了,无法确定最小的K个数,需要缩小范围,那么 end = pIndex - 1,对【begin~end】范围内重新确定pIndex。

若pIndex 小于于 K - 1,说明 【0~pIndex】中是该数组最小的pIndex + 1(小于 K)个数,说明还不足K个最小的元素,需要继续在右边找,那么 begin = pIndex +1,对【begin~end】范围内重新确定pIndex.

不断迭代,直至pIndex等于 K-1

经验方法:

java中的是用优先级队列 PriorityQueue,可以实现指定大小的大根堆或者小根堆。构造方法中若是不传入比较器,那么默认是小根堆。要想实现大根堆,必须传入比较器。比较器有以下几种传法

PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, Collections.reverseOrder());


新建一个类,实现Comparable接口重写里面的 compare()方法,改变比较规则

传入匿名内部类,和上一个类似

PriorityQueue有以下几个方法:add, remove,poll,peek

PriorityQueue 同样可以用 foreach,具体见实现

若数组中无重复元素,利用 Partition过程可以找到第K大的数字,当然,用堆也可以实现

该题是找最小的K个数,若找最大的K个数, 第K小的数,第K大的数,都类似思想

注意该题与剑指offer-数组中出现次数超过一半的数字联系

代码实现

方法1:

public ArrayList<Integer> GetLeastNumbers_Solution2(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<>();
if (input == null || input.length == 0 || k > input.length || k <= 0) {
return res;
}
//对数组排序
Arrays.sort(input);
//取前k个数
for (int i = 0; i < k; i++) {
res.add(input[i]);
}
return res;
}


方法2:

public ArrayList<Integer> GetLeastNumbers_Solution3(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<>();
if (input == null || input.length == 0 || k > input.length || k <= 0) {
return res;
}
//冒泡排序:只找出来前k个数即可
for (int i = 0; i < k; i++) {
for (int j = input.length - 2; j >= i; j--) {
if (input[j] > input[j + 1]) {
int temp = input[j];
input[j] = input[j + 1];
input[j + 1] = temp;
}
}
}
for (int i = 0; i < k; i++) {
res.add(input[i]);
}
return res;
}


方法3:

public ArrayList<Integer> GetLeastNumbers_Solution1(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<>();
if (input == null || input.length == 0 || k > input.length || k <= 0) {
return res;
}
//创建大根堆的方式1:通过传入Collections.reverseOrder()
//PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, Collections.reverseOrder());
//创建大根堆的方式2:传入自定义的类对象(该类继承Comparator接口,重写compare方法,实现了从大到小排序)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, new MaxHeapComparator());
//先填满这个大根堆
for (int i = 0; i < k; i++) {
maxHeap.add(input[i]);
}

for (int i = k; i < input.length; i++) {
//如果元素小于最大堆的堆顶元素(这k个值中的最大值),那么弹堆,将该元素进堆
if (input[i] < maxHeap.peek()) {
maxHeap.poll();
maxHeap.add(input[i]);
}
}
//将堆中元素装入list
// res.addAll(maxHeap);

for (Integer num : maxHeap) {
res.add(num);
}

return res;
}

public class MaxHeapComparator implements Comparator<Integer> {
//compare方法,返回-1时,排列顺序为o1,o2;返回1时,排列顺序为o2,o1
public int compare(Integer o1, Integer o2) {
/*
if (o1 > o2) {
return -1;
}else {
return 1;
}
*/
return o2.compareTo(o1);
}
}


方法四:

public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {

b6b3
ArrayList<Integer> res = new ArrayList<>();
if (input == null || input.length == 0 || k > input.length || k <= 0) {
return res;
}
//利用partition过程使pivot停在 k-1处,那么【0~k-1】便是最小的K个数
int begin = 0;
int end = input.length - 1;
int pIndex = partition(input, begin, end);
while (pIndex != k - 1 ) {
if (pIndex > k - 1 ) {
end = pIndex - 1;
pIndex = partition(input, begin, end);
}
if (pIndex < k - 1) {
begin = pIndex + 1;
pIndex = partition(input, begin, end);
}
}
for (int i = 0; i < k; i++) {
res.add(input[i]);
}
return res;
}

public int partition(int[] arr, int begin, int end) {
int pivot = arr[begin];
int i = begin + 1;
int j = end;
while (true) {
while (i <= end && arr[i] < pivot) {
i++;
}
while (j >= begin + 1 && arr[j] > pivot) {
j--;
}
if (i < j && i <= end && j >= begin + 1 ) {
swap(arr,i,j);
i++;
j--;
}else {
break;
}
}
swap(arr,begin,j);
return j;
}

public void swap(int[] arr, int i ,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息