Python 每日一记260>>>Java索引最小优先队列实现

什么是索引最小优先队列

在这里插入图片描述
一般的队列我们只能从头开始访问元素,如果我们要直接根据索引进行元素的访问呢?这个时候就需要使用索引优先队列了,本例中使用所用最小优先队列,注意,这里最小指的是最小堆,即根节点比子节点大的堆。

item数组用来装数据;
pq数组用来装数据对应的索引,并且这个数组是按照其对应的元数据的值堆排序的,使用的是最小堆;
qp数组是pq数组的逆序,也就是说两者的索引的和值是互换的,pq的索引是qp的值,pq的值是qp的索引;

pq数组和qp数组有什么用呢?首先我们的堆需要有序,这里采用最小堆的结构,之前的最小优先队列可以访问最小值,那么如果要修改或者访问某个值怎么办呢,只有使用索引的方式了,也就是将每个添加的元素关联一个索引,但是为了堆的有序性,我们就需要调整堆的元素顺序,如果这个时候直接在item上调整,则会使得原始索引和元素的对应关系打乱,怎么办呢,我们可以不改变item,转而使用一个pq数组用来装item元素对应的索引,然后我们对堆的有序操作直接在pq数组上操作,但是底层的比较大小还是根据item的大小进行的,只是相应的改变item元素索引在pq数组中的位置,解决了在满足堆有序的条件下,索引和元素的对应关系后,新的问题是,假如我们要在item中,根据索引改变某个值,那么我们怎么去找这个索引呢,我们可以循环遍历pq数组,找到索引,然后修改,之后在使得堆有序,但是如果数组很长,遍历很费时间,怎么办呢,我们可以创建一个数组qp,qp数组是pq数组的逆序,也就是说两者的索引的和值是互换的,pq的索引是qp的值,pq的值是qp的索引,其实item数组的索引和qp数组的索引是一致的,这样根据item的索引就可以在qp数组找到pq数组的索引,在根据这个索引在pq数组内找到item数组对应的索引。
以上有一个显而易见的环形思维,感觉走了弯路,修改某个值不能直接根据索引在item中修改吗,答案是可以,但是我们真正的队列结构其实是根据pq实现的,也就是说,即便是根据索引在item中修改,我们还是要在pq中解决堆有序的问题,这个时候还是需要根据那个索引,找到我们要在pq中要进行操作的索引,因此还是需要qp,然后再pq中进行操作。

java代码实现

/**
 * 数组实现堆的数据结构
 */

package mypackage;

//堆类
//T extends Comparable<T>,后续的数据才可进行比较
class heap<T extends Comparable<T>> {
    //    实现基础为数组,元素个数N
    private T[] items;
    private int[] pq;
    private int[] qp;
    private int N;

    //    构造方法,注意capacity+1,因为我们第一个元素是从索引1开始的,0处的索引不装数据
//    因此虽然我们传入的容量为capacity,这个capacity表示的是堆的数量,capacity+1才是数组的大小
    public heap(int capacity) {
        this.items = (T[]) new Comparable[capacity + 1];
        this.pq = new int[capacity + 1];
        this.qp = new int[capacity + 1];
        N = 0;
//        初始化为-1.表示没有元素
        for (int i = 0; i < qp.length; i++) {
            qp[i] = -1;
        }
    }

    //堆元素个数
    public int size() {
        return N;
    }

    //是否为空
    public boolean isEmpty() {
        return N == 0;
    }

    //    判断数组元素的大小,i,j在pq中,但是实际上值的比较还是在item中
    public boolean less(int i, int j) {
        return items[pq[i]].compareTo(items[pq[j]]) < 0;
    }

    //    交换数组元素,i,j在pq中
    public void exchange(int i, int j) {
        int temp = pq[i];
        pq[i] = pq[j];
        pq[j] = temp;
//        还要更新qp中的值
        qp[pq[i]] = i;
        qp[pq[j]] = j;
    }

    //    判断k索引的元素是否存在,这里的k是在item中,等价于在qp中
    public boolean isExist(int k) {
//        无元素为-1,不等于-1表示有元素
        return qp[k] != -1;
//        其实也可以return items[k]!=null;
    }

    //    最小元素的索引
    public int minIndex() {
//        因为pq是有序的item元素的索引,所以pq的第一个元素就是item最小元素的索引
        return pq[1];
    }

    //    插入元素,并且关联索引i
//    如果已经存在就直接返回,不允许插入元素。
//    如果不存在,执行插入操作,在item指定索引处插入元素,pq内添加元素的索引,qp是pq的逆序,最后对pq上浮操作
    public void insert(int i, T t) {
        if (isExist(i)) {
            return;
        } else {
            N++;
            items[i] = t;
            pq[N] = i;
            qp[i] = N;
//            注意swim的是pq
            swim(N);
        }
    }

    //    删除最小的元素并返回该元素的索引,其实就是删除根节点的元素
//    先交换第一个元素和最后一个元素,然后删除最后一个元素,这个就是最小的元素
//    因为此时顺序是乱的,然后采用下沉算法,使得堆有序
    public int delminIndex () {
        int minIndex = pq[1];
        exchange(1, N);
        qp[pq[N]] = -1;
        pq[N] = -1;
        items[minIndex] = null;
        N--;
        sink(1);
        return minIndex;
    }

    //    删除指定指定索引的元素并返回
    public T del(int i) {
        int k = qp[i];
        exchange(k, N);
        qp[pq[N]] = -1;
        pq[N] = -1;
        T itemsi=items[i];
        items[i] = null;
        N--;
//        注意因为不一定是首尾,因此要进行下沉和上浮
        sink(k);
        swim(k);
        return itemsi;
    }

    //  把与索引i处的元素修改为t
    public void changeItem(int i, T t) {
        items[i] = t;
        int k = qp[i];
//        注意因为不一定是首尾,因此要进行下沉和上浮
        sink(k);
        swim(k);
    }

    //浮算法swim让堆的元素有序,注意是对pq进行操作的
//    k位置处的父节点位置为k/2,左子节点为2k,右子节点位置为2k+1
//    浮算法swim就是不断的循环比较,如果这个节点小于父节点,就交换
    public void swim(int k) {
        while (k > 1) {
            if (less(k, k / 2)) {
                exchange(k, k / 2);
            } else {
                break;
            }
//            每次k=k/2
            k = k / 2;
        }
    }

    //    下沉算法,使得堆有序,这是比较麻烦的算法,注意是对pq进行操作的
//    思路就是,从某个节点开始,向下循环
//    如果有右节点,就比较左右节点的大小,选取较小的一个最为较小值,如果没有右节点,那么左节点作为较小值
//    如果当前节点比较小的节点大,就交换,且让当前索引设置为较小值得索引,继续下沉循环
    public void sink(int k) {
        while (2 * k <= N) {
            int min;
            if (2 * k + 1 <= N) {
                if (less(2 * k, 2 * k + 1)) {
                    min = 2 * k;
                } else {
                    min = 2 * k + 1;
                }
            } else {
                min = 2 * k;
            }

            if (less(k, min)) {
                break;
            }
            exchange(k, min);
            k = min;
        }
    }
}


//测试
public class MyJava {

    public static void main(String[] args) {
        heap<String> heap = new heap<>(10);
        heap.insert(1, "A");
        heap.insert(5, "F");
        heap.insert(2, "B");
        heap.insert(3, "C");
        heap.insert(4, "D");

//        添加元素后,堆元素个数
        System.out.println("元素个数:" + heap.size());
        //        循环弹出队列中最大的值

//        更改元素
        heap.changeItem(5,"E");

        //删除指定索引的元素,并返回
        System.out.println("删除的元素为:"+heap.del(1));

        System.out.print("依次弹出最小元素的索引:");
        while (!heap.isEmpty()) {
            System.out.print(heap.delminIndex () + ",");
        }
        System.out.println();
        System.out.println("是否为空:" + heap.isEmpty());
    }
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值