堆的应用——优先级队列

接上文: 堆排序Heap Sort——浅显易懂+Java实现

        堆分最小堆最大堆,优先级队列也分最小优先级队列和最大优先级队列。先来介绍一下优先级队列:

        优先级队列是用来维护由一组元素构成的集合S的数据结构,这一组元素中的每一个都有一个关键字key,一个最大优先级队列支持以下操作:

        Insert(S, x):把元素x插入S中

        Maximun(S):返回S中具有最大关键字的元素

        ExtractMax(S):去掉并返回S中的具有最大关键字的元素

        IncreaseKey(S, x, k):将元素x的值增加到k,这里的k必须大于等于x

        最大优先级队列可以用来对计算机内部作业进行调度。当一个作业做完或者被中断的时候,就由ExtractMax来选出合适的优先级队列。在任何时候,一个新作业都可以有Inset来插入到队列中去。最小优先级其实还有更多的不同的应用,这个我在之后的博客会讲。

接下来是上述四个操作的实现代码:

首先堆的数据结构贴一下:为什么用ArrayList不用数组,这在堆排序的博客讲过,而其实就是在优先级队列的应用中,存在着数组完成不了的事情

    public class Heap {  
      
        private ArrayList<Integer> A;  
      
        private int heapSize;  
      
        public ArrayList<Integer> getA() {  
            return A;  
        }  
      
        public void setA(ArrayList<Integer> a) {  
            A = a;  
        }  
      
        public int getHeapSize() {  
            return heapSize;  
        }  
      
        public void setHeapSize(int heapSize) {  
            this.heapSize = heapSize;  
        }  
      
    }  

以下HeapMaximum代码,没什么好解释的,最大堆的根结点就是最大元素,复杂度O(1)

/**
     * 返回堆中具有最大关键字的元素
     * @param heap 堆
     * @return 堆的最大元素
     */
    public int HeapMaximum(Heap heap) {
        return heap.getA().get(0);
    }

以下HeapExtractMax代码,堆大小小于1的时候,队列为空,抛出异常,不然就交换根结点和末尾的一个元素,然后使用MaxHeapify保持最大堆性质,这和HeapSoet的过程很类似。复杂度是O(lgn),这个复杂度来自于MaxHeapify的复杂度。

/**
     * 去掉并返回堆中的具有最大关键字的元素
     * @param heap 堆
     * @return 堆的最大元素
     * @throws Exception 堆大小小于1的时候,队列为空,抛出异常
     */
    public int HeapExtractMax(Heap heap) throws Exception {
        ArrayList<Integer> A = heap.getA();
        int heapSize = heap.getHeapSize();
        if (heapSize < 1) {
            throw new Exception("heap underflow");
        }
        int max = A.get(0);
        A.set(0, A.get(heapSize - 1));
        heap.setHeapSize(heapSize - 1);
        this.MaxHeapify(heap, 0);
        return max;
    }

以下HeapIncreaseKey代码,如果key小于下标i的元素值,抛出异常。否则就设置下标i为key,只有这个被改变的值可能会影响堆的性质,所以就与父结点做比较,如果比父结点大,那就和父结点交换。然后再和新的父结点做比较,直到小于父结点。复杂度O(Ign),因为最坏情况是n个元素,从最底下一层交换到根结点。

/**
     * 将元素i的值增加到key,这里的key必须大于等于元素i的值
     * @param heap 堆
     * @param i 目标元素的下标
     * @param key 目标值
     * @throws Exception 如果key小于下标i的元素值,抛出异常
     */
    public void HeapIncreaseKey(Heap heap, int i, int key) throws Exception {
        ArrayList<Integer> A = heap.getA();
        int heapSize = heap.getHeapSize();
        if (key < A.get(i)) {
            throw new Exception("new key is smaller than current key!");
        }
        A.set(i,key);
        while (i > 0 && A.get(this.parent(i)) < A.get(i)) { // 跟父节点比较
            int temp = A.get(this.parent(i));
            A.set(this.parent(i), A.get(i));
            A.set(i, temp);
            i = this.parent(i);
        }
    }

把元素i从4增大到15的过程大致如下:


以下MaxHeapInsert代码,先让heapSize增加1,然后添加一个最小元素放在末尾,再调用HeapIncreaseKey即可。复杂度为O(lgn)

/**
     * 把元素key插入堆中
     * @param heap 堆
     * @param key 目标值
     * @throws Exception HeapIncreaseKey所产生的
     */
    public void MaxHeapInsert(Heap heap, int key) throws Exception {
        ArrayList<Integer> A = heap.getA();
        int heapSize = heap.getHeapSize();
        heap.setHeapSize(++heapSize);
        A.add(Integer.MIN_VALUE);
        HeapIncreaseKey(heap, heapSize - 1, key);
    }

所以,我们可以在O(lgn)的时间里,完成任意的优先级队列操作,效率还是相当高的。


上面有所有方法的单元测试:https://github.com/qjkobe/IntroductionToAlgorithms


如果有啥问题记得跟我说哈


原文:http://blog.csdn.net/qj30212/article/details/52454270

我略微修改了一下代码和描述






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值