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个数是按照顺序排列的


  • 相关阅读:
    Leastsq 最小二乘法拟合一次函数简单入门例子
    Matplotlab 基础画图
    plot(x,y1,'g',x,y2,'b--o',x,y3,'c*') 方法中对数据类型的要求和线条类型和颜色的设置
    [Java] 获取255年前芝加哥格式化时间
    [SoapUI] 在Assertion和Groovy Script里获取RequestId
    [SoapUI] 在脚本里写code执行某个step
    [SoapUI] 获取TestStep的状态
    [SoapUI] 判断失败继续执行
    [Java] 获取两个日期间的所有日期
    [Excel] excel中如何快速统计出各个项出现的次数?
  • 原文地址:https://www.cnblogs.com/cherri/p/1902886.html
Copyright © 2011-2022 走看看