索引堆

1 原理

    堆(heap)是一种常见的数据结构,经常用来实现优先队列。其中最常见的是二叉堆(binary heap)。由于它特殊的性质(二叉满树)所以可以用数组高效的实现。
    堆还能用来进行排序,堆排序(heap sort)具有快速(复杂度O(N * logN)),稳定的特点,尤其是非常稳定,因此适用于某些需要排序稳定性的场合。
    But,普通的二叉堆有两个缺陷:
    1,在堆中的元素体积非常大的情况下,经常性的移动元素是低效的。
    2,如果在堆的使用过程中,堆中的元素的值要改变,则普通堆对此无能为力。简单的说,一个元素如果进入堆之后,它的值就不能改变了,否则会影响堆的性质。
    第一个缺陷还能用类似指针排序的技术解决,但是第二个缺陷不采用特殊的技术是没办法解决的。然而在一些场合,堆中元素的值确实需要改变。因此索引堆(index heap)闪亮登场~~
    所谓索引堆,简单地说,就是在堆里头存放的不是数据,而是数据所在数组的索引,也就是下标(本文中索引和下标是一个意思,互相换用),根据数据的某种优先级来调整各个元素对应的下标在堆中的位置。本质上来说索引堆也是堆,提供堆的接口。由于索引堆最终用来实现优先队列,所以又可以叫索引优先队列(index priority queue)。接下来就说明索引堆的实现,以及为什么索引堆能解决刚才说的两个缺陷。

2 应用

    与普通二叉堆相比,索引堆本身也有两个不算缺点的缺点:一,实现稍微复杂;二,因为开辟了两个数组pq和map,因此需要占用更多空间。但索引堆近乎完美地解决了普通堆了两个缺陷,这两个缺点就显得微不足道了。
    索引堆在解决图算法的最小生成树(minimum spanning tree)和 最短路径(shortest path)问题中都有很优雅的应用(这也就是为什么我会知道这个东西- -。。。)最小生成树和Prim算法的索引堆实现和最短路径的Dijkstra算法的索引堆的实现几乎一模一样。

3 实现

public class IndexMaxHeap {
    private int[] data;
    private int[] indexes;
    private int count;
    private int capacity;

    /**
     * 构造函数 构造一个空堆 可容纳capacity个元素
     * 
     * @param capacity
     */
    public IndexMaxHeap(int capacity) {
        data = new int[capacity + 1];
        indexes = new int[capacity + 1];
        count = 0;
        this.capacity = capacity;
    }

    /**
     * 返回索引堆中元素个数
     * 
     * @return
     */
    public int size() {
        return count;
    }

    /**
     * 判断索引堆是否为空
     * 
     * @return
     */
    public boolean isEmpty() {
        return count == 0;
    }

    /**
     * 向索引堆中添加一个新的元素 新元素索引为i 对用户而言,索引从0开始
     * 
     * @param i
     * @param k
     */
    public void insert(int i, int k) {
        assert (count + 1 < capacity);
        assert (i + 1 >= 1 && i + 1 <= capacity);

        i += 1;
        data[i] = k;
        indexes[count + 1] = i;
        count++;

        shiftUp(count);
    }
    /**
     * 获取索引堆中堆顶元素 
     * @return
     */

    public int getMax(){
        assert count>0;
        return data[indexes[1]];
    }

    //从索引堆中取出堆顶元素的索引
    public int extractMaxIndex(){
        assert count>0  ;
        int ret =indexes[1]-1;
        swapIndexes(1,count);
        count--;
        shiftDown(1);
        return ret;
    }

    /**
     * 从索引堆中取出堆顶元素 即索引堆中存储最大数据
     * @return
     */
    public int extractMax(){
        assert count>0;

        int ret = data[indexes[1]];
        swapIndexes(1,count);
        count--;
        shiftDown(1);
        return ret;
    }

    /**
     * 将最大索引堆中索引为i的元素修改为v
     * @param i
     * @param v
     */
    public void change(int i,int v){
        i+=1;
        data[i]=v;
        for(int j=1;j<=count;j++){
            if(indexes[j]==i){
                shiftUp(j);
                shiftDown(j);
            }
        }
    }
    /**
     * 辅助函数*************************************
     * ******************************************
     */

    private void swapIndexes(int i,int j){
        int t = indexes[i];
        indexes[i]=indexes[j];
        indexes[j]=t;
    }
    private void shiftDown(int k){
        while(2*k<=count){
            int j=2*k;
            if(j+1<=count && data[indexes[j+1]]>data[indexes[j]])
                j++;
            if(data[indexes[k]]>data[indexes[j]])
                break;

            swapIndexes(k,j);
            k=j;
        }
    }

    /**
     * 索引堆中,数据之间的比较根据data 实际操作的是索引 即:比较用data 更改用index
     * 
     * @param k
     *            :k是count 就是数组元素的个数 也是当前indexes的索引
     */
    private void shiftUp(int k) {
        while (k > 1 && data[indexes[k / 2]] < data[indexes[k]]) {
            swapIndexes(k,k/2);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值