剑指offer-30.最小的K个数

https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&tPage=2&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

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

输出最小的k 个数不一定要求有序。

题解:
方法一:
把输入的 n 个数排序,排序后位于最前面的 k 个数就是最小的 k 个数。
时间复杂度为 O(nlogn)

方法二:
利用快排的思想,通过多轮快排,数组下标从0开始,确定出第 k-1 个位置上的数,则位于它左边的数都比他小,位于它右边的数都比他大。则0到 k-1 位置上的数字就是最小的k 个数,不保证有序。
时间复杂度 O(n)

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> result=new ArrayList<>();
        if (input == null || input.length == 0 || k <= 0 || k > input.length ) {
            return result;
        }
        int x=input[k-1];
        int start=0;
        int end=input.length-1;
        int index=quickGetPosition(input,start,end);//确定一趟快排后,哪个位置的元素确定了
        while(index!=k-1){// 确定的不是位置 k-1 上的元素 
            if(index>k-1){//如果index在k-1 的右边,则从index左边继续找
                end=index-1;
                index=quickGetPosition(input, start, end);
            }else{//如果index在k-1 的左边,则从index右边继续找
                start=index+1;
                index=quickGetPosition(input, start, end);
            }
        }
        for(int i=0;i<k;i++){// 将0 到k-1 位置上的数字将入result
            result.add(input[i]);
        }
        return result;
    }
    private int quickGetPosition(int[] array, int start, int end) {
        if (start < end) {
            int x = array[start];
            while (start < end) {
                while (start < end && array[end] > x) {
                    end--;
                }
                if (start < end) {
                    array[start] = array[end];
                    start++;
                }
                while (start < end && array[start] < x) {
                    start++;
                }
                if (start < end) {
                    array[end] = array[start];
                    end--;
                }
            }
            array[start] = x;
        }
        return start;
    }
}

方法三:

用最大堆,设置容量为k, 如果最大堆已有元素小于 k 个,直接加入,如果等于k 个,则比较堆顶元素,是否大于 当前遍历元素,如果大于,则弹出堆顶元素,将改元素加入最大堆 。最后最大堆中的元素就是数组中最小的 k 个元素。
对最大堆的插入和删除元素,时间复杂度为 O(logk),获得堆顶元素时间复杂度为O(1)
对于n个元素的数组,总的时间复杂度为 O(nlogk)

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Comparator;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> result = new ArrayList<>();
        if (input == null || input.length == 0 || k <= 0 || k > input.length) {
            return result;
        }
        Comparator<Integer> comparator = new Comparator<Integer>() {// 降序
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        };
        Queue<Integer> queue = new PriorityQueue<Integer>(k, comparator);// 最大堆
        for (int i = 0; i < input.length; i++) {
            if (queue.size() < k) {
                queue.add(input[i]);
            } else {
                if(queue.peek()>input[i]){
                    queue.poll();
                    queue.add(input[i]);
                }
            }
        }
        while(!queue.isEmpty()){
            result.add(queue.poll());
        }
        return result;
    }
}

两种算法比较:

~ 基于quickGetPosition函数 基于最大堆
时间复杂度 O(n) O (nlogk)
是否需要修改输入数组
是否适用于海量数据

方法三虽然时间复杂度大了点,但是不用修改输入数组,且适合用于海量数据。
对于n 很大的海量数据,不能一次性载入内存,我们可以从硬盘中一次读入一个数字,判断是否放入 最大堆中,内存只要能容纳最大堆即可,因此方法三适用于n 很大并且k 较小的情况。

猜你喜欢

转载自blog.csdn.net/zxm1306192988/article/details/81092389
今日推荐