數據結構與算法之優先隊列

什麼是優先隊列
快速得找到元素的最大或最小值。爲了實現這種數據結構
優先隊列便孕育而生。優先隊列或ADT是一種數據結構,支持插入和刪除最小值或最大值並返回最大值或最小值得操作。

優先隊列的應用
1.數據壓縮(赫夫曼編碼)
2.最短路徑
3.最小生成樹
4.選擇問題:查找第K個最小元素

優先隊列的實現
1.有序/無序數組實現
2.有序/無序鏈表實現
3.二叉樹實現
4.二叉堆實現

主要介紹二叉堆的實現
二叉堆是一棵具有特殊性質的二叉樹。
基本要求是所有節點必須大於或等於(小於或等於)其孩子節點
並且應該是一棵完全二叉樹

二叉堆的實現


/*
 * 優先隊列是至少允許下列兩種操作的數據結構
 * insert(插入) deleteMin(刪除最小) 
 * 優先隊列的工作是 找出 返回 刪除 優先隊列中的最小或最大元素
 * 
 * 完全二叉堆實現優先隊列
 * 利用完全二叉樹的緊湊性將完全二叉樹轉化爲數組形式
 */
public class BinaryHeap<T extends Comparable<? super T>> {
    // 堆
    private T[] heap;
    // 當前大小
    private int currentSize;
    // 默認大小
    private final static int DEFAULT_SIZE = 10;

    public BinaryHeap() {
        this(DEFAULT_SIZE);
    }

    // 打印二叉堆
    public void printHeap() {
        for (int i = 0; i < currentSize; i++)
            System.out.print(this.heap[i] + ",");
    }

    @SuppressWarnings("unchecked")
    public BinaryHeap(int size) {
        heap = (T[]) new Comparable[size];
    }

    // 將一個數組轉化爲二叉堆
    @SuppressWarnings("unchecked")
    public BinaryHeap(T[] items) {
        currentSize = items.length;
        heap = (T[]) new Comparable[(currentSize + 2) * 11 / 10];
        System.arraycopy(items, 0, heap, 0, items.length);
        bulidHeap();
    }

    /*
     * 對外開放的方法
     */

    // 插入堆
    public void insert(T t) {
        // 數據臨界 擴一倍
        if (currentSize == heap.length)
            enlargeArray(heap.length * 2);
        // 當前數量加一
        currentSize++;
        // 插入節點的序號
        int i = currentSize - 1;
        while (i > 0 && t.compareTo(heap[(i - 1) / 2]) > 0) {
            // 插入數據大於其父節點的數據
            heap[i] = heap[(i - 1) / 2];
            // 下一個父節點
            i = (i - 1) / 2;
        }
        heap[i] = t;
    }

    // 找到最大值
    public T findMax() {
        if (currentSize > 0)
            return heap[0];
        return null;

    }

    // 刪除最大值
    public T deleteMax() {
        T t = null;
        if (currentSize > 0) {
            t = heap[0];
            // 將最後一個元素替代第一個
            heap[0] = heap[currentSize - 1];
            currentSize--;
            percolateDown(0);
        }
        return t;
    }

    /*
     * 私人方法
     */

    // 下濾 (先找到孩子的最大值在與小於孩子的父節點互換)
    private void percolateDown(int hole) {
        // 判斷節點是否有孩子
        int left = hole * 2 + 1;
        T temp;
        // 如果左孩子存在
        if (left < currentSize) {
            // 如果右孩子存在
            if (left + 1 < currentSize) {
                // 若右孩子大於左孩子互換
                if (heap[left + 1].compareTo(heap[left]) > 0) {
                    temp = heap[left + 1];
                    heap[left + 1] = heap[left];
                    heap[left] = temp;
                }
            }
            // 如果左節點大於父節點 互換
            if (heap[left].compareTo(heap[hole]) > 0) {
                temp = heap[hole];
                heap[hole] = heap[left];
                heap[left] = temp;
            }
        }
    }

    // 建立二叉堆
    public void bulidHeap() {
        // 調整所有節點 (currentSize表示當前堆中元素個數)
        for (int i = (currentSize - 2) / 2; i >= 0; i--) {
            percolateDown(i);
        }
    }

    // 動態擴充數組變爲原來的兩倍
    @SuppressWarnings("unchecked")
    private void enlargeArray(int newSize) {
        // 保存舊數據
        T[] temp = heap;
        // 擴一倍
        heap = (T[]) new Comparable[2 * heap.length];
        // 導入舊數據
        System.arraycopy(temp, 0, heap, 0, temp.length);
    }
}

至此,優先隊列的基礎介紹完畢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章