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

题目描述
输入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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值