优先队列与堆

Posted 2018zxy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优先队列与堆相关的知识,希望对你有一定的参考价值。

1、定义:将优先级最高的元素先出队列的队列。

2、基本操作:入队(插入),出队(删除优先级最高的元素,代码中以元素值最小为优先级最高),构建堆,

修改元素等。

3、二叉堆:父节点小于子节点的完全二叉树。

性质:

(1)结构性:完全二叉树结构

(2)堆序性:父节点的值小于子节点的值

3、代码实现:

(1)堆的结构:用线性表顺序存储实现即可,不需要复杂的指针。

struct Node{
    int *data;
    int Capcity,size;
};
typedef struct Node* PriorityQueue;

 

(2)堆的插入(优先队列的入队):

先在数组后分配一个新的空间,然后寻找x要插入的位置,将x与大于它的父节点进行交换(上滤),

直到再次满足堆的堆序性。

void Insert(int x,PriorityQueue Q) //插入 
{
    if(IsFull(Q)){
        printf("The Queue is Full
");
        return ;
    }
    int i;
    for(i=++Q->size;Q->data[i/2]>x;i/=2) //上滤 
    Q->data[i]=Q->data[i/2];
    Q->data[i]=x;
}

 

(2)堆的删除最小值(优先队列的出队):

直接将头结点出队,然后不断向下调整,选取子节点中较小的值变为根节点,然后再调整改变较小的子节点(下滤),使堆重新有序。

int DeleteMin(PriorityQueue Q) //出队 
{
    if(IsEmpty(Q)){
        printf("The Queue is Empty!!!
");
        return -1;
    }
    int i,MI=Q->data[1],child,Last=Q->data[Q->size--];
    for(i=1;i*2<=Q->size;i=child) //下滤 
    {
        child=i*2;
        if(Q->size!=child&&Q->data[child+1]<Q->data[child]) child++;
        if(Last>Q->data[child]) Q->data[i]=Q->data[child];
        else break;
    }
    Q->data[i]=Last;
    return MI;
}

 

(3)堆的上滤和下滤:

上滤:从要调整的值开始,不断将当前值向上移动,直到位置合适(画图更好理解)。

void PrecolateUp(int pos,PriorityQueue Q) //上滤 
{
    int i,x=Q->data[pos];
    for(i=pos;Q->data[i/2]>x;i/=2)
    Q->data[i]=Q->data[i/2];
    Q->data[i]=x;
}

下滤:与上滤相反

void PrecolateDown(int pos,PriorityQueue Q) //下滤 
{
    int i,Last=Q->data[pos],child;
    for(i=pos;i*2<=Q->size;i=child)
    {
        child=i*2;
        if(child!=Q->size&&Q->data[child+1]<Q->data[child]) child++; //考虑是否有单个节点 
        if(Last>Q->data[child]) Q->data[i]=Q->data[child];
        else break;
    }
    Q->data[i]=Last;
}

 

(4)堆的修改:

增值:先增加当前值,然后下滤

void IncreaseKey(int pos,int x,PriorityQueue Q)
{
    if(IsEmpty(Q)) return ;
    Q->data[pos]+=x;
    PrecolateDown(pos,Q);
}

降值:先减小当前值,然后上滤

void IncreaseKey(int pos,int x,PriorityQueue Q)
{
    if(IsEmpty(Q)) return ;
    Q->data[pos]+=x;
    PrecolateDown(pos,Q);
}

 

(5)堆的删除:

先将这个值减小为负无穷,再删除最小值。

void Delete(int pos,PriorityQueue Q) //删除某一位置的元素 
{
    if(IsEmpty(Q)) return ;
    int tmp=INT_MAX,i;
    DecreaseKey(pos,tmp,Q);
    DeleteMin(Q);
}

 

(6)构建堆:

从叶子节点的父节点开始调整,使第i层及以下序列有序,然后向上调整(用于堆排序)。

void BuildHeap(PriorityQueue Q) //构建堆 
{
    for(int i=Q->size/2;i>0;i--)
    PrecolateDown(i,Q);
}

 

总代码:

技术分享图片
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1200;
const int MINSIZE = 3;
struct Node{
    int *data;
    int Capcity,size;
};
typedef struct Node* PriorityQueue;
PriorityQueue Init(int size) //初始化 
{
    PriorityQueue Q=new Node;
    if(Q==NULL) printf("Ouf of Space!!!
");
    if(size<MINSIZE) printf("Too Small
");
    Q->data=new int[size+1];
    if(Q->data==NULL){
        printf("Ouf of Space!!!
");
    }
    Q->Capcity=size;
    Q->size=0;
    Q->data[0]=INT_MIN;
    return Q;
} 
bool IsEmpty(PriorityQueue Q) 
{
    return Q->size==0;
}
bool IsFull(PriorityQueue Q)
{
    return Q->size>Q->Capcity;
}

void PrecolateUp(int pos,PriorityQueue Q) //上滤 
{
    int i,x=Q->data[pos];
    for(i=pos;Q->data[i/2]>x;i/=2)
    Q->data[i]=Q->data[i/2];
    Q->data[i]=x;
}
void PrecolateDown(int pos,PriorityQueue Q) //下滤 
{
    int i,Last=Q->data[pos],child;
    for(i=pos;i*2<=Q->size;i=child)
    {
        child=i*2;
        if(child!=Q->size&&Q->data[child+1]<Q->data[child]) child++; //考虑是否有单个节点 
        if(Last>Q->data[child]) Q->data[i]=Q->data[child];
        else break;
    }
    Q->data[i]=Last;
}

void Insert(int x,PriorityQueue Q) //插入 
{
    if(IsFull(Q)){
        printf("The Queue is Full
");
        return ;
    }
    int i;
    for(i=++Q->size;Q->data[i/2]>x;i/=2) //上滤 
    Q->data[i]=Q->data[i/2];
    Q->data[i]=x;
}

//修改 
void DecreaseKey(int pos,int x,PriorityQueue Q)
{
    if(IsEmpty(Q)) return ;
    Q->data[pos]-=x;
    PrecolateUp(pos,Q); 
}
void IncreaseKey(int pos,int x,PriorityQueue Q)
{
    if(IsEmpty(Q)) return ;
    Q->data[pos]+=x;
    PrecolateDown(pos,Q);
}

void BuildHeap(PriorityQueue Q) //构建堆 
{
    for(int i=Q->size/2;i>0;i--)
    PrecolateDown(i,Q);
}
int DeleteMin(PriorityQueue Q) //出队 
{
    if(IsEmpty(Q)){
        printf("The Queue is Empty!!!
");
        return -1;
    }
    int i,MI=Q->data[1],child,Last=Q->data[Q->size--];
    for(i=1;i*2<=Q->size;i=child) //下滤 
    {
        child=i*2;
        if(Q->size!=child&&Q->data[child+1]<Q->data[child]) child++;
        if(Last>Q->data[child]) Q->data[i]=Q->data[child];
        else break;
    }
    Q->data[i]=Last;
    return MI;
}
void Delete(int pos,PriorityQueue Q) //删除某一位置的元素 
{
    if(IsEmpty(Q)) return ;
    int tmp=INT_MAX,i;
    DecreaseKey(pos,tmp,Q);
    DeleteMin(Q);
}
void Print(PriorityQueue Q)
{
    while(!IsEmpty(Q)){
        int x=DeleteMin(Q);
        printf("%d ",x);
    }
    printf("
");
} 
int main(void)
{
    int n,i,x;
    scanf("%d",&n);
    PriorityQueue Q=Init(n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&x);
        Insert(x,Q);
    }
    IncreaseKey(3,99,Q);
    DecreaseKey(5,1000,Q);
    Delete(1,Q);
    Print(Q);
    
    return 0;
}
/*
测试数据:
5
12 34 11 5 6
*/ 
View Code

 

以上是关于优先队列与堆的主要内容,如果未能解决你的问题,请参考以下文章

《算法》笔记 6 - 优先队列与堆排序

漫谈算法2优先队列与堆排序

最大堆与堆排序和优先队列

优先级队列与堆排序

优先队列及(二叉)堆

C语言实现优先队列(priority queue)