二叉堆 - Java 實現

二叉堆定義

二叉堆是一棵完全二叉樹,除最後一層外,每一層都是滿的,且最後一層的節點從左到右依次生長。

此處實現的是最小二叉堆。

任何一個父節點的關鍵碼總是小於、等於其子節點的關鍵碼。

對於節點 ii,其左孩子爲 2i2 i,右孩子爲 2i+12i+1,父節點爲 i/2\lfloor i/2 \rfloor

import java.util.ArrayList;
import java.util.List;

public class BinaryHeap<T extends Comparable<? super T>> {
    private List<T> list;

    public BinaryHeap() {
        list = new ArrayList<>();
        list.add(null);			// 索引爲 0 的節點爲哨兵,真正的節點數據從索引 1 處開始
    }

	// 節點數
    public int size() {
        return list.size() - 1;
    }
    ...
}

插入元素

設待插入元素爲 e。

首先在最後一個位置創建一個空穴,並暫時用 e 填充它,然後執行上濾操作。

上濾

設空穴節點之父爲 p 。若 e 小於 p 的關鍵碼,則將 p 下沉至空穴處,空穴上升至 p 處。繼續此過程,直至 e 能夠放入空穴中。

注意:在執行上濾操作之前,首先將索引爲 0 的哨兵節點的關鍵碼設爲 e 。該哨兵節點可看成是二叉堆的根節點之父。這樣可使空穴最多上升至根節點(因爲根節點之父的關鍵碼爲 e ,不小於 e)。

// 插入元素
public void insert(T e) {
    list.add(e);            // 在末尾添加一個空洞,暫時用 e 填充它
    int hole = size();

    percolateUp(hole);
}

// 上濾
private void percolateUp(int hole) {
    T e = list.get(hole);
    list.set(0, e);         // 哨兵

    // 只要 e 比空洞的父節點小,便將父節點往下拉,然後空洞上升
    while (e.compareTo(list.get(hole >> 1)) < 0) {
        list.set(hole, list.get(hole >> 1));
        hole >>= 1;
    }

    list.set(hole, e);
}

刪除最小元素

最小元素在二叉堆的根節點處。

刪除根節點後,將在根節點處產生一個空穴,且堆的大小將減少一。

可以首先刪除最後一個節點,並暫時用它填充空穴,然後執行下濾操作。

下濾

設最後一個節點(刪除操作之前)的關鍵碼爲 e 。

設空穴所在節點的最小孩子爲 c,若 c 的關鍵碼小於 e,則 c 上升至空穴處,空穴下沉至 c 處。重複此過程,直至 e 可放入空穴。

// 刪除最小元素
public T removeMinimum() {
	// 堆爲空
    if (size() < 1) {
        return null;
    }

    T minimum = list.get(1);		// 最小元素
    T last = list.remove(size());

	// 刪除之後,堆不爲空:原先堆至少有兩個節點
    if (size() > 0) {
        list.set(1, last);
        percolateDown(1);
    }

    return minimum;
}

// 下濾
private void percolateDown(int hole) {
    T e = list.get(hole);
    int n = size();

    while ((hole << 1) <= n) {
    	// 空穴節點之最小孩子:開始時設爲左孩子
        int child = hole << 1;

        // child 有右兄弟,且右兄弟比 child 還要小
        if (child != n && list.get(child + 1).compareTo(list.get(child)) < 0) {
            child++;
        }

        // 子節點比 e 要小,則子節點上升,空穴下降
        if (list.get(child).compareTo(e) < 0) {
            list.set(hole, list.get(child));
            hole = child;
        } else {
            break;
        }
    }

    list.set(hole, e);
}

構建堆

從最後一個內部節點(n/2\lfloor n/2 \rfloor)開始,依次對每個內部節點執行下濾操作:即,依次以每個內部節點爲根,構建一棵最小二叉堆。

時間複雜度爲 O(n) :可以通過計算虛線的最大條數來得到。

在這裏插入圖片描述

// 構建堆
public void buildHeap(List<T> list) {
    this.list.clear();
    this.list.add(null);

    for (int i = 0; i < list.size(); i++) {
        this.list.add(list.get(i));
    }

    // 從最後一個內部節點開始,執行下濾操作
    for (int i = size() >> 1; i > 0; i--) {
        percolateDown(i);
    }
}
發佈了118 篇原創文章 · 獲贊 18 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章