堆 优先队列

来源:互联网 发布:ff14 猫女捏脸数据 编辑:程序博客网 时间:2024/05/07 23:05

  首先看完全二叉树的定义:

若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

  如果一个完全二叉树的某个结点值都不大于或小于他的父结点,那就叫做堆。根结点是所有结点里最大的叫大根堆,最小的叫小根堆。

  比如:

 9-1 a是大根堆,9-2 a是小根堆,剩下的都不是堆(因为不是完全二叉树)。

  那么怎么样才能构造出堆呢?

把一棵树结点按顺序标号

从后往前找到第一个有孩子的结点(肯定在倒数第二排,最后一排已经是堆),看以这个结点为根结点的树满不满足堆的条件,如果不满足就和下面的交换,直到满足堆的条件。就这么一个一个结点检查,一直到根结点,堆就完成了~

  比如到结点4(大根堆),15比17小,17没孩子,所以只要把15和17换就行了,此时结点4是17,结点8是15,再到结点2的时候,因为12比17小,所以把17移动到结点2,12又比15小,所以把15移动到结点4,最后把12移动到结点8。

  为什么要从后往前呢?这是因为在检查一个结点的时候,如果以他孩子为根结点的树已经是堆,就只要找到这个点应该插入的位置,把子结点都往上移一个就行了~

小根堆代码:

void heapadjust(int i,int l)    //检查结点函数,i是这个结点的位置,l是堆的结点数,设根结点为0
{
    int child,t;
    for(t=a[i]; i*2+1<l; i=child)  //i*2+1是左孩子,t是检查的结点的值
    {
        child=i*2+1;    //  i是父结点
        if(child+1<l&&a[child+1]<a[child]) child++;  //如果右孩子比左孩子小(取最小的孩子)
        if(t>a[child]) a[i]=a[child];   //如果最小的孩子比t小,就把那个孩子的值给父结点
        else break; 
    }
    a[i]=t;   //此时已经找到该放的位置,插入t
}
void heapsort(int l)
{
    int i,temp;
    for(i=l/2-1; i>=0; i--) heapadjust(i,l);  //从第1个有孩子的结点开始找,一直到根结点
}

  大根堆只要把符号改一下就行了,方法是一样的~


void maxheap_add(int x){    Lmax++;    int child=Lmax,fa=child>>1;    while(fa>0&&a[fa]<x){        a[child]=a[fa];        child=fa;        fa>>=1;    }    a[child]=x;}void minheap_add(int x){    Lmin++;    int child=Lmin,fa=child>>1;    while(fa>0&&b[fa]>x){        b[child]=b[fa];        child=fa;        fa>>=1;    }    b[child]=x;}void maxheap_adjust(int x){    int t=a[x],fa=x,child=fa<<1;    while(child<=Lmax){        if(child+1<=Lmax&&a[child+1]>a[child]) child++;        if(t<a[child]){            a[fa]=a[child];            fa=child;            child=fa<<1;        }        else break;    }    a[fa]=t;}void minheap_adjust(int x){    int t=b[x],fa=x,child=fa<<1;    while(child<=Lmin){        if(child+1<=Lmin&&b[child+1]<b[child]) child++;        if(t>b[child]){            b[fa]=b[child];            fa=child;            child=fa<<1;        }        else break;    }    b[fa]=t;}int maxheap_del(){    int t=a[1];    a[1]=a[Lmax];    Lmax--;    maxheap_adjust(1);    return t;}int minheap_del(){    int t=b[1];    b[1]=b[Lmin];    Lmin--;    minheap_adjust(1);    return t;}


  堆只能保证根结点最大或最小,其他元素并不一定是有序的,但只要每次把根结点和最后一个结点交换,再把剩下无序的结点调整成堆,再把根结点放到后面,再。。重复。。直到最后一个结点,就排完序了~

void heapsort(int l)
{
    int i,temp;
    for(i=l/2-1; i>=0; i--) heapadjust(i,l);
    for(i=l-1;i>0;i--){
    temp=a[0];             //交换最后一个和根结点
    a[0]=a[i];
    a[i]=temp;
    heapadjust(0,i);} //  这时除根结点,其他本来已经都是堆,只需检查根结点
}


  下面来说优先队列(也是刚发现这个东东。。原理就是堆吧),具体的我也说不好,就简单说下怎么用吧得意

  首先头文件里要有#include<queue>

最小值优先:priority_queue<int,vector<int>,greater<int> >q;

最大值优先:priority_queue<int,vector<int>,less<int> >q;

  第一个int就是int类型的,vector<int>就是默认的什么=。=我也搞不懂,写上就行了。。委屈,greater是最小值优先,less是最大值优先。


顺便说一下STL队列(不是优先队列)

queue <int> q;

Push():入队,即插入元素

Pop():出队,即删除元素

Front():读取队首元素

Back():读取队尾元素

Empty():判断队列是否为空

Size():队列当前元素

杭电4006

The kth great number

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 4790 Accepted Submission(s): 1962


Problem Description
Xiao Ming and Xiao Bao are playing a simple Numbers game. In a round Xiao Ming can choose to write down a number, or ask Xiao Bao what the kth great number is. Because the number written by Xiao Ming is too much, Xiao Bao is feeling giddy. Now, try to help Xiao Bao.

Input
There are several test cases. For each test case, the first line of input contains two positive integer n, k. Then n lines follow. If Xiao Ming choose to write down a number, there will be an " I" followed by a number that Xiao Ming will write down. If Xiao Ming choose to ask Xiao Bao, there will be a "Q", then you need to output the kth great number.

Output
The output consists of one integer representing the largest number of islands that all lie on one line.

Sample Input
8 3I 1I 2I 3QI 5QI 4Q

Sample Output
123
Hint
Xiao Ming won't ask Xiao Bao the kth great number when the number of the written number is smaller than k. (1=<k<=n<=1000000).

  刚开始做的是插入元素后就堆排序,输出时就输出a[k],怎么都超时。。后来发现。。只要只在数组里保存k个,在开始构造成一个最小堆,再插入时只要比较要插入的元素和堆的根结点哪个大,如果它比根结点大,就用它代替根结点,这时因为底下的都已经是堆,所以只要调用那个检查结点函数检查根结点就行~(并不需要每次都排序,只要保证堆中k个元素都是目前最大的k个,输出时直接输出根结点就行了(k个最大的里面的最小的))。这道题用优先队列更简单~

最小堆:

#include<stdio.h>int a[1000010];void heapadjust(int i,int l){    int child,t;    for(t=a[i]; i*2+1<l; i=child)    {        child=i*2+1;        if(child+1<l&&a[child+1]<a[child]) child++;        if(t>a[child]) a[i]=a[child];        else break;    }    a[i]=t;}void heapsort(int l){    int i,temp;    for(i=l/2-1; i>=0; i--) heapadjust(i,l);}int main(){    freopen("D:\\CodeBlocks\\file\\in.txt","r",stdin);    int n,k,t;    char s[5];    while(scanf("%d%d",&n,&k)!=EOF)    {        int l=k,i;        for(i=0; i<k; i++)            scanf("%s%d",s,&a[i]);        heapsort(k);        n-=k;        while(n--)        {            scanf("%s",s);            if(s[0]=='I')            {                scanf("%d",&t);                if(t>a[0])                {                    a[0]=t;                    heapadjust(0,k);                }            }            else            {                printf("%d\n",a[0]);            }        }    }    return 0;}

优先队列:

#include<iostream>#include<cstdio>#include<cstring>#include<queue>using namespace std;int main(){    freopen("D:\\CodeBlocks\\file\\in.txt","r",stdin);    int n,k,t;    char s[5];    while(scanf("%d%d",&n,&k)!=EOF)    {        priority_queue<int,vector<int>,greater<int> >q;        int i,t;        for(i=0; i<n; i++)        {            scanf("%s",s);            if(s[0]=='I')            {                scanf("%d",&t);                if(q.size()<k) q.push(t);                else                {                    q.push(t);                    q.pop();                }            }            else printf("%d\n",q.top());        }    }    return 0;}


原创粉丝点击