堆的应用

 

“堆”定义

  n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):

(1) ki≤K2i且ki≤K2i+1 //小顶堆

(2)Ki≥K2i且ki≥K2i+1(1≤i≤ n) //大顶堆

//ki相当于二叉树的非叶结点,K2i则是左孩子,k2i+1是右孩子

树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构则堆实质上是满足如下性质的完全二叉树:

“堆的高度”

堆可以被看成是一棵树,结点在堆中的高度可以被定义为从本结点到叶子结点的最长简单下降路径上边的数目;定义堆的高度为树根的高度。我们将看到,堆结构上的一些基本操作的运行时间至多是与树的高度成正比,为O(lgn)。

堆在查找的时间复杂度为O(NlgN);

Oj上“序列合并”

有两个长度都为N的数列A和B,在A和B中各取一个数相加,可以得到N2个和,求这N2个和中最小的N个。

思路:先建立N个元素的堆,每次维持N个元素的堆,此题要建立的是大顶堆;

代码:

#include<stdio.h> 
int a[100005],b[100005],c[100005]; 
int n; 
void heapadjust(int s,int t) 
{ 
     int rc,i,j; 
     rc=c[s]; //暂存根节点
     i=s; 
     for(j=2*i;j<=t;j=2*j) 沿节点值较小的儿子往下层筛选,2*j是左儿子,2*j+1是右儿子
     { 
          if(j<t&&c[j]<c[j+1]) 如果右儿子大于左儿子,j++转移到右儿子
               j++; 
          if(rc>c[j]) 更改大小的地方
               break; 
          c[i]=c[j]; 较大的儿子向父节点平移,并更新i的值
          i=j; 
     } 
     c[i]=rc; //找到的位置是i,放进适合的位置;
} 
void heapsort(int n) 
{ 
     int i; 
     for( i=n;i>1;i--) 
     { 
          c[0]=c[1];交换堆顶和堆底
          c[1]=c[i]; 
          c[i]=c[0]; 
          heapadjust(1,i-1); 
     } 
} 
void heap(int n) 
{ 
     int i; 
     for( i=n/2;i>0;i--) //只需要算出n/2个数;且开始的位置是从n/2这个位置开始才行!
     { 
          heapadjust(i,n); 
     } 
} 

int main() 
{ 
     int i,j,sum,p; 
     scanf("%d",&n); 
     for(i=0;i<n;i++) 
          scanf("%d",&a[i]); 
     for(i=0;i<n;i++) 
          scanf("%d",&b[i]); 
     for(i=0;i<n;i++) 
     { 
          c[i+1]=a[0]+b[i]; 
     } 
     heap(n);//初始化建立n个元素的堆;建立的是大顶堆 
     p=n; 
     for(i=1;i<n;i++) 
     { 
          for(j=0;j<p;j++) 
          { 
               sum=a[i]+b[j]; 
               if(c[1]>sum) 
               { 
                    c[1]=sum; 
                    heapadjust(1,n); 
               } 
               else 
               { 
                    p=j; 
                    break; 
               } 
          } 
     } 
     heapsort(n); 
     for(i=1;i<=n;i++) 
          printf("%d ",c[i]); 
     printf("\n"); 
     return 0; 
}


成都赛区网络赛“第k大的数”水题!

思路:建立k个元素的小顶堆,每次保持k个元素的堆,比较堆顶元素的大小,更新堆。

AC代码:时间:46MS

#include<stdio.h>
int c[1000005];
void heapadjust(int s,int t)
{
    int rc,i,j;
    rc=c[s];
    i=s;
    for(j=2*i;j<=t;j=j*2)
    {
        if(j<t&&c[j]>c[j+1])
            j++;
        if(rc<c[j])//父亲节点小于孩子节点的话,跳出循环,找到合适的位置了
            break;
        c[i]=c[j];否则,更新i的位置
        i=j;
    }
    c[i]=rc;
}
void heap(int n)
{
    int i;
    for( i=n/2;i>0;i--)
    {
        heapadjust(i,n);初始建立堆;
    }
}
int main()
{
    int n,k,i,x;
    char ch;
    while(scanf("%d%d",&n,&k)!=EOF)//记得加上EOF,不然会超时。切记!!!
    {
        getchar();
        i=0;
        while(n--)
        {
            scanf("%c",&ch);
            getchar();
            if(ch=='I')
            {
                scanf("%d",&x);
                getchar();
                if(i<k)
                {
                    i++;
                    c[i]=x;
                    
                }
                if(i==k)
                {
                    heap(k);
                    i++;
                    continue;
                }
                if(c[1]<x&&i>k)
                {
                    c[1]=x;
                    heapadjust(1,k);
                }
            
            }
            else
            {
                printf("%d\n",c[1]);
            }
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值