面试题30:最小的k个数(未)

面试题30:最小的k个数

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

题目分析:
找出最小的k个数,直观的解法从小到大排序,取前k个即可,时间复杂度是O(NlogN),显然方法不够好。

1. 时间复杂度O(N)的解法

思路1:
前面已经可以得出,采用基于快速排序Partition过程有时间复杂度O(N)的解法,最小的k个数,不需要有序,明白快速排序的话,就好说了,直接上代码。

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> res;
        int len = input.size();
        if (len < k || len == 0)
            return res;
        int left = 0;
        int right = len - 1;
        int pos = Partition(input, left, right);
        while (pos != k) {
            if (pos < k) {
                left = pos + 1;
                pos = Partition(input, left, right);
            } else {
                right = pos - 1;
                pos = Partition(input, left, right);
            }
        }
        for (int i = 0; i < k; i ++) {
            res.push_back(input[i]);
        }
        return res;
    }
    int Partition(vector<int> &nums, int left, int right) {
        int pivot = nums[left];
        while (left < right) {
            while (left < right && nums[right] >= pivot)
                -- right;
            nums[left] = nums[right];
            while (left < right && nums[left] < pivot)
                ++ left;
            nums[right] = nums[left]; 
        }
        nums[left] = pivot;
        return left;
    }
};

2. 堆排序

思路2:
堆排序很适合来求top k问题,尤其当数据量很大时,时间复杂度是O(Nlogk)。
先创建一个大小为 k 的数据容器来存储最小的 k 个数字,接下来我们每次从输入的 n 个整数中读入一个数.如果容器中已有的数字少于 k 个,则直接把这次读入的整数放入容器之中:如果容器中己有k 数字了,也就是容器己满,此时我们不能再插入新的数字而只能替换已有的数字。找出这己有的 k 个数中的最大值,然后这次待插入的整数和最大值进行比较。如果待插入的值比当前己有的最大值小,则用这个数替换当前已有的最大值:如果待插入的值比当前已有的最大值还要大,那么这个数不可能是最小的k个整数之一,于是我们可以抛弃这个整数。

因此当容器满了之后,我们要做 3 件事情: 一是在 k 个整数中找到最大数: 二是有可能在这个容器中删除最大数: 三是有可能要插入一个新的数字。我们可以使用一个大顶堆在 O(logk)时间内实现这三步操作。

实现代码1:

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> res;
        int len = input.size();
        if (len < k || len == 0)
            return res;
        int i;
        for (i = 0; i < k; i ++)
            res.push_back(input[i]);
         BuildMaxHeap(res, k);
        /* 从第k到最后,开始不断调整堆*/
        for (i = k; i < len; i ++) {
            if (input[i] < res[0]) {
                res[0] = input[i];
             AdjustHeap(res, 0, k);
            }
        }
        return res;
    }
    void BuildMaxHeap(vector<int> &nums, int len) {
        for (int i = len / 2 - 1; i >= 0; i --)
            AdjustHeap(nums, i, len);
    }
    void AdjustHeap(vector<int> &nums, int i, int len) {
        int child = 2 * i + 1;
        int parent = nums[i];
        while (child < len) {
            if (child + 1 < len && nums[child] < nums[child + 1])
                ++ child;
            if (parent < nums[child]) {
                nums[i] = nums[child];
                i = child;
                child = 2 * i + 1;
            } else {
                break;
            }
        }
        nums[i] = parent;
    }
};

不知道为什么在牛客网上会报错,段错误:您的程序发生段错误,可能是数组越界,堆栈溢出(比如,递归调用层数太多)等情况引起,没有找到错误地方。

实现代码2:
使用优先队列,优先队列的结构类似于堆,默认是最大堆,top()是最大堆的最大值,堆中的第一个元素,push(val)会自动调用push_heap来调整堆,pop()是将堆中的最大值出队列。

优先队列的模板声明带有三个参数,priority_queue< Type, Container, Functional>Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。Container 必须是用数组实现的容器,比如 vector, deque 但不能用 list.
STL里面默认用的是 vector. 比较方式默认用 operator< , 所以如果你把后面两个参数缺省的话,优先队列就是大顶堆,队头元素最大。

参考:
http://www.cplusplus.com/reference/queue/priority_queue/
http://www.cppblog.com/Darren/archive/2009/06/09/87224.html

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> res;
        if(k < 1 || k > input.size())
            return res;
        priority_queue<int> pq;
        int i = 0;
        int len = input.size();
        while (i < len) {
            if (pq.size() < k)
                pq.push(input[i]);
            else {
                if (pq.top() < input[i]) {
                    ++ i;
                    continue;
                } else {
                    pq.pop();
                    pq.push(input[i]);
                }
            }
            ++ i;
        }
        while (!pq.empty()) {
            res.push_back(pq.top());
            pq.pop();
        }
        return res;
    }
};

实现代码3:可以采用multiset,先写到这里了,回来补充。

猜你喜欢

转载自blog.csdn.net/zxc995293774/article/details/48978611