剑指offer——最小的K个数(堆排或partition,复习排序算法思想精髓)
2017-06-21 16:23
239 查看
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
思路:
维护一个由k个值组成的动态最大堆。然后接下来的每个数字和堆顶的数据比较,如果比堆顶的数据小,那就把数据放入堆中。最小堆内部不用严格排序,只要保证堆中最大的数字在堆顶即可。
或者直接快排,再把前n位数字输出。
另一种解法,基于partition的思想
因为快排每次都能得到基于分界点的数组,分界点左边的元素一定小于分界点上的元素。如果此分界点正好是k,则得到了最小的k个数。
最坏情况是,一直没有得到这个点,得到了一个部分排序的数组。但时间复杂度仍小于O(nlogn)
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
思路:
维护一个由k个值组成的动态最大堆。然后接下来的每个数字和堆顶的数据比较,如果比堆顶的数据小,那就把数据放入堆中。最小堆内部不用严格排序,只要保证堆中最大的数字在堆顶即可。
或者直接快排,再把前n位数字输出。
import java.util.*; public class Solution { public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { ArrayList<Integer> list = new ArrayList<>(); if(k<=0||k>input.length) return list; if(k>1){ // 这里先对前k个数进行堆排序,组成了一个最大堆(堆顶为最大值) for(int i = k/2-1; i>=0; i-- ){ heap(input,i,k); } } // 然后将剩下的元素插入最大堆,方法是与堆顶的元素进行比较,如果小于堆顶元素,即插入,并维护堆 for(int i = k; i<input.length; i++){ if(input[i]<input[0]){ input[0] = input[i]; heap(input,0,k); } } for(int i = 0; i<k; i++){ list.add(input[i]); } return list; } public void heap(int[] input, int i, int k){ int flag = i; if(2*i+1<k){ if(input[2*i+1]>input[flag]) flag = 2*i+1; } if(2*i+2<k){ if(input[2*i+2]>input[flag]) flag = 2*i+2; } if(flag!=i){ int temp = input[i]; input[i] = input[flag]; input[flag] = temp; heap(input,flag,k); } } }
另一种解法,基于partition的思想
因为快排每次都能得到基于分界点的数组,分界点左边的元素一定小于分界点上的元素。如果此分界点正好是k,则得到了最小的k个数。
最坏情况是,一直没有得到这个点,得到了一个部分排序的数组。但时间复杂度仍小于O(nlogn)
< 4000 span class="hljs-keyword">public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { ArrayList<Integer> list = new ArrayList<>(); if (k <= 0 || k > input.length) return list; int start = 0; int end = input.length-1; int index = partition(input,start,end); while(index!=k){ // 把判断写在主函数,很好! if(index>k){ end = index-1; index = partition(input,start,end); } if(index<k){ start = index+1; index = partition(input,start,end); } } for(int i = 0; i<k; i++){ list.add(input[i]); } return list; } public int partition(int[] input, int start, int end){ if(start>=end) return start; int flag = input[end]; while(start<end){ while(input[start]<=flag&&start<end) ++start; int temp = input[start]; input[start] = input[end]; input[end] = temp; while(input[end]>=flag&&end>start) --end; temp = input[start]; input[start] = input[end]; input[end] = temp; } return start; }
相关文章推荐
- 剑指offer:旋转数组中的最小数字
- 剑指Offer:面试题30 最小的k个数
- 剑指offer 面试题8 旋转数组的最小数字
- 剑指Offer-29-数组出现次数超过一半的数字-基于Partition函数的O(n)算法
- 剑指offer 最小的K个数
- 剑指offer:最小的K个数
- 剑指offer面试题33:把数组排成最小的数
- 牛客_剑指offer_重建二叉树,再后续遍历_递归思想_分两端
- 【剑指Offer学习】【面试题8 : 旋转数组的最小数字】
- 【剑指offer】把数组排成最小的数
- 剑指offer 面试题33—把数组排成最小的数
- 剑指offer面试题[8]-旋转数组的最小数字
- 剑指Offer_面试题30_最小的k个数
- (二十二)剑指offer之最小的k个数
- 剑指offer:定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数
- 剑指offer_输入n个整数,找出其中最小的K个数
- 剑指offer面试题8:旋转数组的最小数字
- 剑指offer——扑克牌顺子(思路,bitmap思想,位运算)
- 【剑指Offer-时间效率】最小的k个数
- 【剑指offer-Java版】33把数组排成最小的数