Priority Queues (Heaps)

当任务提交给打印机时,通常会生成一个队列。但如果某些任务特别重要的时候,这显然是不合适的,我们希望它一到就立刻执行。类似的在一个多用户的系统中,操作系统需要决定先执行哪些进程。这些应用需要一种特定的队列,通常称为优先队列。

Model

优先队列至少有两种操作,insert和deleteMin,类似于普通队列的出队和入队。有很多种实现优先队列的方式,这里我们将常用二分堆的方式,通常也直接称之为堆(Heap)。类似于二分查找树,堆有两种属性,结构特性和堆特性。像AVL树一样,某些操作是可以破坏其中一个特性,因此堆需要修复所有特性后才能停止操作。

Structure Property

堆是一种完全二叉树,完全二叉树当高度为h时,会有2h到2h+1 - 1个节点。也意味着堆的高度是LogN。由于完全二叉树的特性,完全可以将它存储在一个数组中也不需要链接。
对于数组中任意位置i,左孩子位置为2i,右孩子位置为2i+1,父节点则为。

Heap-Order Propery

堆的特性则支持我们能够快速的执行某些操作,例如我们需要快速的获得最小值,显而易见最小的元素应该是根节点。我们假设所有的子树都是一个堆,因此所有的节点都应该小于它的子孙节点。
因此,在一个堆中,对于所有的节点X,X的所有父节点都小于或者等于X(不包括根节点,因为根节点没有父节点)。这样的话最小的节点就在根节点,我们可以在常量时间完成findMin操作。

Basic Heap Operations

一个堆的结构可以表示为:

import java.nio.BufferUnderflowException;

/**
 * @author dalu
 * @create 2020-04-15 22:22
 */
public class BinaryHeap > {
    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到堆中,我们需要在下一个可用的位置创建一个孔,保持堆的完全性。如果X不违反堆的顺序,我们就用X替换这个孔,然后完成操作。否则,我们就将这个孔向着它的父节点进行滑动,直到X可以被插入或者到根节点。
这种方法通常称为渗透,新的元素不断找寻对的位置从而渗透到堆中。

deleteMin

找到最小的元素时容易的,问题是如何删除它。当最小的元素被删除时,根节点将会变为一个孔。由于现在对少了一个元素,因此最后一个元素X就必须被移动。如果X可以被移动到这个孔上,操作就结束。我们需要将孔的孩子节点移动这个孔上,然后不断的下沉这个孔。重复这个操作直到X可以替换这个孔。

你可能感兴趣的:(Priority Queues (Heaps))