0
点赞
收藏
分享

微信扫一扫

算法基础入门 - 6、复杂排序算法-堆结构介绍+自定义堆+java堆

狐沐说 2022-04-03 阅读 91

堆结构

堆结构就是用数组实现的特殊完全二叉树结构:

  1. 有右子节点时,必有左子节点
  2. 每个父节点都大于等于子节点(或者每个父节点都小于等于子节点)

堆的类型有两种:

堆中的方法:

堆结构优点(以下解释以大根堆为例):

下面用代码示例来演示堆结构

package com.leftGod;

/**
 * 基础堆结构
 *
 * 基础:
 * 求父节点下标:(i-1)/2
 * 求左孩子节点下标:i*2+1
 * 求右孩子节点下标:i*2+2
 *
 * @author zhao.hualuo
 * Create at 2022/4/2
 */
public class BaseHeap {

    /** 堆当前使用量 */
    private static int heapSize = 0;

    /** 堆数组,用来存放当前元素 */
    private static int[] heapArr = new int[4];

    /**
     * 添加新元素
     *
     * @param newValue 新元素
     */
    public static void add(int newValue) {
        //将这个新元素放到堆的最后一个位置
        heapArr[heapSize] = newValue;

        //将现在的数组重新转化成堆
        heapInsert(heapSize);

        //堆长度+1
        heapSize++;
        //判断堆是否需要扩容
        expansion();
    }

    /**
     * 从数组中根据下标移除元素
     *
     * @param index 要移除的元素下标
     * @return 移除的元素值
     */
    public static int remove(int index) {
        int returnValue = heapArr[index];

        //将最后一个位置的元素交换到要移除的这个元素位置上,堆长度减1
        swap(index, --heapSize);

        //1、看看这个元素是不是比父元素要大。若是,需要上移
        heapInsert(index);

        //2、看看这个元素是不是比子元素要小。若是,需要下沉
        heapify(index);

        return returnValue;
    }

    /**
     * heapInsert
     * 元素向上移动,最终形成堆
     *
     * @param index 下标
     */
    private static void heapInsert(int index) {
        while (index>=0 && heapArr[index] > heapArr[(index-1)/2]) {
            swap(index, (index-1)/2);
            index = (index-1)/2;
        }
    }

    /**
     * heapify
     * 元素向下移动,重新形成堆
     *
     * @param index 要移除的元素下标
     * @return 移除的元素值
     */
    private static void heapify(int index) {
        int left = index*2+1;
        while (left < heapSize) {
            //看看两个子元素那个大,下一步要和大的进行比较替换
            int maxSonIndex = left+1<heapSize && heapArr[left+1] >heapArr[left] ? left+1 : left;
            //当前元素和较大的那个儿子作比较,若父亲比两个儿子都大,匹配成功,循环结束
            if (heapArr[index] >= heapArr[maxSonIndex]) {
                break;
            }
            //走到这一步,说明儿子比老子大,老子和儿子要交换位置
            swap(index, maxSonIndex);
            //准备下一次循环
            index = maxSonIndex;
            left = left*2+1;
        }
    }

    /**
     * 堆自动扩容机制
     */
    private static void expansion() {
        //判断是否需要扩容
        if (heapSize < heapArr.length) {
            return;
        }
        //创建新数组,并复制
        int[] newHeapArr = new int[heapArr.length*2];
        System.arraycopy(heapArr, 0, newHeapArr, 0, heapArr.length);
        heapArr = newHeapArr;
        System.out.println("触发扩容,新堆长度:" + heapArr.length);
    }

    /**
     * 将两个元素交换位置
     *
     * @param originIndex 原位置
     * @param targetIndex 目标位置
     */
    private static void swap(int originIndex, int targetIndex) {
        if (originIndex == targetIndex) {
            return;
        }
        heapArr[originIndex] = heapArr[originIndex] ^ heapArr[targetIndex];
        heapArr[targetIndex] = heapArr[originIndex] ^ heapArr[targetIndex];
        heapArr[originIndex] = heapArr[originIndex] ^ heapArr[targetIndex];
    }

    public static void main(String[] args) {
        //测试heapInsert
        add(15);
        add(40);
        add(9);
        add(8);
        add(32);
        add(39);
        add(35);
        System.out.println("\n当前堆大小:" + heapArr.length + ",已占用堆空间:" + heapSize);
        System.out.print("heapInsert结果:");
        for (int i = 0; i < heapSize; i++) {
            System.out.print(heapArr[i] + ", ");
        }
        System.out.println();
        //[40, 32, 39, 8, 15, 9, 35, 0]

        //测试heapify
        //应该将8删除,用最后一个元素35来替换,35会和父亲32做一次heapInsert
        remove(3);
        System.out.println("\n当前堆大小:" + heapArr.length + ",已占用堆空间:" + heapSize);
        System.out.print("heapify结果:");
        for (int i = 0; i < heapSize; i++) {
            System.out.print(heapArr[i] + ", ");
        }
        //[40, 35, 39, 32, 15, 9, 8, 0]
        System.out.println();
    }
}

上面示例的运行结果如下:

"C:\Program Files\Java\jdk1.8.0_212\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.2\lib\idea_rt.jar=9226:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_212\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\rt.jar;D:\CodeWorkspace\PersonCode\demo\target\classes;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter\2.4.3\spring-boot-starter-2.4.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\2.4.3\spring-boot-2.4.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\5.3.4\spring-context-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\5.3.4\spring-aop-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\5.3.4\spring-beans-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\5.3.4\spring-expression-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.4.3\spring-boot-autoconfigure-2.4.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.4.3\spring-boot-starter-logging-2.4.3.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;C:\Users\Administrator\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\5.3.4\spring-core-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-jcl\5.3.4\spring-jcl-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-sdk-http\4.1.0.5\gexin-rp-sdk-http-4.1.0.5.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-sdk-template\4.0.0.24\gexin-rp-sdk-template-4.0.0.24.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-sdk-base\4.0.0.30\gexin-rp-sdk-base-4.0.0.30.jar;C:\Users\Administrator\.m2\repository\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-fastjson\1.0.0.3\gexin-rp-fastjson-1.0.0.3.jar;C:\Users\Administrator\.m2\repository\com\getui\push\restful-sdk\1.0.0.1\restful-sdk-1.0.0.1.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;C:\Users\Administrator\.m2\repository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;C:\Users\Administrator\.m2\repository\com\google\code\gson\gson\2.8.6\gson-2.8.6.jar" com.leftGod.BaseHeap
触发扩容,新堆长度:8

当前堆大小:8,已占用堆空间:7
heapInsert结果:40, 32, 39, 8, 15, 9, 35, 

当前堆大小:8,已占用堆空间:6
heapify结果:40, 35, 39, 32, 15, 9, 
Process finished with exit code 0

上面介绍的是堆的概念,并且自己动手实现了一个堆的示例。

但是大多数情况下并不需要自己实现堆结构,因为Jdk1.5之后已经帮我们实现了堆,就是PriorityQueue

平时我们可以直接使用Java中实现好的堆,下面使用代码来演示一下:

package com.leftGod;

import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * 测试Java中的堆
 *
 * PriorityQueue支持通过poll移除第一个元素,也支持根据元素值移除元素,
 * 但是不支持根据下标移除(PriorityQueue有removeAt方法,但是是private修饰的)
 * 或者如果我们需要根据下标更新元素值时,PriorityQueue就不支持,就需要我们使用我们自定义的堆结构
 *
 * @author zhao.hualuo
 * Create at 2022/4/3
 */
public class JavaHeapTest {

    public static void main(String[] args) {
        //PriorityQueue默认是小根堆
        PriorityQueue<Integer> minPriorityQueue = new PriorityQueue<Integer>();
        minPriorityQueue.add(15);
        minPriorityQueue.add(40);
        minPriorityQueue.add(9);
        minPriorityQueue.add(8);
        minPriorityQueue.add(32);
        minPriorityQueue.add(39);
        minPriorityQueue.add(35);
        System.out.println("小根堆:" + minPriorityQueue);

        //若想生成大根堆,需要手动实现比较器
        MyCustomComparator comparator = new MyCustomComparator();
        PriorityQueue<Integer> maxPriorityQueue = new PriorityQueue<Integer>(comparator);
        maxPriorityQueue.add(15);
        maxPriorityQueue.add(40);
        maxPriorityQueue.add(9);
        maxPriorityQueue.add(8);
        maxPriorityQueue.add(32);
        maxPriorityQueue.add(39);
        maxPriorityQueue.add(35);
        System.out.println("大根堆:" + maxPriorityQueue);

        //移除元素
        maxPriorityQueue.remove(8);
        System.out.println("移除元素8后的大根堆:" + maxPriorityQueue);

        //poll默认移除第一个元素
        Integer poll = maxPriorityQueue.poll();
        System.out.println("poll移除的元素是:" + poll);
        System.out.println("移除元素后的大根堆:" + maxPriorityQueue);
    }

    //我的自定义类加载器
    public static class MyCustomComparator implements Comparator {

        /**
         * 比较规则
         *
         * @param o1 第一个参数
         * @param o2 第二个参数
         * @return 若返回负数,o1在前面; 若返回正数,o2在前面
         */
        @Override
        public int compare(Object o1, Object o2) {
            //若o2-o1<0  说明02小,o1应该在前面
            return (int)o2 - (int)o1;
        }
    }
}

上面代码的运行结果如下:

"C:\Program Files\Java\jdk1.8.0_212\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.2\lib\idea_rt.jar=11054:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_212\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_212\jre\lib\rt.jar;D:\CodeWorkspace\PersonCode\demo\target\classes;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter\2.4.3\spring-boot-starter-2.4.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot\2.4.3\spring-boot-2.4.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-context\5.3.4\spring-context-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-aop\5.3.4\spring-aop-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-beans\5.3.4\spring-beans-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-expression\5.3.4\spring-expression-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.4.3\spring-boot-autoconfigure-2.4.3.jar;C:\Users\Administrator\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.4.3\spring-boot-starter-logging-2.4.3.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;C:\Users\Administrator\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-core\5.3.4\spring-core-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\springframework\spring-jcl\5.3.4\spring-jcl-5.3.4.jar;C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-sdk-http\4.1.0.5\gexin-rp-sdk-http-4.1.0.5.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-sdk-template\4.0.0.24\gexin-rp-sdk-template-4.0.0.24.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-sdk-base\4.0.0.30\gexin-rp-sdk-base-4.0.0.30.jar;C:\Users\Administrator\.m2\repository\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\Users\Administrator\.m2\repository\com\gexin\platform\gexin-rp-fastjson\1.0.0.3\gexin-rp-fastjson-1.0.0.3.jar;C:\Users\Administrator\.m2\repository\com\getui\push\restful-sdk\1.0.0.1\restful-sdk-1.0.0.1.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;C:\Users\Administrator\.m2\repository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;C:\Users\Administrator\.m2\repository\com\google\code\gson\gson\2.8.6\gson-2.8.6.jar" com.leftGod.JavaHeapTest
小根堆:[8, 9, 15, 40, 32, 39, 35]
大根堆:[40, 32, 39, 8, 15, 9, 35]
移除元素8后的大根堆:[40, 35, 39, 32, 15, 9]
poll移除的元素是:40
移除元素后的大根堆:[39, 35, 9, 32, 15]

Process finished with exit code 0
举报

相关推荐

0 条评论