【筆記】堆及其實現

Time-space發表於2017-10-29

  有一種特殊形式的完全二叉樹——堆,它有兩種基本形式:最大堆最小堆
  最大堆:如果一顆完全二叉樹的任意一個非終結結點的元素都不小於其左兒子結點和右兒子結點的元素,則稱此完全二叉樹為最大堆。
  最小堆:如果一顆完全二叉樹的任意一個非終結結點的元素都不大於其左兒子結點和右兒子結點的元素,則稱此完全二叉樹為最小堆。

  最大堆的根結點中的元素在整個堆中是最大的;而最小堆的根結點中的元素在整個堆中是最小的。
  當把最大堆看做抽象資料型時,有以下幾個基本操作:

  • MaxHeap(heap):建立一個空堆,最多可以容納MaxSize個元素。
  • Insert(heap,item):判斷堆是否為滿。
  • HeapEmpty(heap):插入一個元素。
  • DeleteMax(heap):刪除最大元素。

  由於最大堆是一顆完全二叉樹,所以最大堆可以用一維陣列來表示。

  • 最大堆的型別定義
#define Maxsize 200
typedef struct
{
    int key;
    /*other fields*/
}Elementtype;
typedef struct
{
    Elementtype elements[Maxsize];
    int n;
}HEAP;
  • 建立一個空堆
    初始時,堆的元素個數計數器n為零,
void MaxHeap(HEAP *heap)
{
    heap->n=0;
}
  • 判斷堆是否為空
int HeapEmpty(HEAP heap)
{
    return(!heap.n);
}
  • 堆中插入一個元素

  當要插入一個元素時,為了保持完全二叉樹的結構性質,新增結點的編號應為i=n+1。同時,為了保持最大堆的性質,還要把結點i的元素與其父結點的元素進行比較。如果結點i的元素大於其父結點的元素,則將結點i的元素與其父結點的元素進行交換,並令結點i的父結點成為新結點i,繼續向上比較,直到結點i的元素不大於其父結點的元素或者i到達根結點為止。

  由於堆是一顆完全二叉樹,因此具有n個元素的堆,其高度為[log(n+1)]。這就意味著while迴圈要重複O(logn)次,因此插入函式的時間複雜性為O(logn)。

void Insert(HEAP *heap,Elementtype element)
{
    int i;
    i=heap->n+1;
    while( (i!=1)&&(element.key>heap->elements[i/2].key))
    {
        heap->elements[i]=heap->elements[i/2];
        i/=2;
    }
    heap->elements[i]=element;
    heap->n++;
}
  • 堆中刪除一個元素

  在最大堆中刪除最大元素時,一定是從堆的根結點中刪除元素。為了在刪除結點後保持完全二叉樹的結構性質,把最後一個結點(編號最大的葉結點n)的元素暫存在根節點i=1中,此時完全二叉樹中元素的個數變為n-1,但可能不符合最大堆的定義。為了保持最大堆的性質,還需要比較結點i的元素預期較大兒子結點的元素。如果結點i的元素小於其較大兒子結點的元素,則將結點i與其較大兒子結點的元素對換,並令結點i的較大兒子結點成為新的結點i,繼續向下比較,直到結點i的元素不小於其較大兒子結點的元素或者i到達葉結點為止。
  刪除操作的時間複雜性為O(logn)。

Elementtype DeleteMax(HEAP *heap)
{
    int parent=1,child=2;
    Elementtype element,tmp;
    if(!HeapEmpty(*heap))
    {
        element=heap->elements[1];
        tmp=heap->elements[heap->n--];
        while(child <= heap->n)
        {
            if((child<heap->n)&&(heap->elements[child].key<heap->elements[child+1].key))
                child++;
            if(tmp.key >= heap->elements[child].key)
                break;
            heap->elements[parent]=heap->elements[child];
            parent=child;
            child*=2;
        }
        heap->elements[parent]=tmp;
        return element;
    }
}
  • 主函式
int main()
{
    int n,i;
    HEAP h;
    MaxHeap(&h);
    printf("請輸入要插入的元素個數:(不超過200)");
    scanf("%d",&n);
    while(n>Maxsize)
    {
        printf("元素個數超出堆的容量!請重新輸入:");
        scanf("%d",&n);
    }
    Elementtype a[n+1],x;
    printf("請依次輸入每個元素的值:");
    for (i = 1; i <= n; i++)
    {
        scanf("%d",&a[i].key);
        Insert(&h, a[i]);
    }
    while (!HeapEmpty(h))
    {
        x = DeleteMax(&h);
        printf("%d", x.key);
        if (!HeapEmpty(h))
            printf(",");
    }
    printf("\n");
    return 0;
}
  • 測試結果


這裡寫圖片描述

  經常使用堆來實現優先順序佇列。優先順序佇列只對最高(或最低)優先順序的元素進行刪除。但是在任何時候,都可以把任意優先順序的元素插入到優先順序佇列。如果應用程式要求刪除最高優先順序的元素,就使用最大堆。如果應用程式要求刪除最低優先順序的元素,則使用最小堆。

相關文章