zoukankan      html  css  js  c++  java
  • 优先队列(堆) -数据结构(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操作,又延申了左式堆和二次队列

  • 相关阅读:
    洛谷 P1508 Likecloud-吃、吃、吃
    Codevs 1158 尼克的任务
    2017.10.6 国庆清北 D6T2 同余方程组
    2017.10.6 国庆清北 D6T1 排序
    2017.10.3 国庆清北 D3T3 解迷游戏
    2017.10.3 国庆清北 D3T2 公交车
    2017.10.3 国庆清北 D3T1 括号序列
    2017.10.4 国庆清北 D4T1 财富
    2017.10.7 国庆清北 D7T2 第k大区间
    2017.10.7 国庆清北 D7T1 计数
  • 原文地址:https://www.cnblogs.com/secoding/p/9609369.html
Copyright © 2011-2022 走看看