zoukankan      html  css  js  c++  java
  • 寻找最大的K个数之堆排序(1)

    一.问题:假设有一个含有n个不同元素的数组S,现在要求寻找出数组S中前K大的元素。

    利用堆排序算法解决这个问题,有两个思路:

    1.利用大根堆排序“每一趟都能产生1个最大值”的特性,来做K趟堆排序,则可找出这个前K大的元素

    2.首先取出数组S中的前K个元素,利用这K个元素,建立小根堆,然后利用小根堆产生的最小值与剩余的S-K个元素相比较,如果后者大,则交换他们的位置,否则不变,这样也能产生前K大的元素。

    二.分别实现这两种思路

    回忆一下堆排序:1.自底向上修复原始堆(建堆) 2.取堆中最大的叶子取带堆的根元素,并输出根元素 3.自顶向下的修复根堆

    其中最重要的子方法:以某个节点开始进行堆修复,以大根堆为例,大根堆具备这样的性质:根节点一定比其孩子结点大。

    //大根堆修复(递归算法):
    1 void max_heapfy(vector<int> &vec,int i) // 时间复杂度为O(h),其中h为以i为根的子树的高度
    2  {
    3 int largest;
    //getLeft(i):获得左孩子
    4 if(getLeft(i)<vec.size()&&vec[getLeft(i)]>vec[i])
    5 largest = getLeft(i);
    6 else
    7 largest = i;
    //getRight(i):获得右孩子
    9 if(getRight(i)<vec.size()&&vec[getRight(i)]>vec[largest])
    10 largest = getRight(i);
    11 if(largest!=i)
    12 {
    13 swap(vec,i,largest); //交换数组中的vec[i]和vec[largest]
    14 max_heapfy(vec,largest); //修复可能遭到破环的子树结构
    15 }
    16 }

    //初始建堆的过程
    1 void build_heap(vector<int> &vec)
    2 {
    3 for(int start = vec.size()/2-1;start!=-1;start--)
    4 max_heapfy(vec,start);
    5 }
    //主函数
    1 void main()
    2 {  
    初始化vec...

    3 build_heap(vec); //ln(n)
    4   while(vec.size()!=1) //n次循环
    5   {
    6 cout<<vec[0]<<endl;
    7 swap(vec,0,vec.size()-1);
    8 vec.pop_back();
    9 max_heapfy(vec,0); //ln(n)
    10   }
    11 }

    易知时间复杂度:ln(n)+n[ln(n)] 空间复杂度:o[n]

    下面我们来改进算法,使之满足我们的要求

    思路一:我们只需要在主程序里将循环的次数从N变为K即可,时间复杂度:ln(n)+K[ln(n)],不再罗列代码,很明显,初始建堆的过程显得很冗余。

    思路二:

    //修改后的大根堆修复:
    1 void max_heapfy(vector<int> &vec,int i,int k) // O(h)
    2  {
    3 int largest;
    4 if(getLeft(i)<k&&vec[getLeft(i)]>vec[i])
    5 largest = getLeft(i);
    6 else
    7 largest = i;
    8
    9 if(getRight(i)<k&&vec[getRight(i)]>vec[largest])
    10 largest = getRight(i);
    11 if(largest!=i)
    12 {
    13 swap(vec,i,largest);
    14 max_heapfy(vec,largest);
    15 }
    16 }
    17  

    //修改初始建堆过程:
    1 void build_heap_k(vector<int> &vec,int k)
    2 {
    3 if(k<=vec.size())
    4 return;
    5 for(int start=k/2-1;i!=-1;k--)
    6 max_heapfy(vec,start,k);
    7 }

    //更改主程序:
    1 void main()
    2 {
    3 build_heap_k(vec,k);
    4 for(int i=k+1;k<vec.size()-k;k++)
    5 {
    6 if(vec[k+1]<vec[0])
    7 swap(vec,0,k+1);
    8 max_heapfy(vec,0,k);
    9 }
    10 }
    时间复杂度:ln(k)+(n-k)ln(k) 明显的比思路的一的方法快,但是思路一的方法,给出的前K个数是按照顺序排列的


  • 相关阅读:
    java 在线网络考试系统源码 springboot mybaits vue.js 前后分离跨域
    springboot 整合flowable 项目源码 mybiats vue.js 前后分离 跨域
    flowable Springboot vue.js 前后分离 跨域 有代码生成器 工作流
    Flowable 工作流 Springboot vue.js 前后分离 跨域 有代码生成器
    java 企业 网站源码 后台 springmvc SSM 前台 静态化 代码生成器
    java 进销存 商户管理 系统 管理 库存管理 销售报表springmvc SSM项目
    基于FPGA的电子计算器设计(中)
    基于FPGA的电子计算器设计(上)
    FPGA零基础学习:SPI 协议驱动设计
    Signal tap 逻辑分析仪使用教程
  • 原文地址:https://www.cnblogs.com/cherri/p/1902886.html
Copyright © 2011-2022 走看看