Priority Queues (Heaps)

當(dāng)任務(wù)提交給打印機時,通常會生成一個隊列。但如果某些任務(wù)特別重要的時候,這顯然是不合適的,我們希望它一到就立刻執(zhí)行。類似的在一個多用戶的系統(tǒng)中,操作系統(tǒng)需要決定先執(zhí)行哪些進(jìn)程。這些應(yīng)用需要一種特定的隊列,通常稱為優(yōu)先隊列。

Model

優(yōu)先隊列至少有兩種操作,insert和deleteMin,類似于普通隊列的出隊和入隊。有很多種實現(xiàn)優(yōu)先隊列的方式,這里我們將常用二分堆的方式,通常也直接稱之為堆(Heap)。類似于二分查找樹,堆有兩種屬性,結(jié)構(gòu)特性和堆特性。像AVL樹一樣,某些操作是可以破壞其中一個特性,因此堆需要修復(fù)所有特性后才能停止操作。

Structure Property

堆是一種完全二叉樹,完全二叉樹當(dāng)高度為h時,會有2h到2h+1 - 1個節(jié)點。也意味著堆的高度是LogN。由于完全二叉樹的特性,完全可以將它存儲在一個數(shù)組中也不需要鏈接。
對于數(shù)組中任意位置i,左孩子位置為2i,右孩子位置為2i+1,父節(jié)點則為\lfloor i/2 \rfloor

Heap-Order Propery

堆的特性則支持我們能夠快速的執(zhí)行某些操作,例如我們需要快速的獲得最小值,顯而易見最小的元素應(yīng)該是根節(jié)點。我們假設(shè)所有的子樹都是一個堆,因此所有的節(jié)點都應(yīng)該小于它的子孫節(jié)點。
因此,在一個堆中,對于所有的節(jié)點X,X的所有父節(jié)點都小于或者等于X(不包括根節(jié)點,因為根節(jié)點沒有父節(jié)點)。這樣的話最小的節(jié)點就在根節(jié)點,我們可以在常量時間完成findMin操作。

Basic Heap Operations

一個堆的結(jié)構(gòu)可以表示為:

import java.nio.BufferUnderflowException;

/**
 * @author dalu
 * @create 2020-04-15 22:22
 */
public class BinaryHeap <AnyType extends Comparable<? super AnyType>> {
    public BinaryHeap() {
        this(DEFAULT_CAPACITY);
    }

    public BinaryHeap(int capacity) {
        currentSize = 0;
        array = (AnyType []) new Comparable[ capacity + 1];

    }

    public BinaryHeap(AnyType[] items) {
        currentSize = items.length;
        array = (AnyType []) new Comparable[ ( currentSize + 2) * 11 / 10];

        int i = 1;
        for(AnyType item : items) {
            array[i++] = item;
        }
        buildHeap();

    }

    public void insert(AnyType x) {
        if(currentSize == array.length - 1) {
            enlargeArray(array.length * 2 + 1);
        }
        int hole = ++currentSize;
        for(array[0] = x; x.compareTo(array[hole / 2]) < 0; hole /= 2) {
            array[hole] = array[hole / 2];
        }
        array[hole] = x;

    }

    public AnyType findMin() {
        if(isEmpty()) {
            throw new BufferUnderflowException();
        }
        return  array[1];

    }

    public AnyType deleteMin() {
        if(isEmpty()) {
            throw new BufferUnderflowException();
        }
        AnyType minItem = findMin();
        array[1] = array[currentSize-- ];
        percolateDown(1);
        return minItem;
    }

    public boolean isEmpty() {
        return (currentSize == 0);

    }

    public void makeEmpty() {
        currentSize = 0;
    }

    private static final int DEFAULT_CAPACITY = 10;

    private int currentSize;
    private AnyType[] array;

    private void percolateDown(int hole) {
        int child;
        AnyType tmp = array[hole];

        for(; hole * 2 <= currentSize; hole = child) {
            child = hole * 2;
            if(child != currentSize && array[child + 1].compareTo(array[child]) < 0) {
                child++;
            }
            if(array[child].compareTo(tmp) < 0) {
                array[hole] = array[child];
            } else  {
                break;
            }
        }
        array[hole] = tmp;
    }

    private void buildHeap() {
        for(int i = currentSize / 2; i > 0; i--) {
            percolateDown(i);
        }
    }

    private void enlargeArray(int newSize) {
        AnyType [] old = array;
        array = (AnyType []) new Comparable[newSize];
        for(int i = 0; i < old.length; i++) {
            array[i] = old[i];
        }

    }
}

insert

插入一個元素X到堆中,我們需要在下一個可用的位置創(chuàng)建一個孔,保持堆的完全性。如果X不違反堆的順序,我們就用X替換這個孔,然后完成操作。否則,我們就將這個孔向著它的父節(jié)點進(jìn)行滑動,直到X可以被插入或者到根節(jié)點。
這種方法通常稱為滲透,新的元素不斷找尋對的位置從而滲透到堆中。

deleteMin

找到最小的元素時容易的,問題是如何刪除它。當(dāng)最小的元素被刪除時,根節(jié)點將會變?yōu)橐粋€孔。由于現(xiàn)在對少了一個元素,因此最后一個元素X就必須被移動。如果X可以被移動到這個孔上,操作就結(jié)束。我們需要將孔的孩子節(jié)點移動這個孔上,然后不斷的下沉這個孔。重復(fù)這個操作直到X可以替換這個孔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容