优先队列(堆) -数据结构(C语言实现)

数据结构与算法分析

优先队列

模型

  • Insert(插入) == Enqueue(入队)
  • DeleteMin(删除最小者) == Dequeue(出队)

基本实现

  • 简单链表:在表头插入,并遍历该链表以删除最小元

    时间代价昂贵

  • 二叉查找树

    二叉查找树支持许多不需要的操作,实现麻烦,不值得
    最合适:二叉堆

二叉堆

堆的两种性质

结构性
  • 完全二叉树:除底层外完全填满,底层也是从左至右填
  • 完全二叉树的高为
  log N
  • 分布很有规律可以用数组实现

左儿子 = 2i

右儿子 = 2i + 1

堆序性
  • 树的最小元应该在根节点上
  • 每个节点X,X的父亲的关键字应该小于或等于X的关键字

实现

优先队列的声明
struct HeapStrcut ;
typedef struct HeapStruct *PriorityQueue ;

PriorityQueue Intialize(int MaxElement) ;
void Destory(PriorityQueue H) ;
void MakeEmpty(PriorityQueue H) ;
void Insert(ElementType X, PriorityQueue H) ;
ElementType DeleteMin(PriotityQueue H) ;
ElementType Find(PritityQueue H) ;
int IsEmpty(PriorityQueue H) ;
int IsFull(PriorityQueue H) ;

srtuct HeapStruct
{
    int Capacity ;
    int Size l
    ElementType *Elements ;
}

初始化
PriorityQueue Intialize(int MaxElement)
{
    PriorityQueue H ;
    H->Elements = malloc((MaxElement + 1) * sizeof(ElementType) ;
    if(H->Elements == NULL)
        FatalError("内存不足");
    H->Capacity = MaxElement ; 
    H->Size = 0;
    H->Elements[0] = MinData ;//在根节点赋一个绝对的小的值
    
    return H ;
}

Insert操作

上滤

void Insert(ElementType X, PriorityQueue H)
{
    int i ;
    if(IsFull(H))
        Error("堆满") ;
    
    for(i = ++H->Size;H->Elements[i/2] > X;i/2)
        H->Elenemts[i] = H->Elements[i/2] ;
    H->Elements[i] = X ;
    
    return H ;
}

Delete函数

下滤

先拿到最后一个元素,和当前被删除后剩下的空穴的最小儿子比较,如果儿子小则换至空穴,继续下滤,反之将最后一个元素放置空穴结束下滤

ElementType Insert(PriorityQueue H)
{

    int i,Child ;
    ElementType MinElement,LastElement ;
    
    if(IsEmpty(H))
    {
        Error("堆为空") ;
        return H->Elements[0] ;
    }
    MinElement = H->Elements[1];
    LastElement = H->Elements[H->Size--] ;
    for(i = 1; i * 2 <= H->Size;i = Child)
    {
        Child = i * 2;
        if(Child != H->Size && H->Element[Child] > H->Elements[Child + 1])
            Child ++ ;
            
        if(LastElement > H->Elements[Child)
            H->Elements[i] = H->Elements[Child] ;
        else break ;
    }
    H->Elements[i] = LastElement ;
    
    return MinElenemt;
}

左式堆

性质

高效支持Merge操作

和二叉树唯一区别在于:左式堆不是理想平衡的

对于堆中的每一个节点X,左儿子的零路径长NPL大于右儿子的零路径长NPL

- 零路径长(NPL):从该节点到一个没有两个儿子的节点的最短路径长

左式堆的类型声明
PriorityQueue Intailize(void) ;
ElementType FindMin(PriorityQueue H) ;
int IsEmpty(PriorityQueue H) ;
PriorityQueue Merge(PriorityQueue H1,PriorityQueue H2) ;

#define Insert(X,H) (H = Insert1(X,H)) ; //为了兼容二叉堆

PriorityQueue Insert1(ElementType, PriorityQueue H) ;
PriorityQueue DeleteMin(PriorityQueue H) ;

sturct TreeNode
{
    ElementType Element ;
    PriorityQueue Left ;
    PriorityQueue Right ;
    int Npl ;
}

Merge操作

驱动程序

PriorityQueue Merge(PriorityQueue H1,PriorityQueue H2)
{
    if(H1 == NULL)
        return H2 ;
    eles if(H2 == NULL)
        return H1 ;
    else if(H1->Element > H2->Element)
        return Merge1(H1,H2) ;
    else
        return Merge1(H1S,H2) ;
}

实际操作

PriorityQueue Merge1(PriortyQueue H1,PriorityQueue H2)
{
    if(H1->Left == NULL)
        H1->Left = H2 ;
    else
    {
        H2->Right = Merge1(H1->Right,H2) ;
        if(H1->Left->Npl < H1->Right->Npl)
            SwapChildren(H1) ;
        H1->Npl = H1->Right->Npl + 1;
    }
    
    return H1 ;
}

Insert操作
PriorityQueue Insert(ElementType X,PriorityQueue H)
{
    PriorityQueue SinglNode ;
    SinglNode = malloc(sizeof(TreeNode)) ;
    if(SinglNode == NULL)
        FatalError("内存不足") ;
    else
    {
        SingleNode->Element = X ;
        SingleNode->Npl = 0 ;
        SingleNode->Left = SingleNode->Right = NULL ;
        Merge(SingleNode,H) ;
    }
    return H ;
}

Delete操作
PriorityQueue DeleteMin1(PriorityQueue H)
{
    PriorityQueue LeftHeap,RightHeap ;
    
    if(IsEmpty(H))
        FatalError("队列为空") ;
    else
    {
        LeftHeap = H1->Left ;
        RightHeap = H1->Right ;
        free(H) ;
        Merge(LeftHeap,RightHeap) ;
    }
}

二项队列

结构

  • 二项队列是堆序树的集合,称为森林
  • 堆序中每颗树都是有约束的树,称为二项树
  • 高度为k的二项树有一颗二项树Bk-1附接到另一颗二项树Bk-1的根上

二项队列的实现

二项队列将是二项树的数组

二项树的每个节点包含数据,第一个儿子和兄弟

二项队列的类型声明
`
typedef struct BinNode *Position ;
typedef struct Collection *BinQueue ;

struct BinNode
{
    ElementType Element ;
    Position LeftChild ;
    Position NextBiling ;
}

typedef Position BinTree ;

struct Collection
{
    int CurrentSize ;
    BinTree TheTrees[MaxTree] ;
}

Merge操作

合并两个相同大小的两颗二项树

BinTree ConbineTrees(BinTree T1,BinTree T2)
{
    if(T1->Element > T2->Element)
        return CombineTree(T2,T1) ;
    T2->NextBling = T1->LeftChild ;
    T1->LeftChild = T2 ;
    
    return T1 ;
}

合并两个优先队列

BinQueue Merge(BinQueue H1,BinQueue H2)
{
    BinTree T1,T2,Carry = NULL ;
    int i ,j ;
    if(H1->CurrentSize + H2->CurrentSize > Capacity)
        Error("合并后过大") ;
    
    H1->CurrentSize += H2->CurrentSize ;
    for(i = 0;j = 1;j <= H1->CurrentSize; i++,j *= 2)
    {
        T1 = H1->TheTree[i] ;
        T2 = H2->TheTree[i] ;
        
        switch(!!T1 + 2 * !!T2 + 4 * !!Carry)
        {
            case 0 : //空树
            case 1:
                break ; //只有H1
            case 2:
                H1->TheTree[i] = T2
                H2->TheTree[i] = NULL ;
                break ;
            case 4:
                H1->TheTree[i] = Carry ;
                Carry = NULL ;
            case 3: //h1 and h2
                Carry = CombineTrees(T1,T2) ;
                H1->TheTree[i] = H1->TheTree[i] = NULL ;
                break ;
            case 5: //h1 and carry
                Carry = ConbineTrees(T1,Carry) ;
                H1->TheTrees[i] = NULL ;
            case 6:
                Carry = ConbineTrees(T2,Carry) ;
                H2->TheTrees[i] = NULL ;
            case 7: //都有
                H1->TheTree[i] = Carry ;
                Carry = CombineTrees(T1,T2) ;
                H2->TheTrees[i] = NULL ;
                break ;
                
        }
        
    }
    return H1 ;
}

总结

优先队列可以用二叉堆实现,简单快速

但考虑到Merge操作,又延申了左式堆和二次队列

转载于:https://www.cnblogs.com/secoding/p/9609369.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值