1. 程式人生 > >優先隊列(堆) -數據結構(C語言實現)

優先隊列(堆) -數據結構(C語言實現)

++ eem enter ext lock 二次 arr 快速 left

數據結構與算法分析

優先隊列

模型

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

優先隊列(堆) -數據結構(C語言實現)