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

【思路】很多人都觉得这个问题是一个排序问题,但我觉得不一定要排序啊,可以使用堆结构(最小堆),首先将所有的元素都压入到最小堆中,每次弹出最小值就好了,一共弹出k个数。直接使用STL库中的堆结构,也就是优先级队列!代码就非常简洁了!

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        priority_queue<int, vector<int>, greater<int>> pq;
        vector<int> res;
        if(input.size() < k) return res;  // 返回数值内存大于输入内存,则返回空
        for(auto i: input){
            pq.push(i);
        }
        while(k--){
            res.push_back(pq.top());
            pq.pop();
        }
        return res;
    }
};

当然,只会用库在面试官面前是不行的,接下来我们手动使用vector实现一个最小堆!
文章链接: 从底层实现堆结构和堆排序

这里面我将上面文章中的最大堆改成了最小堆,右一个细节就是:heapify中有一个left+1的边界,如果不满足这个边界,那么必须返回left,而不是left+1。

class Solution {
public:
    void heapInsert(vector<int>& list, int index){
        while(list[index] < list[(index-1)/2]){
            swap(list[index], list[(index-1)/2]);  // 与根节点交换
            index = (index-1)/2;   // 当前位置更新
        }
    }

    // 改变某个值,仍然是最小堆结构
    void heapify(vector<int>& list, int index, int heapSize){
        int left = index*2+1;
        while(left < heapSize){
            int mini = (left + 1) < heapSize && list[left] > list[left+1]
                ? left + 1 : left; 
            // 这个判断错误时只能是left,由于left+1可能出了索引范围
            mini = list[mini] < list[index] ? mini : index;
            if(mini == index){
                break;
            }
            swap(list[mini], list[index]);
            index = mini;
            left = index*2+1;
        }
    }

    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> res;
        if(input.size() == 0) return res;
        if(input.size() < k) return res;
        for(int i = 0;i < input.size(); i++){
            heapInsert(input, i);
        }
        int heapSize = input.size();
        while(k--){
            res.push_back(input[0]);
            swap(input[0], input[--heapSize]);
            heapify(input, 0, heapSize);
        }
        return res;
    }
};