數據結構-堆(heap)

堆(heap)也被稱爲優先隊列(priority queue)。隊列中允許的操作是先進先出(FIFO),在隊尾插入元素,在隊頭取出元素。而堆也是一樣,在堆底插入元素,在堆頂取出元素,但是堆中元素的排列不是按照到來的先後順序,而是按照一定的優先順序排列的。這個優先順序可以是元素的大小或者其他規則。如圖一所示就是一個堆,堆優先順序就是大的元素排在前面,小的元素排在後面,這樣得到的堆稱爲最大堆。最大堆中堆頂的元素是整個堆中最大的,並且每一個分支也可以看成一個最大堆。同樣的,我們可以定義最小堆,如圖二所示。

這裏寫圖片描述
來源:http://blog.qiji.tech/archives/4855

堆的存儲

堆可以看成一個二叉樹,所以可以考慮使用二叉樹的表示方法來表示堆。但是因爲堆中元素按照一定的優先順序排列,因此可以使用更簡單的方法——數組——來表示,這樣可以節省子節點指針空間,並且可以快速訪問每個節點。堆得數組表示其實就是堆層級遍歷的結果,如下圖所示:

這裏寫圖片描述
來源:http://rock3.info/blog/category/algorithm-and-data-structures/

這樣對於每一個下標爲i的節點,其左子節點在下標爲2*i的位置,其右子節點在下標爲2*i+1的位置,而其父節點在下標爲 floor{i / 2},其中i從1開始。通過數組的存儲方式,可以通過計算下標,直接獲取到相關節點。

typedef struct heap
{
    int capacity;
    int size;
    int *arr;
} Heap;

#define MIN -1

Heap* CreateEmptyHeap (int max) {

    Heap *heap = (Heap*)malloc(sizeof(Heap));

    if (heap == NULL) {
        printf("out of space!\n");
        return NULL;
    } 

    heap->capacity = max;
    heap->size = 0;
    heap->arr = (int*)malloc(sizeof(int) * (heap->capacity + 1));
    heap->arr[0] = MIN;

    return heap;
}

最小堆爲例,說明堆得插入、刪除等操作。

插入

堆還可以看成一個完全二叉樹,每次總是先填滿上一層,再在下一層從左往右依次插入。堆的插入步驟:

  1. 將新元素增加到堆的末尾
  2. 按照優先順序,將新元素與其父節點比較,如果新元素小於父節點則將兩者交換位置。
  3. 不斷進行第2步操作,直到不需要交換新元素和父節點,或者達到堆頂
  4. 最後通過得到一個最小堆

通過將新元素與父節點調整交換的操作叫做上濾(percolate up)

這裏寫圖片描述
來源:http://blog.csdn.net/tuke_tuke/article/details/50357939

Heap* Insert (Heap* heap, int val) {

    if (IsFull(heap)) {
        printf("heap is full!\n");
        return heap;
    }

    // percolate up new element
    int i;
    for (i = ++heap->size; heap->arr[i] > val; i /= 2) 
        heap->arr[i] = heap->arr[i/2];
    heap->arr[i] = val;

    return heap;
}


bool IsFull (Heap* heap) {

    return (heap->capacity == heap->size);
}

刪除

堆的刪除操作與插入操作相反,插入操作從下往上調整堆,而刪除操作則從上往下調整堆。

  1. 刪除堆頂元素(通常是將堆頂元素放置在數組的末尾)
  2. 比較左右子節點,將小的元素上調。
  3. 不斷進行步驟2,直到不需要調整或者調整到堆底。

上述調整的方法稱爲下濾(percolate down)
這裏寫圖片描述
來源:http://blog.csdn.net/tuke_tuke/article/details/50357939

bool IsEmpty (Heap* heap) {

    return (heap->size == 0);
}


Heap* DeleteMin (Heap* heap) {

    if (IsEmpty(heap)) {
        printf("empty heap!\n");
        return NULL;
    }

    int minElement = heap->arr[1];
    int lastElement = heap->arr[heap->size--];

    int i, childIndex;
    for (i = 1; 2*i <= heap->size; i = childIndex)
    {
        childIndex = 2*i;

        //get the bigger child node
        if (heap->arr[childIndex] != heap->size 
         && heap->arr[childIndex] > heap->arr[childIndex+1])
            childIndex++;

        if (lastElement > heap->arr[childIndex])
            heap->arr[i] = heap->arr[childIndex];
        else
            break;
    }
    heap->arr[i] = lastElement;

    return heap;
}
發佈了37 篇原創文章 · 獲贊 52 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章