剑指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; }
相关文章推荐
- 剑指offer-----旋转数组的最小数字
- 【剑指offer】题8:旋转数组的最小值
- 剑指offer面试题 旋转数组的最小数字
- (C++)剑指offer-6:旋转数组的最小数字
- 剑指offer 把数组排成最小的数
- 剑指Offer旋转数组中的最小数字
- [牛客网,剑指offer,python] 旋转数组的最小数字
- 剑指offer-Java-旋转数组的最小数字
- 剑指offer:旋转数组中的最小数字
- 剑指Offer之旋转数组中的最小数字(题8)
- 剑指offer-求数组中最小的k个数
- 剑指offer的最小k个数
- 【剑指offer】之把数组排成最小的数
- 剑指Offer 33 把数组排成最小的数
- [剑指Offer] 6.旋转数组的最小数字(二分法)
- 剑指offer之旋转数组的最小数字
- 剑指offer——旋转数组的最小数字
- 剑指offer-----面试题30(最小的k个数)
- 剑指offer:最小的K个数
- 19.剑指offer-把数组排成最小的数