zoukankan      html  css  js  c++  java
  • 编程之美2.5——寻找最大的K个数

    一列数中寻找最大K个数。

    【思路】

    1.常规思路,排序,数前K个数。缺点:时间O(nlogn),总个数n和k差距大的时候,比如k=1,很不实用。

    如果k<=logn,用部分排序算法更快(选择排序和冒泡排序),时间O(n*k)。

    2.递归,把问题规模缩小。快排的思想是用一个数把数列分成两半,一半a小于该数,一半b大于该数,

    如果b的个数b.size小于k,则在a中找k-b.size个最大数,

    如果b的个数b.size大于k,则在b中找k个最大数。

    【code】//用伪代码翻译过来的code,不算自己写的= =

    void ipartition(vector<int> src, vector<int>& s1, vector<int>& s2)
    {
        int len=src.size();
        int p=src[rand()%len];//wow!
        for(int i=0; i<src.size(); i++){
            if(src[i]<p)
                s1.push_back(src[i]);
            else
                s2.push_back(src[i]);
        }
        //s1.size()<s2.size()?s1.push_back(p):s2.push_back(p);
    }
    
    vector<int> Kbig(vector<int> s, int k)
    {
        vector<int> ret;
        if(k<=0) return ret;
        if(s.size()<=k) return s;
        vector<int> s1;
        vector<int> s2;
        ipartition(s, s1, s2);
        if(s2.size()<k){
            //copy(v1.begin(),v1.end(),back_inserter(v2))
            vector<int> tmp=Kbig(s1, k-s2.size());
            copy(tmp.begin(),tmp.end(),back_inserter(s2));
            return s2;
        }
        else if(s2.size()>k){
            return Kbig(s2, k);
        }
        else
            return s2;
    }

    【注意】

    1.ipartition的部分,产生一个随机下标的方式:rand()%n,防止每次从s[0]开始划分会产生退化。

    2.原书有注释的那一行,是将s[0]和是s[rand()%n]交换,然后从i=1开始计数,最后把划分的界放在元素书较少的那一边,

    我觉得直接把用来划分的界s[rand()%n]位置不变,等于该值时随意放在哪边。

    3.vector没有append方法,即将一个vector整体连在另一个vector后面,这里用copy(tmp.begin(),tmp.end(),back_inserter(s2));实现。

    4.用数组和下标控制操作范围的接口似乎不太方便,故这里选用vector类型,如果遇到用数组的要求再转换。

    5.时间复杂度是O(n*logk),和k的大小有关,不需要全排序。

    【思路3】

    用小根堆,前k个数形成一个小根堆作为k个最大数的初始化,依次用第k+i个数和堆顶的数比较,如果比堆顶大,则堆顶设为当前数,并且调整成小根堆,再与下一个数比较。

    用小根堆的原因是,要把第K+i个数与最大K个数的最小数比较,每次替换最小的数,时间是O(N*K).

    【code】

    void min_heapify(int src[], int i, int length)
    {
        int left=i*2+1;
        int right=i*2+2;
        int smallist=i;
        if(left<length&&src[left]<src[smallist])
            smallist=left;
        if(right<length&&src[right]<src[smallist])
            smallist=right;
        if(smallist!=i){
            int temp=src[i];
            src[i]=src[smallist];
            src[smallist]=temp;
            min_heapify(src, smallist, length);
        }
    }
    void build_min_heap(int src[], int length)
    {
        for(int i=length/2; i>=0; i--)
            min_heapify(src, i, length);
    }
    void findKmax(int src[], int length, int k)
    {
        if(length<k){
            cout<<"error"<<endl;
            return;
        }
        int i;
        int *heap=new int[k];
        for(i=0; i<k; i++)
            heap[i]=src[i];
        build_min_heap(heap, k);
        while(i<length){
            if(src[i]>heap[0]){
                heap[0]=src[i];
                min_heapify(heap, 0, k);
            }
        }
        for(i=0; i<k; i++)
            cout<<heap[i]<<' ';
        delete heap [];
    }

    【总结】

    1.堆代码分为两部分:调整堆、建立堆

    2.堆的存储结构是数组(向量也行吧),用下标规律表示父子关系。

    (此代码未经验证)

  • 相关阅读:
    常见寻找OEP脱壳的方法
    Windows内核原理系列01
    HDU 1025 Constructing Roads In JGShining's Kingdom
    HDU 1024 Max Sum Plus Plus
    HDU 1003 Max Sum
    HDU 1019 Least Common Multiple
    HDU 1018 Big Number
    HDU 1014 Uniform Generator
    HDU 1012 u Calculate e
    HDU 1005 Number Sequence
  • 原文地址:https://www.cnblogs.com/ketchups-notes/p/4505946.html
Copyright © 2011-2022 走看看