【手撕数据结构】二叉树和堆

news2024/11/13 9:30:37

目录

  • 树的概念
  • 树的相关概念
  • 二叉树
    • 二叉树的概念
    • 满二叉树和完全二叉树
  • 堆的概念与结构
    • 堆的向上调整算法
      • 思路分析
      • 代码详细解说
    • 堆的向下调整算法
      • 算法图解分析
      • 代码详解分析
    • 堆的各个接口
      • 堆的定义及声明
      • 堆的初始化
      • 堆的销毁
      • 堆的插入
      • 堆的删除
      • 取堆顶数据
      • 堆的数据个数
      • 堆的判空

树的概念

  • 树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的
    在这里插入图片描述
  • 有一个特殊的结点,称为根结点,根节点没有前驱结点
  • 除根节点外,每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
  • 树是递归定义的
  • ⼀棵N个结点的树有N-1条边

树的相关概念

在这里插入图片描述

  • 叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点
  • 节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
  • 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节
  • 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 树的高度或深度:树中节点的最大层次; 如上图:树的高度为4

二叉树

二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:

  • 或者为空
  • 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
    在这里插入图片描述
  • 二叉树不存在度大于2的结点(节点的度是含含有子树节点个数的数)
  • 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
    在这里插入图片描述

满二叉树和完全二叉树

  • 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是
    说,如果一个二叉树的层数为K,且结点总数是 2^k-1,则它就是满二叉树。
    在这里插入图片描述
  • 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K
    的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点(从左到右)一一对
    应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
    在这里插入图片描述
    ##求满二叉树和完全二叉树的高度

在这里插入图片描述

在这里插入图片描述

堆的概念与结构

如果有一个关键码的集合K = { k0, k1, k2,…,kn-1 },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中。并满足:Ki <= K2i+1 且 Ki <= K2i+2 ( Ki >= K2i+1 且 Ki >= K2i+2 ) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆

堆的性质

  • 堆的子节点总是大于或小于根节点
  • 堆总是一颗完全二叉树

基本了解堆的概念后,我们来看看琢磨一下什么是大根堆和小根堆

在这里插入图片描述

在这里插入图片描述

  • 父节点(parent)和子节点(child)的下标关系(堆底层是一个数组)
    【lchild = parent * 2 + 1】 左孩子
    【rchild = parent * 2 + 2】 右孩子
    parent = (child - 1) / 2】

堆的向上调整算法

思路分析

  • 对于向上调整,从字面意思上看就是从下往上左一个调整,那具体是怎么一个调整法呢,我们看下面
    在这里插入图片描述
  • 可以看到向上调整算法实际上就是在堆底插入数据的时候,我们需要保证插入这个数据后这个堆还是一个大根堆或者小根堆,大根就是保证父节点一定比子节点大,小根就是保证父节点一定比子节点小,所以我们需要把插入的数据与他的父节点进行比较,例如小跟堆,如果比父节点大不需要交换,如果比父节点小就需要交换

代码详细解说

  • 首先我们应该知道出入什么参数,我们需要改变堆,堆实际是一个用结构体定义的顺序表,所以第一个参数是需要传结构体指针,第二我们需要找父节点与孩子节点进行比较,要求父节点就是(child-1)/2,所以我们需要传孩子节点。
void Adjust_UP(Hp* hp, int child)
  • 有了这个新入的孩子后,我们就要去查找到它的父亲,因为是要和它的父亲去做一个对比,前面我们有说到过怎么通过一个孩子去找它的父亲,忘了的再翻上去找找哦
int parent = (child - 1) / 2;
  • 找到父节点后就需要和孩子节点进行比较(这里举例大根堆),如果孩子比父节点大,就交换
if (hp->a[child] > hp->a[parent])
	swap(&hp->a[child], &hp->a[parent]);		//交换孩子和父亲,逐渐变为大根堆

  • 但是呢我们交换一次就可以了吗❓那当然不是,这是一个不断进行调整的过程,所以我们每次在交换完后需要再次去更新父亲和孩子的值,然后将这段逻辑放到一个循环里。若是调整到符合堆的性质了,就break跳出这个循环

那么什么时候不用交换了呢?要么循环过程中,父节点不再比孩子节点小,要么孩子节点已经到达下标为0(也就是祖宗节点)也可以说是堆顶

//while (parent >= 0)	这样写不好,程序会非正常结束
while (child  > 0)
{
	if (hp->a[child] > hp->a[parent])
	{
		swap(&hp->a[child], &hp->a[parent]);		//交换孩子和父亲,逐渐变为大根堆
		//迭代 —— 交替更新孩子和父亲
		child = parent;		
		parent = (child - 1) / 2;
	}
	else {
		break;		//若是比较无需交换,则退出循环
	}
}

以下整体代码:

/*交换函数*/
void swap(HpDataType* x1, HpDataType* x2)
{
	HpDataType t = *x1;
	*x1 = *x2;
	*x2 = t;
}

/*向上调整算法*/
void Adjust_UP(Hp* hp, int child)
{
	int parent = (child - 1) / 2;

	//while (parent >= 0)	这样写不好,程序会非正常结束
	while (child  > 0)
	{
		if (hp->a[child] > hp->a[parent])
		{
			swap(&hp->a[child], &hp->a[parent]);		//交换孩子和父亲,逐渐变为大根堆
			//迭代 —— 交替更新孩子和父亲
			child = parent;		
			parent = (child - 1) / 2;
		}
		else {
			break;		//若是比较无需交换,则退出循环
		}
	}
}

堆的向下调整算法

  • 对于向下调整算法这一块,在后面堆的数据结构中的删除堆顶数据和堆排序都需要用到它,因此重点掌握

算法图解分析

  • 对于向下调整算法,前提很重要就是保证他的左子树和右子树是大堆或者小堆,才能从堆顶进行向下调整。
    在这里插入图片描述

  • 此时就需要使用到这个【向下调整算法】,当然我这个是大堆的调整,小堆的话刚好相反。原理:找出当前结点的两个孩子结点中教大的那一个换上来,将这个【18】换下去,但是呢此时还不构成大堆,因此我们还需要再去进行一个调整,一样是上面的找法,然后直到这个【18】的孩子结点到达【n - 1】就不作交换了,因为【n - 1】就相当于是位于数组下标的最后一个值

代码详解分析

  • 首先也是对所需参数进行分析,我们需要结构体定义的一个堆进行调整,所以需要指向这个堆的结构体指针,还需要找孩子节点进行根父节点进行比较,而公式2 * parent + 1(左孩子),2 * parent +2(右孩子),所以传parent,然后我们需要孩子节点到达 n - 1的时候结束调整(n是有效数据个数)
void Adjust_Down(int* a, int n, int parent)

  • 因为我们是需要和孩子结点中大的那个做交换,因为我这里是直接假设左孩子比较大
    那么如果右孩子大呢?这就需要比较左孩子和右孩子谁大了,但注意右孩子下标不能超过 n 否则会越界
		//判断是否存在右孩子,防止越界访问
if (child + 1 < n && a[child + 1] > a[child])
{
	++child;		//若右孩子来的大,则转化为右孩子
}

  • 然后是循环内部的逻辑,和【向上调整算法】一样,就是一个比较和迭代更新的过程
if (a[child] > a[parent])
{
	swap(&a[child], &a[parent]);
	parent = child;
	child = parent * 2 + 1;
}
else {
	break;
}

下面是全部代码

/*向下调整算法*/
void Adjust_Down(int* a, int n, int parent)
{
	int child = parent * 2 + 1;		//默认左孩子来得大
	while (child < n)
	{		//判断是否存在右孩子,防止越界访问
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;		//若右孩子来的大,则转化为右孩子
		}
		if (a[child] > a[parent])
		{
			swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			break;
		}
	

堆的各个接口

堆的定义及声明

  • 首先看到结构体的定义及声明,是不是回想起了我们之前所学的顺序表,因为顺序表的底层其实也是一种数组
typedef int HpDataType;
typedef struct Heap {
	HpDataType* a;
	int size;
	int capacity;
}Hp;

堆的初始化

/*初始化堆*/
void HeapInit(Hp* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

  • 和顺序表一样

堆的销毁

/*销毁堆*/
void HeapDestroy(Hp* hp)
{
	assert(hp);
	if(hp->a)
	{
		free(hp->a);
		hp->a = NULL;
		hp->size = hp->capacity = 0;
	}
}

  • 这里要注意只释放首地址,因为底层是数组,数组是一块连续的空间所以只需要释放首地址就可以全部释放了。

堆的插入

  • 堆底插入也就是数组末尾,然后为了保证他是大根堆或者小根堆,需要进行向上调整。
/*堆的插入*/
void HeapPush(Hp* hp, HpDataType x)
{
	assert(hp);
	//扩容逻辑
	if (hp->size == hp->capacity)
	{
		int newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HpDataType* tmp = (HpDataType*)realloc(hp->a, newCapacity * sizeof(HpDataType));
		if (tmp == NULL)
		{
			perror("fail realloc");
			exit(-1);
		}
		hp->a = tmp;
		hp->capacity = newCapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;

	Adjust_UP(hp, hp->size - 1);
}

  • 注意向上调整传参的时候孩子节点减1,因为插入的时候自增到了下一个空白节点,但是我们要调整的是插入的节点。所以减1

堆的删除

  • 首先可以来看下代码,可以看到很显目的一句,就是交换【a[0]】和【a[hp->size - 1]】,这其实值的就是堆顶的结点和堆顶的末梢结点,为什么先要交换它们呢,我们来分析一下
  • 注意删除堆的数据是删除堆顶
  • List item

数据

/*堆的删除*/
void HeapPop(Hp* hp)
{
	assert(hp);
	assert(hp->size > 0);
	//首先交换堆顶和树的最后一个结点 —— 易于删除数据,保护堆的结构不被破坏
	swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;		//去除最后一个数据

	Adjust_Down(hp->a, hp->size, 0);
}

改写族谱,关系紊乱😵

  • 是我们什么都不做,直接去删除一下这个堆顶的数据,后面的结点就需要前移,此时的原本的孩子结点就会变成父亲,父亲呢可能又会变成孩子。原本49和34是好兄弟,但是删除下标0堆顶数据后,下标1 【49】就成了34的爸爸这就不合理了把
    在这里插入图片描述
  • 此时就像代码中写的一样,我们可以先去交换一下堆顶和堆底末梢的数据,然后将交换下来的数删除,这样既可以删除这个堆顶的数据,也不会影响整棵树的结构,就是堆顶需要向下调整,这时候向下调整也满足他的左右子树都是大堆
    在这里插入图片描述

取堆顶数据

/*取堆顶数据*/
HpDataType HeapTop(Hp* hp)
{
	assert(hp);
	assert(hp->size > 0);
	return hp->a[0];
}

  • 这块很简单,因为堆顶的数据就是数组的首元素,因此直接return【hp->a[0]】即可

堆的数据个数

  • 上面说到过,结构体中的【size】是指向当前堆底末梢数据的后一个位置,也就相当于【n】,因此求数据个数直接return【hp->size】即可
/*返回堆的大小*/
size_t HeapSize(Hp* hp)
{
	assert(hp);
	return hp->size;
}

堆的判空

  • 堆的判空就是当数据个数为0的时候
/*判断堆是否为空*/
bool HeapEmpty(Hp* hp)
{
	assert(hp);
	return hp->size == 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CVE-2024-38077】核弹级Windows RCE漏洞如何自检并修复该漏洞(附批量漏洞检测工具及分析伪代码)

代码详细分析点击此处 # 伪代码分析链接 工具为官方工具&#xff0c;师傅可自行测试 深信服CVE-2024-38077漏洞扫描工具.exe Algorithm : SHA1Hash : 85ECBDB053950A20B9748E867586D059AAA19115Algorithm : SHA256Hash : 1BF3A372F95C4F5B2D776C6ABB1E9BCA51933C3…

机器学习·L3W2-协同过滤

推荐算法 推荐算法可以预测用户评分&#xff0c;并根据评分推荐数据 推荐算法与其他预测算法的区别在于&#xff1a;推荐算法中的数据大多都不完整&#xff0c;用户只对几个电影评分&#xff1b;而预测算法则要求数据完整&#xff0c;便于拟合和预测 协同过滤 评分矩阵Y&#x…

Install pytorch 使用 torch 的例子

如果不知道怎么开始和安装软件 从这里开始 如果需要GPU版本&#xff0c;请选择CUDA&#xff0c;而不是CPU PyTorchhttps://pytorch.org/ Python 3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 06:04:10) [GCC 10.3.0] on linux Type "help", &quo…

k8s环境使用cronjob对mysql8进行备份

一、configmap 数据库备份脚本写入k8s环境的configmap文件&#xff0c;并生成config。 # cat mysql-back-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: mysql8backnamespace: crontabs data:mysql8back.sh: |#!/bin/bashDUMPDIR/tmp#backupbackupDatedate…

【专题】2023-2024跨境旅游消费趋势研究报告合集PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37306 近日&#xff0c;“世界旅游联盟中欧旅游对话”在匈牙利布达佩斯举办&#xff0c;发布《2023 - 2024 跨境旅游消费趋势研究报告》。 报告显示&#xff0c;2023 - 2024 年全球旅游业复苏&#xff0c;跨境旅游人数和支出显著增加…

【Material-UI】Checkbox组件:Indeterminate状态详解

文章目录 一、什么是Indeterminate状态&#xff1f;二、Indeterminate状态的实现1. 基本用法示例2. 代码解析3. Indeterminate状态的应用场景 三、Indeterminate状态的UI与可访问性1. 无障碍设计2. 用户体验优化 四、Indeterminate状态的最佳实践1. 状态同步2. 优化性能3. 提供…

SQL Zoo 10.Window functions

以下数据均来自SQL Zoo 1.Show the lastName, party and votes for the constituency S14000024 in 2017.&#xff08;显示2017年选区“S14000024”的姓氏、政党和选票&#xff09; SELECT lastName, party, votesFROM geWHERE constituency S14000024 AND yr 2017 ORDER BY…

多个IT系统数据同步方案-操作标识

这是一个很普遍的方案&#xff0c;实现多个IT系统数据同步&#xff1a; 从上游发出的数据标识&#xff0c;就可以完整的控制数据&#xff0c;当然对上游的要求也多一些&#xff0c;就是打数据标识前一定要读取一下现有的数据。

Spring Boot 基于 SCRAM 认证集成 Kafka 的详解

一、说明 在现代微服务架构中&#xff0c;Kafka 作为消息中间件被广泛使用&#xff0c;而安全性则是其中的一个关键因素。在本篇文章中&#xff0c;我们将探讨如何在 Spring Boot 应用中集成 Kafka 并使用 SCRAM 认证机制进行安全连接&#xff1b;并实现动态创建账号、ACL 权限…

Android Studio生成系统签名platform.jks

准备工作&#xff1a; 系统工程师需要提供以下文件并且在同一个目录文件夹下面 1、platform.pk8 2、platform.x509.pem 3、signapk.jar(通用的) 按照以下顺序执行 1、platform.pk8和platform.x509.pem 生成 .jks 提供platform.pk8、platform.x509.pem 、signapk.jar&…

低代码革命:重塑开发效率与质量的未来

1. 引言 随着信息技术的快速发展&#xff0c;企业对应用程序的需求日益增长。然而&#xff0c;传统的软件开发模式面临着开发周期长、成本高、人才短缺等问题。近年来&#xff0c;“低代码”开发平台如雨后春笋般涌现&#xff0c;承诺让非专业人士也能快速构建应用程序。这种新…

Netty原理及高性能

1. Netty原理 Netty是一个基于Java的异步事件驱动的网络应用框架&#xff0c;他用于快速开发高性能、高可靠性的网络服务器和客户端应用。Netty的原理涉及多个方面&#xff0c;包括 Reactor模式、核心组件、编解码、线程模型以及TCP粘包和拆包处理等。 1.1 Reactor模式 Reactor…

玻璃存储还没整明白,陶瓷纳米存储又来了!

关注我们 - 数字罗塞塔计划 - 在信息爆炸的当下&#xff0c;我们每天产生的数据比以往任何时候都多。其实很多数据都是存储后很少被访问&#xff0c;但仍需要长期保存的“冷数据”。磁带、硬磁盘、光盘等传统存储介质难以提供冷数据存储所需的超长寿命、超大容量和持续可访问性…

Docker Compse单机编排

一.Docker Compse 介绍 Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过 Compose&#xff0c;你可以使用 YAML 文件来配置应用程序的服务、网络和卷&#xff0c;然后使用单个命令创建和启动所有服务。这使得在开发、测试和部署过程中管理多容器应用程…

精彩分享|暴雨亮相第二十届智能计算国际会议(ICIC 2024)

8月6日至8日&#xff0c;第二十届智能计算国际会议&#xff08;ICIC 2024&#xff09;在天津盛大召开&#xff0c;这场由宁波东方理工大学&#xff08;暂名&#xff09;主办&#xff0c;天津科技大学承办&#xff0c;中国矿业大学&#xff08;北京&#xff09;、中国矿业大学和…

【学习笔记】Matlab和python双语言的学习(非线性规划法)

文章目录 前言一、非线性规划法二、例题&#xff1a;选址问题1.确定决策变量2.确定约束条件3.确定目标函数4.建立模型5.求解 三、代码实现----Matlab1.Matlab 的 fmincon 函数&#xff08;1&#xff09;基本用法&#xff08;2&#xff09;简单示例 2.Matlab 代码第一问&#xf…

RegNet 图像识别网络,手写阿拉伯数字的图像分类

1、RegNet 网络介绍 regnet 是一个深度学习模型架构&#xff0c;用于图像分类任务。它是由 Facebook AI Research&#xff08;FAIR&#xff09;提出的&#xff0c;旨在实现高效的网络设计。regnet 通过在不同的网络层级上增加网络宽度和深度来提高模型性能。 regnet 的设计思…

如何使用 AWS CLI 创建和运行 EMR 集群

为初学者提供清晰易懂的教程 为初学者提供清晰易懂的教程 Apache Spark 和 AWS EMR 上的 Spark 集群 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 欢迎来到雲闪世界。Spark 被认为是“大数据丛林之王”&#xff0c;在数据分析、机器学习、流媒体和图形…

DataWhale AI夏令营第四期-魔搭生图task1学习笔记

根据教程提供的链接&#xff0c;进入相应文章了解魔搭生图的主要工作是通过对大量图片的训练&#xff0c;生成自己的模型&#xff0c;然后使用不同的正向、反向提示词使模型输出对应的图片 1.官方跑baseline教程链接:Task 1 从零入门AI生图原理&实践 2.简单列举一下赛事的…

【K8S】K8S架构及相关组件

文章目录 1 K8S总体架构2 相关组件2.1 控制面板组件2.2 节点组件2.3 附加组件 写在最后 1 K8S总体架构 K8S&#xff0c;全称Kubernetes&#xff0c;是一个开源的容器部署和管理平台&#xff0c;由Google开发&#xff0c;后捐献给云原生计算基金会&#xff08;CNCF&#xff09;…