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操作,又延申了左式堆和二次队列

  • 相关阅读:
    关于HTML标签
    windows bat 命令
    maven 基本用法
    idea 永久激活
    mysql 性能测试 mysqlslap 的使用介绍
    Javafx 入门教程(一)
    websocket server 服务端工具
    OPC 技术
    以http为例解读OSI七层协议
    Javafx 入门教程前言
  • 原文地址:https://www.cnblogs.com/secoding/p/9609369.html
Copyright © 2011-2022 走看看