• Java数据结构之堆(Heap)



    提示:以下是本篇文章正文内容,Java系列学习将会持续更新

    一、基本概念

    1. 堆在逻辑上就是一棵完全二叉树
    2. 堆在物理上是储存在数组中。
    3. 满足任意根节点值>=子树节点值,叫做大根堆、最大堆。
    4. 满足任意根节点值<=子树节点值,叫做小根堆、最小堆。
    5. 堆的基本作用是快速找集合中的最值

    在这里插入图片描述

    回到目录…

    二、上浮操作(siftUp)

    	/** 元素上浮操作 **/
        private void siftUp(int k) {
            //确认终止条件,当到达顶部时不需要继续上浮
            //并且只有该节点值>父节点时,才进行上浮
            while(k > 0 && list.get(k) > list.get((k - 1) / 2)){
                //交换该节点和父节点的val值
                swap(k, (k - 1) / 2);
                //更新索引为父节点的索引
                k = (k - 1) / 2;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    回到目录…

    三、下沉操作(siftDown)

    原理
    该节点和它的左右孩子比较,如果该节点值<孩子的值,则swap(cur.val, max(left.val,right.val));
    并且继续向下比较,再让刚刚交换过的孩子节点和它的孩子作比较,直到叶子节点

    	/** 元素下沉操作 **/
    	private void siftDown(int k) {
            // 在确保有孩子的情况下,下沉
            while(2 * k + 1 < list.size()) {
                // 先认为左孩子是比较大的孩子
                int maxChild = 2 * k + 1;
                // 如果存在右孩子,且右孩子 > 左孩子,则 maxChild 记录右孩子的位置
                if(2 * k + 2 < list.size() && list.get(maxChild) < list.get(maxChild + 1)){
                    maxChild += 1;
                }
                // 比较自己和大孩子的值
                if(list.get(k) < list.get(maxChild)){
                    swap(k, maxChild);
                    k = maxChild;
                }else{
                    // 如果此时符合堆性质,则不需要再向下比较了,结束循环
                    break;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    回到目录…

    四、数组堆化

    	//数组堆化,O(N)
        public MaxHeap(int[] arr) {
            this.list = new ArrayList<>(arr.length);
            //将元素放入list中
            for(int i : arr){
                list.add(i);
            }
            //从最后一个非叶子节点开始倒着下沉每一个节点,直到堆顶
            for (int i = (arr.length - 2) / 2; i >= 0; i --) {
                siftDown(i);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    回到目录…

    五、实现大根堆

    /**
     * 自己实现大根堆
     */
    public class MaxHeap {
    
        private List<Integer> list;
    
        public MaxHeap() {
            this(10);
        }
        public MaxHeap(int size) {
            list = new ArrayList<>(size);
        }
        // 数组堆化
        public MaxHeap(int[] arr) {
            list = new ArrayList<>(arr.length);
            for(int i : arr) {
                list.add(i);
            }
            // 从最后一个非叶子节点的位置往上做下沉操作
            for (int i = (arr.length - 2) / 2; i >= 0; i --) {
                siftDown(i);
            }
        }
    
        // 上浮操作
        private void siftUp(int k) {
            // 和父节点比较大小做交换
            while(k > 0 && list.get(k) > list.get((k - 1) / 2)){
                swap(k, (k - 1) / 2);
                k = (k - 1) / 2;
            }
        }
    
        // 下沉操作
        private void siftDown(int k) {
            // 在确保有孩子的情况下,下沉
            while(k <= (list.size() - 2) / 2) {
                // 先认为左孩子是比较大的孩子
                int maxChild = 2 * k + 1;
                // 如果存在右孩子,且右孩子 > 左孩子,则 maxChild 记录右孩子的位置
                if(2 * k + 2 < list.size() && list.get(maxChild) < list.get(maxChild + 1)){
                    maxChild += 1;
                }
                // 比较自己和大孩子的值
                if(list.get(k) < list.get(maxChild)){
                    swap(k, maxChild);
                    k = maxChild;
                }else{
                    // 如果此时符合堆性质,则不需要再向下比较了,结束循环
                    break;
                }
            }
        }
    
        // 添加元素
        public void add(int val) {
            // 先插到最后面
            list.add(val);
            // 从最后一节点开始上浮
            siftUp(list.size() - 1);
        }
    
        // 弹出最大堆的最大值
        public int extractMax() {
            int max = list.get(0);
            // 交换首尾元素的值
            swap(0, list.size() - 1);
            // 删掉最后一个元素的值(最大值)
            list.remove(list.size() - 1);
            // 第一个元素做下沉操作
            siftDown(0);
            return max;
        }
    
        // 交换
        private void swap(int i, int j) {
            int tmp = list.get(i);
            list.set(i, list.get(j));
            list.set(j, tmp);
        }
    
        @Override
        public String toString() {
            return list.toString();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    回到目录…


    总结:
    提示:这里对文章进行总结:
    以上就是今天的学习内容,本文是Java数据结构的学习,认识了堆,学习了堆的上浮、下沉操作原理,以及自己手写一个大根堆。之后的学习内容将持续更新!!!

  • 相关阅读:
    我参加NVIDIA Sky Hackathon 训练文件的路径设置
    解压缩和压缩命令
    【云备份】
    法国A+ 法国VOC标签最高环保级别
    软件测试 —— 常见的自动化测试架构!
    memcpy内存拷贝函数
    在国内企业做数据治理,建议您考个DAMA-CDGA/CDGP证书
    ES5+ 和 ES6
    上周热点回顾(10.24-10.30)
    centos7下nginx的安装与配置
  • 原文地址:https://blog.csdn.net/qq15035899256/article/details/126164004