【算法】最小的k个数

版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/82631155

面试题40:最小的k个数

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

解法1

还是用Partition函数,存在这样的划分,正好取到第k小的数字为基准数,能够使比其小的数字都在其左边,就找到了最小的k个数。

#include<bits/stdc++.h>
#include "../Utilities/Array.h"
using namespace std;

//输入长为n的input数组,将最小的k个数字写入长为k的output数组
void GetLeastNumbers_Solution1(int* input, int n, int* output, int k) {
    //非空校验,注意k是不能大于n的
    if(input == nullptr || output == nullptr || k > n || n <= 0 || k <= 0)
        return;
    //先从头到尾随机划分一下
    int start = 0;
    int end = n - 1;
    int index = Partition(input, n, start, end);
    //当划分到的位置不是第k个位置(从0开始下标k-1)
    while(index != k - 1) {
        if(index > k - 1) {//如果大了
            //就在左边继续划分着找
            end = index - 1;
            index = Partition(input, n, start, end);
        } else {//如果小了
            //就在右边继续划分着找
            start = index + 1;
            index = Partition(input, n, start, end);
        }
    }
    //找完以后,最左边的k个数字就是最小的k个数字
    for(int i = 0; i < k; ++i)
        output[i] = input[i];
}

int main() {
    int data[] = {4, 5, 1, 6, 2, 7, 3, 8};
    int out[4];
    GetLeastNumbers_Solution1(data,8,out,4);
    for(int i=0;i<4;i++)
        cout<<out[i]<<"\t";
    return 0;
}
解法2

解法1需要整个数组都读出来,解法2可以边读边去检查处理。适合处理海量数据,因为海量数据没法一次读到内存里。

因为只要有能保存k个小数字的容器把它保存下来就可以了,可以用能容纳k个数字的最大堆,如果在堆满了以后读到比堆顶更小的,就把它替换下来,这样最终堆里的k个数字就是最小的k个数。

书上指出堆不容易实现,可以用实现了红黑树的容器。红黑树的插入、删除、查找都是O(logk)的时间,使用STL中的multisetset也实现了RBT,但是它是不允许元素重复的,而multiset允许。

#include<bits/stdc++.h>
#include "../Utilities/Array.h"
using namespace std;

//typedef用来声明别名:"typedef 复杂的东西 别名;"
//greater表示内置类型从大到小排序,即第一个元素一定是最大的
//set和multiset会根据特定的排序原则将元素排序,这里即模拟了最大堆
typedef multiset<int, greater<int> >            intSet;
typedef multiset<int, greater<int> >::iterator  setIterator;

//传入数组,取最小的k个元素的multiset容器,k的值
//找到数组中最小的k个元素,放入容器中供调用者使用
void GetLeastNumbers_Solution2(const vector<int>& data, intSet& leastNumbers, int k) {
    leastNumbers.clear();//清空容器
    //检查输入合法性
    if(k < 1 || data.size() < k)
        return;
    //从数组前面开始
    vector<int>::const_iterator iter = data.begin();
    for(; iter != data.end(); ++ iter) {//向后遍历整个数组
        if((leastNumbers.size()) < k)//如果容器还没满
            leastNumbers.insert(*iter);//直接插入容器中
        else {//如果容器已经满了
            //取排序multiset的第一个元素,即逻辑最大堆的堆顶
            setIterator iterGreatest = leastNumbers.begin();
            //如果当前这个数比最大堆堆顶还小
            if(*iter < *(leastNumbers.begin())) {
                leastNumbers.erase(iterGreatest);//把堆顶移出去
                leastNumbers.insert(*iter);//把它放进来
            }
        }
    }
}

int main() {
    int data[] = {4, 5, 1, 6, 2, 7, 3, 8};
    //写入vector数组 
    vector<int> vectorData;
    for(int i = 0; i < 8; ++ i)
        vectorData.push_back(data[i]);
    //multiset容器 
    intSet leastNumbers;
    GetLeastNumbers_Solution2(vectorData,leastNumbers,4);
    //在这个容器里是从大到小有序的,使用真的最大堆时不能保证有序 
    for(setIterator iter = leastNumbers.begin(); iter != leastNumbers.end(); ++iter)
        cout<<*iter<<"\t";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/SHU15121856/article/details/82631155