二叉树之堆树

堆树是一种完全二叉树,完全二叉树特点:除了最后一层所有层都填满,最后一层节点从左到右排列。堆树分为两种类型:大顶堆和小顶堆。

大顶堆:每个节点的值都大于或等于其子节点的值,根节点是最大值。

小顶堆:每个节点的值都小于或等于其子节点的值,根节点是最小值。
在这里插入图片描述

堆的存储

由于堆是一个完全二叉树,根据完全二叉树的特点,可以使用数组存储堆树。

在这里插入图片描述

如上小顶堆。数组下标x对应的左子节点下标为x*2,右子节点元素下标为x乘2+1,其父节点下标为x/2。

如元素15其下标为3,则左子元素31的下标为6,右子元素61下标为7。父节点下标为1。

堆的构建

往堆上添加一个元素需要继续满足堆的特点,这就需要进行元素调整交换,这个过程就叫做堆化。

堆化有两种方式:

向上堆化:新插入一个节点后,比较该节点与其父节点的值,如果不符合堆的性质,则交换,继续向上堆化,直到满足堆的性质。

向下堆化:从某个节点开始,比较该节点与其子节点的值,如果不符合堆的性质(例如在最大堆中,父节点小于子节点),则交换节点和最大的子节点,继续向下堆化直到满足堆的性质。

元素的插入

这里以大顶堆构建为例来代码演示

先定义堆基本结构

    int[] data; //数组存储堆
    int capacity; //容量
    int size;//当前堆中元素数

    public MaxHeapTree(int capacity){
        data = new int[capacity+1];
        this.capacity = capacity;
        this.size = 0;
    }

向上堆化插入元素

    public void insertBottom(int val){
        if(size >= capacity) return;
        data[++size] = val;
        int current = size; //当前节点下标
        int parent = current/2;//父节点下标
        //判断当前节点是否大于父节点
        while (parent > 0  && (data[current] > data[parent]) ){
            //当前节点和父节点交换
            int tmp = data[current];
            data[current] = data[parent];
            data[parent] = tmp;

            current = parent;
            parent = current/2;
        }
    }

向上堆化需要将新插入的元素放到堆最后。代码还是比较简单的。

插入数据测试

        MaxHeapTree heap = new MaxHeapTree(10);
        int[] arr = {8,6,5,9,4,27,22,55,3,88};
        for (int i = 0; i < arr.length; i++) {
            heap.insertBottom(arr[i]);
        }
        System.out.println(Arrays.toString(heap.data));

最后输出内容:[0, 88, 55, 22, 8, 27, 5, 9, 6, 3, 4] 满足大顶堆的特点。

删除堆顶元素

如果将堆顶元素取出,这里也就是取最大数。剩下的数则需要调整用来继续满足堆的特点。如果直接将堆顶元素的左右子节点提升到根节点,进行向下堆化,最后可能导致堆数不满足完全二叉树的特点。

所以这里堆顶元素删除后一般将最后一个元素拿到堆顶位置,然后进行向下堆化。这样堆化完成后的结构很顶还是完全二叉树,因为堆化过程中只进行节点的交换。

来看下具体代码

    public int removeTop(){
        if(size == 0) return -1;
        int val = data[1];
        data[1] = data[size];
        heapfiyUp(1);
        return val;
    }

    public void heapfiyUp(int index,int total){
        while (true){
            int max = index;
            int left = index*2; //左子
            int right = left +1; //右子
            //找到当前节点和其两个字节点中最大的
            if(left <= total && data[left] > data[max]) max = left;
            if(right <= total && data[right] > data[max]) max = right;
            //如果最大是当前元素,不用交换继续比较了
            if(max == index) break;
            //否则交换当前元素和最大元素位置
            swap(index,max);
            //将当前元素切换到最大元素,继续比较
            index = max;
        }
    }

这样从堆顶元素不断取最大元素,就可以获取整个堆中topK的值。heapfiyUp方法就是从上往下进行堆化,入参就是当前要进行堆化的节点位置。

堆排序

对于堆排序只要理解了堆化的两个方法排序就变得简单了。

首先要将排序的数组进行堆化,堆化这里按上面有两种方法,向上堆化,这个是从前往后每个元素一次插入到堆中。而对于向下堆化,由于每次都是和自己子节点进行比较,由于所有的叶子节点都没有子节点,所以叶子节点不需要进行向下堆化的过程,从第一个非叶子节点进行堆化即可。由于堆数是一个完全二叉树,最后一个叶子节点是 节点数/2。所以只需要进行 节点数/2次就可以构建完成堆。

堆构建完成后,元素不是完全有序的,例如上面的例子构建完成后是[0, 88, 55, 22, 8, 27, 5, 9, 6, 3, 4]。这里只能保证一点堆顶是最大的。

排序只需要每次将堆顶元素拿出和数组最后一个元素进行交换,然后剩下的n-1个元素在进行一次向下堆化又找到了剩下的n-1个中最大的,重复上面的过程最后就完成了数组的排序。

代码如下:

    public void sort(){
        int n = size;//堆容量
        while(n > 1){
            swap(1,n);//将堆顶元素和最后一个元素交换
            heapfiyUp(1,--n); //从堆顶进行向下堆化
        }
    }

这里省略建堆的过程。

堆通常用于实现优先队列,快速找到极值。和topK问题,topK一般建立小顶对,然后每次和堆顶元素比较即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/883208.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

降准降息一揽子措施点燃 A 股激情,4% 大涨之后趋势深度剖析

文章目录 牛回速归原因分析引爆点情绪和信心一根大阳线&#xff0c;千军万马来相见阴霾是否一扫而空还未可知 流动性和增量 潜在隐患等待经济复苏配套政策期待中美关系进展 短期内趋势分析空军短期内仍有余力如何看待第2日的回撤外围 趋势分析结论短期内可能仍有波折中长期会是…

Flink Task 日志文件隔离

Flink Task 日志文件隔离 任务在启动时会先通过 MdcUtils 启动一个 slf4j 的 MDC 环境&#xff0c;然后将 jobId 添加到 slf4j 的 MDC 容器中&#xff0c;随后任务输出的日志都将附带 joid。 MDC 介绍如下&#xff1a; MDC ( Mapped Diagnostic Contexts )&#xff0c;它是一个…

C/C++逆向:循环语句逆向分析

在逆向分析中&#xff0c;循环语句通常会以特定的汇编模式或结构体现出来。常见的循环语句包括 for 循环、while 循环和 do-while 循环。由于不同的编译器会根据代码优化的级别生成不同的汇编代码&#xff0c;分析循环的模式也可能会有所不同。以下是三种常见循环语句的汇编分析…

【C++ Primer Plus习题】17.7

问题: 解答: #include <iostream> #include <vector> #include <string> #include <fstream> #include <algorithm>using namespace std;const int LIMIT 50;void ShowStr(const string& str); void GetStrs(ifstream& fin, vector<…

ShardingSphere 分库分表

中间件 常用中间件 MyCat 是基于 Proxy&#xff0c;它复写了 MySQL 协议&#xff0c;将 Mycat Server 伪装成⼀个 MySQL 数据库客户端所有的jdbc请求都必须要先交给MyCat&#xff0c;再有 MyCat转发到具体的真实服务器缺点是效率偏低&#xff0c;中间包装了⼀层代码⽆侵⼊性…

【刷题3】找到字符串中所有字母异位词、串联所有单词的子串

目录 一、找到字符串中所有字母异位词二、串联所有单词的子串 一、找到字符串中所有字母异位词 题目&#xff1a; 思路&#xff1a; 用一个变量count来统计有效字符的个数。哈希表2统计字符串p的每个字符出现的个数&#xff0c;然后遍历字符串s&#xff0c;先进窗口&#xf…

Unity-物理系统-碰撞检测-物理材质

物理材质的作用&#xff1a;改变碰撞效果 因为碰撞的过程是相互的&#xff0c;所以在碰撞双方都要加相同的物理材质才能实现效果 物理材质创建 参数

微软宣布弃用WSUS,企业用户尽早准备替换方案

微软最近宣布将逐步弃用Windows Server Update Services (WSUS)&#xff0c;不再为其开发新功能&#xff0c;但会继续支持现有的更新和功能。这一决定对企业客户来说影响深远&#xff0c;尤其是那些依赖WSUS来管理大规模Windows设备更新的组织。 对企业客户的影响 安全性与合规…

模型Alignment之RLHF与DPO

1. RLHF (Reinforcement Learning from Human Feedback) RLHF 是一种通过人类反馈来强化学习的训练方法&#xff0c;它能够让语言模型更好地理解和执行人类指令。 RLHF 的三个阶段 RLHF 的训练过程一般分为三个阶段&#xff1a; 监督微调&#xff08;Supervised Fine-Tuning,…

Apache ZooKeeper 及 Curator 使用总结

1. 下载 官网地址&#xff1a;Apache ZooKeeper 点击下载按钮 选择对应的版本进行下载 2. 使用 1、解压 tar -zxf apache-zookeeper-3.9.2-bin.tar.gz2、复制配置文件&#xff0c;有一个示例配置文件 conf/zoo_sample.cfg&#xff0c;此文件不能生效&#xff0c;需要名称为…

Docker Registry API best practice 【Docker Registry API 最佳实践】

文章目录 1. 安装 docker2. 配置 docker4. 配置域名解析5. 部署 registry6. Registry API 管理7. 批量清理镜像8. 其他 &#x1f44b; 这篇文章内容&#xff1a;实现shell 脚本批量清理docker registry的镜像。 &#x1f514;&#xff1a;你可以在这里阅读&#xff1a;https:/…

安卓13设置动态显示隐藏第一页的某一项 动态显示隐藏无障碍 android13设置动态显示隐藏第一页的某一项

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改4.1修改方法14.2修改方法25.编译6.彩蛋1.前言 有时候,我们的设置里面显示的信息,需要根据不同的情况显示不同的信息,例如,动态的显示或者隐藏 “无障碍” 这一项。 2.问题分析 像这个问题…

基于 K8S kubernetes 搭建 安装 EFK日志收集平台

目录 1、在k8s中安装EFK组件 1.1 安装elasticsearch组件 1.2 安装kibana组件 1.3 安装fluentd组件 文档中的YAML文件配置直接复制粘贴可能存在格式错误&#xff0c;故实验中所需要的YAML文件以及本地包均打包至网盘 链接&#xff1a;https://pan.baidu.com/s/15Ryaoa0_…

Leetcode面试经典150题-39.组合总数进阶:40.组合总和II

本题是扩展题&#xff0c;真实考过&#xff0c;看这个题之前先看一下39题 Leetcode面试经典150题-39.组合总数-CSDN博客 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数…

Java查找算法——(四)分块查找(完整详解,附有代码+案例)

文章目录 分块查找1.1普通分块查找 分块查找 1.1普通分块查找 分块原则&#xff1a; 块内无序&#xff0c;块间有序:前一块中的最大数据&#xff0c;小于后一块中所有的数据&#xff0c;块与块之间不能有数据重复的交集。块的数量一般等于数字个数开根号 核心思路&#xff…

CentOS Linux教程(6)--CentOS目录

文章目录 1. 根目录2. cd目录切换命令3. CentOS目录介绍4. pwd命令介绍5. ls命令介绍5.1 ls5.2 ls -a5.3 ls -l 1. 根目录 Windows电脑的根目录是计算机(我的电脑)&#xff0c;然后C盘、D盘。 Linux系统的根目录是/&#xff0c;我们可以使用cd /进入根目录&#xff0c;然后使…

VUE条件树查询

看如下图所示的功能&#xff0c;是不是可高级了&#xff1f;什么&#xff0c;你没看懂&#xff1f;拜托双击放大看&#xff01; 是的&#xff0c;我最近消失了一段时间就是在研究这个玩意的实现&#xff0c;通过不懈努力与钻研并参考其他人员实现并加以改造&#xff0c;很好&am…

南开大学联合同济大学发布最新SOTA Occ OPUS:使用稀疏集进行占据预测,最快实现8帧22FPS

Abstract 占据预测任务旨在预测体素化的 3D 环境中的占据状态&#xff0c;在自动驾驶社区中迅速获得了关注。主流的占据预测工作首先将 3D 环境离散化为体素网格&#xff0c;然后在这些密集网格上执行分类。然而&#xff0c;对样本数据的检查显示&#xff0c;大多数体素是未占…

Windows内核编程基础(3)

内存分配 在应用层编程时&#xff0c;系统提供了GlobalAlloc/HeapAlloc/LocalAlloc等函数。C/C库提供了malloc函数&#xff0c;以及new操作符在堆上分配内存。 在我前面一个关于Windows页交换文件的博客中&#xff0c;介绍了虚拟内存&#xff0c; 虚拟内存是计算机系统内存管…

Unity开发绘画板——03.简单的实现绘制功能

从本篇文章开始&#xff0c;将带着大家一起写代码&#xff0c;我不会直接贴出成品代码&#xff0c;而是会把写代码的历程以及遇到的问题、如何解决这些问题都记录在文章里面&#xff0c;当然&#xff0c;同一个问题的解决方案可能会有很多&#xff0c;甚至有更好更高效的方式是…