剑指offer——最小的K个数(堆排或partition,复习排序算法思想精髓)

来源:互联网 发布:金山恢复数据恢复软件 编辑:程序博客网 时间:2024/05/21 12:20

题目描述
输入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)

    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;    }