数据结构第六课 -----链式二叉树的实现

news2024/12/27 14:15:16

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
​🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


链式二叉树

  • **作者前言**
  • 二叉树
  • 二叉树的遍历
    • 前序遍历
    • 中序遍历
    • 后序遍历
  • 小例子
  • 知识点

二叉树

前面粗略的介绍了二叉树
二叉树主要有两种 空树和非空树
而非空树拆分为 : 根节点 和左子树和右子树
在这里插入图片描述
二叉树的性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^(i-1)个结点.

  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h -1 .

  3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为n2 ,则有 n0= n2+1

  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log(n+1) . (ps: 是log以2
    为底,n+1为对数)

  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:

    1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
    2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
    3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

二叉树的遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。(根 ->左 ->右)

  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。(左->根->右)

  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。(左->右->根)

为此特意构建一个二叉树

#include<stdio.h>
#include<stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType* val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* rigth;

}BinaryTreeNode;
BinaryTreeNode* CreateNode(BTDataType elemest)
{
	BinaryTreeNode* p = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));
	if (p == NULL)
	{
		perror("malloc");
		return -1;
	}
	p->val = elemest;
	return p;
}
int main()
{
	BinaryTreeNode* n1 = CreateNode(1);
	BinaryTreeNode* n2 = CreateNode(2);
	BinaryTreeNode* n3 = CreateNode(3);
	BinaryTreeNode* n4 = CreateNode(4);
	BinaryTreeNode* n5 = CreateNode(5);
	BinaryTreeNode* n6 = CreateNode(6);
	n1->left = n2;
	n1->rigth = n4;
	n2->left = n3;
	n2->rigth = NULL;
	n3->left = NULL;
	n3->rigth = NULL;
	n4->left = n5;
	n4->rigth = n6;
	n5->left = NULL;
	n5->rigth = NULL;
	n6->left = NULL;
	n6->rigth = NULL;
	return 0;
}

前序遍历

我们以上面图片为例
我们可以写成:
1 2 3 N N N 4 5 N N 6 N N
代码:

void PreOrder(BinaryTreeNode* n1)
{
	if (n1 == NULL)
	{
		printf("NULL ");
		return;
	}
		

	printf("%d ", n1->val);
	PreOrder(n1->left);
	PreOrder(n1->rigth);
}

在这里插入图片描述

中序遍历

我们可以写成:N 3 N 2 N 1 N 5 N 4 N 6 N
代码:

void InOrdef(BinaryTreeNode* n1)
{
	if (n1 == NULL)
	{
		printf("NULL ");
		return;
	}
	PreOrder(n1->left);
	printf("%d ", n1->val);
	PreOrder(n1->rigth);
}

后序遍历

我们可以写成: N N 3 N 2 N N 5 N N 6 4 1
代码:

void PostOrder(BinaryTreeNode* n1)
{
	if (n1 == NULL)
	{
		printf("NULL ");
		return;
	}
	PreOrder(n1->left);
	PreOrder(n1->rigth);
	printf("%d ", n1->val);
}

小例子

叶子节点个数
思路:左子树的节点个数加上右子树的节点个数加上根节点

//节点个数
int TreeSize(BinaryTreeNode* n1)
{
	if (n1 == NULL)
		return 0;
	return 1 + TreeSize(n1->left) + TreeSize(n1->rigth);
}

叶节点个数
思路:左子树的叶节点个数加上右子树的叶节点个数加上根节点 需要注意的是为空树.和只有根节点的情况

//叶节点的个数
int TreeLeafSize(BinaryTreeNode* n1)
{
	//为空树
	if (n1 == NULL)
		return 0;
	//只有一个节点
	if (n1->left == NULL && n1->rigth == NULL)
		return 1;
	return TreeLeafSize(n1->left) + TreeLeafSize(n1->rigth);
}

树的高度
思路:左子树的高度和右子树高度比较,大的高度加上1就是整个二叉树的高度,需要注意的是空树情况下

int TreeHeigth(BinaryTreeNode* n1)
{
	if (n1 == NULL)
		return 0;
	if (n1->left == NULL && n1->rigth == NULL)
		return 1;
	int a = TreeHeigth(n1->left);
	int b = TreeHeigth(n1->rigth);
	return (a > b ? a : b) + 1;
}

#第k层的节点
思路: 左子树的第k-1层的节点个数 加上右子树的第k-1层的节点个数,如果k为0就是空,k=1,就是1

int NodeNum(BinaryTreeNode* n1, int k)
{
	if (n1 == NULL)
		return 0;
	if (k == 0)
		return 0;
	if (k == 1)
		return 1;
	return NodeNum(n1->left, k - 1) + NodeNum(n1->rigth, k - 1);
}

层次遍历
思路:层次遍历就是从第一层开始横向遍历
我们可以借助队列的性质,先进先出,我们先开始插入根节点,然后开始进行循环判断,只要出去的节点的左右孩子不为NULL就插入到队列,直到队列为空
在这里插入图片描述

// 层序遍历
void BinaryTreeLevelOrder(BinaryTreeNode* root)
{
	//创建一个队列
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//插入的是节点,
	if (root != NULL)
		QueuePush(&Qu, root);
	while (QueueSize(&Qu))
	{
		BinaryTreeNode* from = QueueFront(&Qu);
		
		
		printf("%d ", from->val);
		//删除
		QueuePop(&Qu);
		//需要注意的是删除只是释放掉存储了二叉树节点的地址的空间,并没有释放二叉树节点
		if (from->left != NULL)
		{
			QueuePush(&Qu, from->left);
		}
		if (from->rigth != NULL)
		{
			QueuePush(&Qu, from->rigth);
		}
		
	}
	printf("\n");
	QueueDestroy(&Qu);
	
	
	
}

这个是打印全部的
如果要一层层的打印
思路: 我们可以定义一个变量,用来统计当前队列的个数,也就是当层的节点个数,然后每出列一个就把对应的左右孩子插入进去,然后该变量减1,直到为0,也就是该层的节点全部出列了,然后再计算出队列的长度,也就是下一层的节点个数,然后继续,直到队列的长度为0

在这里插入图片描述

// 层序遍历
void BinaryTreeLevelOrder(BinaryTreeNode* root)
{
	//创建一个队列
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//插入的是节点,
	if (root != NULL)
		QueuePush(&Qu, root);
	int size = QueueSize(&Qu);
	while (QueueSize(&Qu))
	{
		while (size--)
		{
			BinaryTreeNode* from = QueueFront(&Qu);


			printf("%d ", from->val);
			//删除
			QueuePop(&Qu);
			//需要注意的是删除只是释放掉存储了二叉树节点的地址的空间,并没有释放二叉树节点
			if (from->left != NULL)
			{
				QueuePush(&Qu, from->left);
			}
			if (from->rigth != NULL)
			{
				QueuePush(&Qu, from->rigth);
			}
		}
		printf("\n");
		size = QueueSize(&Qu);
		
	}
	printf("\n");
	QueueDestroy(&Qu);
	
	
	
}

判断是否是完全二叉树
思路:我们和上面的层次遍历一样,先找一个队列进行一层层的入队和出队,如果遇见节点为NULL的就判断后面是否还有节点存在

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BinaryTreeNode* root)
{
	//创建一个队列
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//插入的是节点,
	if (root != NULL)
		QueuePush(&Qu, root);
	while (QueueSize(&Qu))
	{
		
		BinaryTreeNode* from = QueueFront(&Qu);
		if (from == NULL)
			break;
		//删除
        QueuePop(&Qu);
		//需要注意的是删除只是释放掉存储了二叉树节点的地址的空间,并没有释放二叉树节点
		QueuePush(&Qu, from->left);
		QueuePush(&Qu, from->rigth);
	}
	//判断后面是否还有非空
	while (!QueueEmtry(&Qu))
	{
		BinaryTreeNode* from = QueueFront(&Qu);
		if (from != NULL)
			return 0;
		//删除
		QueuePop(&Qu);
	}
	QueueDestroy(&Qu);
	return 1;

}

知识点

前序:深度优先遍历
层序: 广度优先遍历

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

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

相关文章

Java生成word[doc格式转docx]

引入依赖 <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.32</version></dependency> doc…

针对net core 使用CSRedis 操作redis的三种连接实例方式

1、主从访问 2、哨兵模式 3、集群访问 写法一&#xff1a;写任意一个地址即可&#xff0c;其它节点在运行过程中自动增加&#xff0c;确保每个节点密码一致。如&#xff1a;Console.WriteLine("集群测试");RedisHelper.Initialization(new CSRedis.CSRedisClient(&q…

Http和WebSocket

客户端发送一次http请求&#xff0c;服务器返回一次http响应。 问题&#xff1a;如何在客户端没有发送请求的情况下&#xff0c;返回服务端的响应&#xff0c;网页可以得服务器数据&#xff1f; 1&#xff1a;http定时轮询 客户端定时发送http请求&#xff0c;eg&#…

回溯和分支算法

状态空间图 “图”——状态空间图 例子&#xff1a;农夫过河问题——“图”状态操作例子&#xff1a;n后问题、0-1背包问题、货郎问题(TSP) 用向量表示解&#xff0c;“图”由解向量扩张得到的解空间树。 ——三种图&#xff1a;n叉树、子集树、排序树 ​ 剪枝 不满住条件的…

链表【3】

文章目录 &#x1f433;23. 合并 K 个升序链表&#x1f41f;题目&#x1f42c;算法原理&#x1f420;代码实现 &#x1f437;25. K 个一组翻转链表&#x1f416;题目&#x1f43d;算法原理&#x1f367;代码实现 &#x1f433;23. 合并 K 个升序链表 &#x1f41f;题目 题目链…

Sentinel基础知识

Sentinel基础知识 资源 1、官方网址&#xff1a;https://sentinelguard.io/zh-cn/ 2、os-china: https://www.oschina.net/p/sentinel?hmsraladdin1e1 3、github: https://github.com/alibaba/Sentinel 一、软件简介 Sentinel 是面向分布式服务架构的高可用流量防护组件…

Unity 关于SetParent方法的使用情况

在设置子物体的父物体时&#xff0c;我们使用SetParent再常见不过了。 但是通常我们只是使用其中一个语法&#xff1a; public void SetParent(Transform parent);使用改方法子对象会保持原来位置&#xff0c;跟使用以下方法效果一样&#xff1a; public Transform tran; ga…

【数值计算方法(黄明游)】函数插值与曲线拟合(二):Newton插值【理论到程序】

​ 文章目录 一、近似表达方式1. 插值&#xff08;Interpolation&#xff09;2. 拟合&#xff08;Fitting&#xff09;3. 投影&#xff08;Projection&#xff09; 二、Lagrange插值1. 拉格朗日插值方法2. Lagrange插值公式a. 线性插值&#xff08;n1&#xff09;b. 抛物插值&…

UDS 诊断报文格式

文章目录 网络层目的N_PDU 格式诊断报文的分类&#xff1a;单帧、多帧 网络层目的 N_PDU(network protocol data unit)&#xff0c;即网络层协议数据单元 网络层最重要的目的就是把数据转换成符合标准的单一数据帧&#xff08;符合can总线规范的&#xff09;&#xff0c;从而…

原生横向滚动条 吸附 页面底部

效果图 /** 横向滚动条 吸附 页面底部 */ export class StickyHorizontalScrollBar {constructor(options {}) {const { el, style } optionsthis.createScrollbar(style)this.insertScrollbar(el)this.setScrollbarSize()this.onEvent()}/** 创建滚轴组件元素 */createS…

【踩坑】解决maven的编译报错Cannot connect to the Maven process. Try again later

背景 新公司新项目, 同事拷给我maven的setting配置文件, 跑项目编译发现maven报 Cannot connect to the Maven process. Try again later. If the problem persists, check the Maven Importing JDK settings and restart IntelliJ IDEA 虽然好像不影响, 项目最终还是能跑起来…

计算机组成学习-存储系统总结

复习本章时&#xff0c;思考以下问题&#xff1a; 1)存储器的层次结构主要体现在何处&#xff1f;为何要分这些层次&#xff1f;计算机如何管理这些层次&#xff1f;2)存取周期和存取时间有何区别&#xff1f;3)在虚拟存储器中&#xff0c;页面是设置得大一些好还是设置得小一…

视频剪辑转码:mp4批量转成wmv视频,高效转换格式

在视频编辑和处理的领域&#xff0c;转换格式是一项常见的任务。在某些编辑和发布工作中&#xff0c;可能需要使用WMV格式。提前将素材转换为WMV可以节省在编辑过程中的时间和精力。从MP4到WMV的批量转换&#xff0c;不仅能使视频素材在不同的平台和设备上得到更好的兼容性&…

JavaScript基础—for语句、循环嵌套、数组、操作数组、综合案例—根据数据生成柱形图、冒泡排序

版本说明 当前版本号[20231129]。 版本修改说明20231126初版20231129完善部分内容 目录 文章目录 版本说明目录JavaScript 基础第三天笔记for 语句for语句的基本使用循环嵌套倒三角九九乘法表 数组数组是什么&#xff1f;数组的基本使用定义数组和数组单元访问数组和数组索引…

centos7 设置静态ip

文章目录 设置VMware主机设置centos7 设置 设置VMware 主机设置 centos7 设置 vim /etc/sysconfig/network-scripts/ifcfg-ens33重启网络服务 service network restart检验配置是否成功 ifconfig ip addr

爬虫概念、基本使用及一个类型和六个方法(一)

目录 一、爬虫简介 1.什么是爬虫 2.爬虫的核心 3.爬虫的用途 4.爬虫的分类 5.反爬手段 二、Urllib基本使用 1.导入我们需要的包 2.定义一个url 3.模拟浏览器向服务器发送请求 4.获取响应中的页面的源码 5.打印数据 三、一个类型和六个方法 1.定义url&#xff0c;并…

BFS求树的宽度——结合数组建树思想算距离

二叉树最大宽度 https://leetcode.cn/problems/maximum-width-of-binary-tree/description/ 1、考虑树的宽度一定是在一层上的所以进行BFS&#xff0c;树的BFS不建议直接使用队列&#xff0c;每次add/offer然后poll/remove&#xff0c;这样子层级关系不好显示。我们可以定义…

深入解析常见的软件架构模式

在软件开发领域&#xff0c;选择合适的架构模式对于项目的可维护性和扩展性至关重要。本文将深入探讨常见的软件架构模式&#xff0c;包括MVC、MVP、MVVM、MVVM-C以及VIPER。 1. MVC&#xff08;Model-View-Controller&#xff09; MVC 是一种经典的软件架构模式&#xff0c;将…

电子学会C/C++编程等级考试2022年06月(四级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:公共子序列 我们称序列Z = < z1, z2, ..., zk >是序列X = < x1, x2, ..., xm >的子序列当且仅当存在 严格上升 的序列< i1, i2, ..., ik >,使得对j = 1, 2, ... ,k, 有xij = zj。比如Z = < a, b, f, c &…

MYSQL练题笔记-聚合函数-即时食物配送

我做完上一道题&#xff0c;决定总结一下了&#xff0c;因为现在还是没有一个我认为好的思路去构造语句&#xff0c;这里开始试一试新的思路。果然想要好一点的时候&#xff0c;总是像便秘一下&#xff0c;真的想拉&#xff0c;但是真的难拉啊 一、题目相关内容 1&#xff09…