数据结构——堆和优先级队列

堆和优先级队列是什么?

堆是基于完全二叉树的自适应的数据结构,插入删除会重新调整堆内数据

  • 父节点大于等于其子节点的堆叫最大堆
  • 父节点小于等于其子节点的堆叫最小堆

使用堆可以实现优先级队列,最大堆添加元素只能添加到尾部,删除元素只能删除头部,增删后会自动调整为最大堆

最大堆实现

  • MaxHeap() 创建指定元素数组,后面通过add构建最大堆
  • MaxHeap(int[]) 根据传入数组构建最大堆
  • add()、remove()增删元素后,需要重新构建最大堆

maxHeapChange()为构建最大堆方法,具体为

  • 从右往左从下往上调整所有父节点(非叶节点),数组从0开始,最后一个节点为arr.length - 1,除以2即是最后一个父节点
  • 对比父节点和它的左右节点,从左右节点选择大的与父节点比较,若大于则交换
  • 交换后递归子树,因为交换后可能导致子树不符合最大堆

sort()采用堆排序返回排好的数组,具体为

  • 依次将最后一个节点(叶节点)和根节点交换(即将最大的放在最后)
  • 从根节点往下调整节点(交换后不符合最大堆,需重新构建),此次构建不包括最后节点(传入参数为arr.length - 1)
class MaxHeap {
    private int[] elements;
    private int size;

    public MaxHeap() {
        elements = new int[7];
        size = 0;
    }

    public MaxHeap(int[] array) {
        elements = array;
        size = array.length;
        maxHeapChange();
    }

    public void add(int value) {
        elements[size] = value;
        size++;
        maxHeapChange();
    }

    private void maxHeapChange() {
        for (int i = (size - 1) / 2; i >= 0; i--) {
            buildMaxHeap(size, i);
        }
    }

    public int remove() {
        int temp = elements[0];
        elements[0] = elements[size - 1];
        elements[size - 1] = 0;
        size--;
        maxHeapChange();
        return temp;
    }

    public int peek() {
        return elements[0];
    }

    public int[] sort() {
        for (int i = size - 1; i > 0; i--) {
            int temp = elements[0];
            elements[0] = elements[i];
            elements[i] = temp;
            buildMaxHeap(i, 0);
        }
        return elements;
    }

    private void buildMaxHeap(int size, int parent) {

        int leftChild = 2 * parent + 1;
        int rightChild = 2 * parent + 2;
        int max = parent;

        if (leftChild < size && elements[leftChild] > elements[max]) {
            max = leftChild;
        }
        if (rightChild < size && elements[rightChild] > elements[max]) {
            max = rightChild;
        }

        if (max != parent) {
            int temp = elements[parent];
            elements[parent] = elements[max];
            elements[max] = temp;
            buildMaxHeap(size, max);
        }
    }

    public static void array2CompleteBinaryTree(int[] array) {
        int treeDepth = (int) (Math.log(array.length) / Math.log(2)) + 1;
        StringBuilder stringBuilder = new StringBuilder();
        int space = (int) (Math.pow(2, treeDepth) / 2 - 1);
        int move = 0;
        for (int i = 0; i < treeDepth; i++) {
            for (int j = 0; j < Math.pow(2, i); j++) {
                for (int k = 0; k < space; k++) {
                    stringBuilder.append("*");
                }
                if (move < array.length) {
                    if (move == (Math.pow(2, i + 1) - 2)) {
                        stringBuilder.append(array[move]);
                    } else {
                        stringBuilder.append(array[move] + "*");
                    }
                }
                for (int k = 0; k < space; k++) {
                    stringBuilder.append("*");
                }
                move++;
            }
            space /= 2;
            stringBuilder.append("\n");
        }
        System.out.println(stringBuilder.toString());
    }

    @Override
    public String toString() {
        int treeDepth = (int) (Math.log(elements.length) / Math.log(2)) + 1;
        StringBuilder stringBuilder = new StringBuilder();
        int space = (int) (Math.pow(2, treeDepth) / 2 - 1);
        int move = 0;
        for (int i = 0; i < treeDepth; i++) {
            for (int j = 0; j < Math.pow(2, i); j++) {
                for (int k = 0; k < space; k++) {
                    stringBuilder.append("*");
                }
                if (move < elements.length) {
                    if (move == (Math.pow(2, i + 1) - 2)) {
                        stringBuilder.append(elements[move]);
                    } else {
                        stringBuilder.append(elements[move] + "*");
                    }
                }
                for (int k = 0; k < space; k++) {
                    stringBuilder.append("*");
                }
                move++;
            }
            space /= 2;
            stringBuilder.append("\n");
        }
        return stringBuilder.toString();
    }
}

array2CompleteBinaryTree()和toString()方法是用于打印树型结构,只能对齐打印10以内的元素,具体为

  • 获取数的深度treeDepth
  • 获取最后一层数据总量Math.pow(2, treeDepth),除以2减1得到需要对齐打印的星号
  • 循环i用于打印每层数据,循环j用于定位move的位置(即要打印的元素),循环k用于打印数据前后的空星号

最大堆测试

如下是基于MaxHeap(int[]) 根据传入数组构建最大堆的测试

int[] array = {1, 7, 3, 2, 9, 8};
System.out.print("原来的数组" + Arrays.toString(array));
System.out.print("转化为如下完全二叉树\n");
MaxHeap.array2CompleteBinaryTree(array);
System.out.println("-------------------");
System.out.print("原来的数组" + Arrays.toString(array));
MaxHeap maxHeap = new MaxHeap(array);
System.out.print("转化为如下最大堆\n");
System.out.println(maxHeap);
System.out.println("-------------------");
System.out.print("利用最大堆排序后的数组");
int[] sortArray = maxHeap.sort();
System.out.print(Arrays.toString(sortArray));
System.out.print("转化为如下完全二叉树\n");
MaxHeap.array2CompleteBinaryTree(sortArray);
System.out.println("-------------------");

打印如下

原来的数组[1, 7, 3, 2, 9, 8]转化为如下完全二叉树
***1***
*7***3*
2*9*8*
-------------------
原来的数组[1, 7, 3, 2, 9, 8]转化为如下最大堆
***9***
*7***8*
2*1*3*
-------------------
利用最大堆排序后的数组[1, 2, 3, 7, 8, 9]转化为如下完全二叉树
***1***
*2***3*
7*8*9*

如下是基于add、move方法的测试

System.out.print("往最大堆尾部添加元素1 7 3 2 9 8");
MaxHeap maxHeap = new MaxHeap();
maxHeap.add(1);
maxHeap.add(7);
maxHeap.add(3);
maxHeap.add(2);
maxHeap.add(9);
maxHeap.add(8);
System.out.print(" 转化为如下最大堆\n");
System.out.println(maxHeap);
System.out.print("从最大堆取出头部元素" + maxHeap.remove() + "后最大堆调整为\n");
System.out.println(maxHeap);

打印如下

往最大堆尾部添加元素1 7 3 2 9 8 转化为如下最大堆
***9***
*7***8*
1*2*3*0
从最大堆取出头部元素9后最大堆调整为
***8***
*7***3*
1*2*0*0

最小堆实现

最小堆和最大堆实现原理相同,只不过是修改比较规则,不再赘述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值