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.堆的存储结构是数组(向量也行吧),用下标规律表示父子关系。

    (此代码未经验证)

  • 相关阅读:
    jenkins自动化部署前端项目
    jenkins自动部署java项目
    Jenkins插件修改为国内源
    jenkins搭建以及初始化
    gitlab-ci集成sonarqube完成代码检测
    微信小程序模板消息后端代码
    解决上传图片到服务器 水印文字显示框框不显示文字的问题
    ES transport client使用
    签发的用户认证token超时刷新策略
    api权限管理系统与前后端分离实践
  • 原文地址:https://www.cnblogs.com/ketchups-notes/p/4505946.html
Copyright © 2011-2022 走看看