索引堆简单介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiyei2009/article/details/80207342

1 索引堆基本概念

堆的基本概念不用多种介绍了,但是对于堆来说,有时堆结点存储的数据结构交换时间及空间比较大,或者结点存储的是比较复杂的数据结构。这个时候,我们可以采用索引堆这种数据结构来解决

索引堆:在堆的基础上用一个索引数组来存储数据元素的位置,即索引堆里面包含两个数组,如下:
建堆之前:
这里写图片描述
建堆之后
这里写图片描述
在这个图中,有两个数组,data[]存储真实的数据元素,index[]存储data的索引,并且index构成一个特殊的堆。
例如:index[1]是堆顶的第一个元素,index[1] = 10,表示取data数组索引10的元素62。可以看到62确实是最大的一个元素,这是一个最大的索引堆

索引堆的两大重要特点
1 比较的是真实元素的大小,交换的是对应的索引index的位置,真实的data数组并没有任何改变
2 访问数据元素,必须先找到其索引,即先找到index[]的值
注意:data[]数组我们是从1开始存储的,但是真实的索引是从0开始的

索引堆在图论算法中求最短路径以及最小生成树中都有应用。

2 最大索引堆

如上定义,最大的索引堆就是堆顶元素是数组最大元素的索引,即index[1] 的值是最大数组元素的索引。一般一个完整的最大索引堆定义如下:

package com.qiyei.heap;

import com.qiyei.util.LogUtil;

/**
 * @author Created by qiyei2015 on 2018/3/31.
 * @version: 1.0
 * @email: 1273482124@qq.com
 * @description: 索引最大堆indexArray[]构成堆  最大特点,比较是比较pq[]的值,交换是交换indexArray[]的值
 */
public class IndexMaxHeap<T extends Comparable<T>> extends BaseHeap{

    /**
     * 索引 indexArray的值是pq[]的下标
     */
    private int[] indexArray;

    public IndexMaxHeap() {
        super();
        indexArray = new int[0];
    }

    public IndexMaxHeap(int max) {
        super(max);
        indexArray = new int[max + 1];
    }

//    public IndexMaxHeap(Comparable[] array) {
//        super(array);
////        System.arraycopy(array,0,in,1,array.length);
//    }

    /**
     * 插入元素到最大索引堆
     * @param i 从0 开始计算
     * @param t
     */
    public void insert(int i,T t){
        if (i < 0 || i >= N){
            return;
        }
        i += 1;
        //记录数据 pq[] 下标i为t
        pq[i] = t;
        //记录索引位置 从1开始记录索引
        count = count + 1;
        indexArray[count] = i;
        swimIndex(count);
    }

    /**
     * 删除最大的元素 从最大堆取出最大元素
     * @return
     */
    public T delMax(){
        //先找到索引,根据索引找到数
        int index = indexArray[1];
        T t = (T) pq[index];
        exchIndex(1,count);
        count--;
        sinkIndex(1);
        return t;
    }

    /**
     * 获取最大的堆顶的元素
     * @return
     */
    public T getMax(){
        return (T) pq[indexArray[1]];
    }

    /**
     * 从堆中取出最大元素的索引,不删除元素
     * @return
     */
    public int getIndexMax(){
        return indexArray[1] - 1;
    }

    /**
     * 取出最大堆堆顶元素,并删除该元素
     * @return
     */
    public int extractIndexMax(){
        int index = indexArray[1] - 1;
        //交换索引
        exchIndex(1,count);
        count--;
        sinkIndex(1);
        return index;
    }

    /**
     * 获取最大索引堆索引为i的元素的值
     * @param i
     * @return
     */
    public T getItemIndex(int i){
        return (T) pq[indexArray[i+1]];
    }

    /**
     * 将堆中索引为i的元素值替换为t
     * @param i
     * @param t
     */
    public void replace(int i , T t){
        i++;
        pq[i] = t;
        for (int j = 0 ; j < count ; j++){
            if (indexArray[j] == i){
                sinkIndex(j);
                swimIndex(j);
                return;
            }
        }
    }

    /**
     * 堆的上浮,解决子节点比父结点大的问题,少交换,优化堆的上浮过程
     * 比较是比较pq[]的值,交换是交换indexArray[]的值
     * @param k 节点k上浮
     */
    private void swimIndex(int k){
        //取索引数组的值,然后得到pq的索引
        int temp = indexArray[k];
        //子节点比父结点大
        while (k > 1 && less(indexArray[k/2],indexArray[k])){
            //父结点移到子节点 子节点暂存 不用每次都去新建一个临时变量来交换
            indexArray[k] = indexArray[k/2];
            indexArray[k/2] = temp;
            k = k/2;
        }
    }

    /**
     * 堆的下沉 父结点小于子节点,将父节点与较大的子节点交换
     * 比较是比较pq[]的值,交换是交换indexArray[]的值
     * @param k
     */
    private void sinkIndex(int k) {
        int temp = indexArray[k];
        //判断有左孩子,有孩子就行
        while (2 * k <= count) {
            int j = 2 * k; //此轮循环中 k 与j交换
            if ((j + 1) <= count && less(indexArray[j], indexArray[j + 1])) {
                j++; //更新为右孩子
            }
            //父结点大于子节点
            if (!less(indexArray[k], indexArray[j])) {
                break;
            }

            //将子节点移到父结点,父结点移到子节点 不用每次都去新建一个临时变量来交换
            indexArray[k] = indexArray[j];
            indexArray[j] = temp;
            k = j; //更新k的位置
        }
    }

    /**
     * 交换索引位置
     * @param i
     * @param j
     */
    private void exchIndex(int i, int j) {
        int temp = indexArray[i];
        indexArray[i] = indexArray[j];
        indexArray[j] = temp;
    }

    /**
     * 打印数组
     */
    public void printData(){
        for (int i = 1 ; i < pq.length ;i++){
            LogUtil.println("data:[ " + (i - 1) + " " + pq[i] + " ]");
        }
    }
}

注意点:可以看到在swimIndex(int k)和sinkIndex(int k)我们比较的是真实的数据的大小(通过indexarray[k]得到pq数组的索引)。但是交换的是indexArray的数组

测试代码如下:

 /**
     * 测试最大索引堆
     */
    private static void testIndexMaxHeap(){
        //索引堆测试
        IndexMaxHeap<Integer> indexMaxHeap = new IndexMaxHeap<>(100);
        for (int i = 0 ; i < 100;i++){
            int value = random.nextInt(100);
            if ( i == 5){
                LogUtil.println("indexMaxHeap index:" + i  + " "+ value);
            }
            indexMaxHeap.insert(i,value);
        }
        LogUtil.println("indexMaxHeap size:" + indexMaxHeap.size());
        LogUtil.println("indexMaxHeap getIndexMax():" + indexMaxHeap.getIndexMax());
        LogUtil.println("indexMaxHeap getMax:" + indexMaxHeap.getMax());

        LogUtil.println("indexMaxHeap getItemIndex(5):" + indexMaxHeap.getItemIndex(5));

        LogUtil.println("indexMaxHeap data:");
        indexMaxHeap.printData();

        for (int i = 0 ; i < 100;i++){
            LogUtil.println("[ " + indexMaxHeap.getIndexMax() + " " + indexMaxHeap.delMax() + " ]");
        }
    }

3 最小索引堆

如上定义,最小的索引堆就是堆顶元素是数组最小的元素索引,即index[1] 的值是最小数组元素的索引。一般一个完整的最小索引堆定义如下:

package com.qiyei.heap;

import com.qiyei.util.LogUtil;

/**
 * @author Created by qiyei2015 on 2018/4/19.
 * @version: 1.0
 * @email: 1273482124@qq.com
 * @description: 索引最小堆indexArray[]构成堆  最大特点,比较是比较pq[]的值,交换是交换indexArray[]的值
 */
public class IndexMinHeap<T extends Comparable<T>> extends BaseHeap {

    /**
     * 索引 indexArray的值是pq[]的下标
     */
    private int[] indexArray;

    public IndexMinHeap() {
        super();
        indexArray = new int[0];
    }

    public IndexMinHeap(int max) {
        super(max);
        indexArray = new int[max + 1];
    }

//    public IndexMinHeap(Comparable[] array) {
//        super(array);
//    }

    /**
     * 插入元素到最小索引堆
     * @param i 从0 开始计算
     * @param t
     */
    public void insert(int i,T t){
        if (i < 0 || i >= N){
            return;
        }
        i += 1;
        //记录数据 pq[] 下标i为t
        pq[i] = t;
        //记录索引位置 从1开始记录索引
        count = count + 1;
        indexArray[count] = i;
        //上浮count
        swimIndex(count);
    }

    /**
     * 删除最小的元素 从最小堆取出堆顶元素
     * @return
     */
    public T delMin(){
        //先找到索引,根据索引找到数
        int index = indexArray[1];
        T t = (T) pq[index];
        exchIndex(1,count);
        count--;
        //下沉堆顶元素
        sinkIndex(1);
        return t;
    }

    /**
     * 获取最小的堆顶的元素
     * @return
     */
    public T getMin(){
        return (T) pq[indexArray[1]];
    }

    /**
     * 从堆中取出最小元素的索引,不删除元素
     * @return
     */
    public int getIndexMin(){
        return indexArray[1] - 1;
    }

    /**
     * 取出最小堆堆顶元素,并删除该元素
     * @return
     */
    public int extractIndexMin(){
        int index = indexArray[1] - 1;
        //交换索引
        exchIndex(1,count);
        count--;
        //下沉
        sinkIndex(1);
        return index;
    }

    /**
     * 获取最大索引堆索引为i的元素的值
     * @param i
     * @return
     */
    public T getItemIndex(int i){
        return (T) pq[indexArray[i+1]];
    }

    /**
     * 将堆中索引为i的元素值替换为t
     * @param i
     * @param t
     */
    public void replace(int i , T t){
        i++;
        pq[i] = t;
        for (int j = 0 ; j < count ; j++){
            if (indexArray[j] == i){
                sinkIndex(j);
                swimIndex(j);
                return;
            }
        }
    }

    /**
     * 是否包含w结点
     * @param w
     * @return
     */
    public boolean contains(int w){
        w++;
        if (w >= 1 && w <= count){
            return true;
        }
        return false;
    }

    /**
     * 堆的上浮,解决子节点比父结点小的问题,少交换,优化堆的上浮过程
     * 比较是比较pq[]的值,交换是交换indexArray[]的值
     * @param k 节点k上浮
     */
    private void swimIndex(int k){
        //取索引数组的值,然后得到pq的索引
        int temp = indexArray[k];
        //如果子节点比父节点小,就交换二者的索引数组
        while (k > 1 && less(indexArray[k],indexArray[k/2])){
            //父结点移到子节点 子节点暂存 不用每次都去新建一个临时变量来交换
            indexArray[k] = indexArray[k/2];
            indexArray[k/2] = temp;
            k = k/2;
        }
    }

    /**
     * 堆的下沉 父结点大于子节点,将父节点与较小的子节点交换
     * 比较是比较pq[]的值,交换是交换indexArray[]的值
     * @param k
     */
    private void sinkIndex(int k) {
        int temp = indexArray[k];
        //判断有左孩子,有孩子就行
        while (2 * k <= count) {
            int j = 2 * k; //此轮循环中 k 与j交换 交换较小的孩子
            if ((j + 1) <= count && less(indexArray[j+1], indexArray[j])) {
                j++; //更新为右孩子
            }
            //父结点小于子节点
            if (less(indexArray[k], indexArray[j])) {
                break;
            }

            //将子节点移到父结点,父结点移到子节点 不用每次都去新建一个临时变量来交换
            indexArray[k] = indexArray[j];
            indexArray[j] = temp;
            k = j; //更新k的位置
        }
    }

    /**
     * 交换索引位置
     * @param i
     * @param j
     */
    private void exchIndex(int i, int j) {
        int temp = indexArray[i];
        indexArray[i] = indexArray[j];
        indexArray[j] = temp;
    }

    /**
     * 打印数组
     */
    public void printData(){
        for (int i = 1 ; i < pq.length ;i++){
            LogUtil.println("data:[ " + (i - 1) + " " + pq[i] + " ]");
        }
    }
}

如最大索引堆一样,比较的是真实的数据元素,交换的是索引数组元素
测试代码如下:

    /**
     * 测试最小索引堆
     */
    private static void testIndexMinHeap(){
        //索引堆测试
        IndexMinHeap<Integer> indexMinHeap = new IndexMinHeap<>(100);
        for (int i = 0 ; i < 100;i++){
            int value = random.nextInt(100);
            if ( i == 5){
                LogUtil.println("indexMinHeap index:" + i  + " "+ value);
            }
            indexMinHeap.insert(i,value);
        }
        LogUtil.println("indexMinHeap size:" + indexMinHeap.size());
        LogUtil.println("indexMinHeap getIndexMin():" + indexMinHeap.getIndexMin());
        LogUtil.println("indexMinHeap getMin:" + indexMinHeap.getMin());

        LogUtil.println("indexMinHeap getItemIndex(5):" + indexMinHeap.getItemIndex(5));
        LogUtil.println("indexMinHeap data:");
        indexMinHeap.printData();
        for (int i = 0 ; i < 100;i++){
            LogUtil.println("[ " + indexMinHeap.getIndexMin() + " " + indexMinHeap.delMin() + " ]");
        }
    }

最后:
源代码github https://github.com/qiyei2015/Algorithms heap部分

猜你喜欢

转载自blog.csdn.net/qiyei2009/article/details/80207342