【二叉树】全家桶-管饱,你敢吃吗?

news2025/1/19 14:31:58

【二叉树扩展学习】💯💯💯

    • 1.【二叉树的创建】
    • 2.【二叉树的销毁】
    • 3.【二叉树的前序遍历】
    • 4.【二叉树的中序遍历】
    • 5.【二叉树的后序遍历】
    • 6.【二叉树的层序遍历】
    • 7.【二叉树的高度】
    • 8.【二叉树结点的个数】
    • 9.【第K层二叉树的结点个数】
    • 10.【二叉树查找值为x的结点】
    • 11.【单值二叉树】
    • 12.【检查两颗树是否相同】
    • 13.【对称二叉树】
    • 14.【另一颗树的子树】
    • 15.【判断一棵树是否是完全二叉树】

1.【二叉树的创建】

通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树

> 基本思想:

<利用前序遍历的方式来创建二叉树>
1.当遇到#则要返回,相当于遇到空返回。
2.当遇到非#,那就要为它创建节点,将它塞到节点里。
3.创建好节点后就要为左右孩子创建节点。

注意点:在递归里如果有要进行自增或自减的变量,不能传变量给函数,而是应该传改变量的指针过去。比如下面的&i

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef char STDataType;
typedef struct BinaryTreeroot
{
	struct BinaryTreeroot* left;
	struct BinaryTreeroot* right;
	STDataType data;

}BTroot;
BTroot* CreatBinaryTree(char*a,int *pi)//创建二叉树
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	//如果不是#那就是字母,如果是字母那就要为它分配节点
	BTroot* root = (BTroot*)malloc(sizeof(BTroot));
	if (root == NULL)
	{
		perror("malloc");
	}
	//将字母塞到节点里
	root->data = a[*pi];
	(*pi)++;
	//创建完节点后就要为左孩子和右孩子创建节点
	root->left=CreatBinaryTree(a, pi);
	root->right= CreatBinaryTree(a, pi);
	return root;
}

2.【二叉树的销毁】

通过后序遍历对二叉树"ABD##E#H##CF##G##"销毁

> 基本思想:
<利用后序遍历思想进行二叉树销毁>
1.销毁二叉树需要将所以节点销毁
2.前序遍历销毁根节点后,左右孩子无法找回,不方便销毁左右孩子
3.后序遍历先销毁左孩子,再销毁右孩子,最后销毁根节点。

void DestroyBinaryTree(BTroot* root)//销毁二叉树
{
	if (root == NULL)
	{
		return;
	}
	//销毁二叉树  先销毁左子树-右子树-根
	DestroyBinaryTree(root->left);
	DestroyBinaryTree(root->right);
	free(root);
}

3.【二叉树的前序遍历】

> 基本思想:
1.一旦遇到空节点就要往后返回

2.前序遍历顺序:先访问根节点—再访问左子树—再访问右子树

void BinaryTreePrevOrder(BTroot* root)//二叉树前序遍历
{
	
	if (root == NULL)
	{
		printf("#");
		return;
		}
			
		printf("%c", root->data);
		BinaryTreePrevOrder(root->left);
		BinaryTreePrevOrder(root->right);
}

递归展开图:
在这里插入图片描述

4.【二叉树的中序遍历】

> 基本思想:
1.一旦遇到空节点就要往后返回

2.中序遍历顺序:先访问左子树—再访问根节点—再访问右子树

void BinaryTreeInOrder(BTroot* root)//二叉树中序遍历
{
	if (root == NULL)
	{
		printf("#");
		return;
	}
	BinaryTreePrevOrder(root->left);
	printf("%c", root->data);
	BinaryTreePrevOrder(root->right);
}

递归展开图:
在这里插入图片描述

5.【二叉树的后序遍历】

> 基本思想:
1.一旦遇到空节点就要往后返回

2.后序遍历顺序:先访问左子树—再访问右子树—再访问根节点

void BinaryTreePostOrder(BTroot* root)//二叉树后序遍历
{
	if (root == NULL)
	{
		printf("#");
		return;
	}
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
	printf("%c", root->data);
}

递归展开图:
在这里插入图片描述

6.【二叉树的层序遍历】

通过利用栈辅助来帮助二叉树遍历

> 基本思想:
1.首先需将根节点入栈。
2.根据栈是否为空来对栈里元素进行出栈
3.每次出栈元素前,先保存下这个元素。
4.每次将元素出栈后,需要将出栈元素的孩子入栈

void LevOlder(BTNode* root)//层序遍历--
{
	Queue q;//定义一个队列
	QueueInit(&q);//初始化队列
	//首先将根 指针插入到队列里去
	if (root)
	{
		QueuePush(&q, root);
	}
	//再出上一层带入下一层
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);//保存一下这个要出队列的指向结点的指针
		QueuePop(&q);
		printf("%d ", front->data);
		//出完后再将它的孩子指针带入进来
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

7.【二叉树的高度】

利用分治递归子问题思想

> 基本思想:
1.求二叉树高度,其实就是求左右子树中较高的子树的高度
2.我们想象根节点是一个校长,它位高权重,并不想下去一个一个数。
3.而左右节点看作院长,校长让左院长去找左子树中最高的。让右院长去找右子树中最高的。
4.而左院长也让它的下面左系主任去数左系最高的,右主任数右系最高……
5.最后院长得到左右院最高的,然后再一比,就可以得到左右院中较高的了。

注意:
1.为什么最后要加1呢?想一想左右子树的高度最后还要加上根节点的这个高度才是最后的高度。
2.注意这里必须要保存左右子树中的最高者,不然递归起来很麻烦。

int BinaryTreeHeight(BTroot* root)//二叉树的高度
{
	if (root == NULL)
		return 0;

	int leftheight = BinaryTreeHeight(root->left);
	int rightheight= BinaryTreeHeight(root->right);
	return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}

递归展开图:
在这里插入图片描述

8.【二叉树结点的个数】

利用分治递归子问题思想

> 基本思想:
1.将根节点可成院长,院长要数全校的人数,它肯定不会自己一个一个数的
2.将任务分给分院长,分院长再将任务分给系主任,系主任再将任务分给班长,班长开始数人数
3.班长数完人数后就开始返回到系主任那报告,报告给主任的数字是原数据上再加1,因为自己还没算进去。
4.系主任得到数据后也返回到分院长那,将数据报告给院长,这个数据是原数据再+1,因为系主任也要把自己算进去。
5.最后校长就得到左右院的人数了。

int BinaryTreeSize(BTroot* root)//二叉树节点的个数
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

递归展开图:
在这里插入图片描述

9.【第K层二叉树的结点个数】

利用分治递归子问题思想

> 基本思想:
1.第K层二叉树的节点个数就是左右子树中第K层节点个数相加
2.而根的第K层,相对于左子树来说是第K-1层,相对于左子树的左子树来说是第K-2层……
3.就比如根的第3层相当于左子树的第2层,相当于左子树的左子树第1层。
4.而我们就需要相当于第一层时节点的个数。

int BinaryTreeLevelKSize(BTroot* root,int k)//二叉树第K层节点的个数
{
	if (root == NULL)
		return 0;
	if (k == 1)//当k=1时,就是根的第K层,只不过对于子树来说是第1层
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

递归展开图:
在这里插入图片描述

10.【二叉树查找值为x的结点】

利用分治递归子问题思想

> 基本思想:
1.当找到该节点后就将该节点返回。
2.先到左子树中找一遍,如果有就返回。
3.再到右子树中找一遍,如果有就返回。
4.最后如果左右子树都没有就返回空。

BTroot* BinaryTreeFind(BTroot* root, STDataType x)//二叉树查找值为x的节点
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTroot* leftx = BinaryTreeFind(root->left, x);
	if (leftx->data == x)
		return leftx;
	BTroot*rightx= BinaryTreeFind(root->right, x);
	if (rightx->data == x)
		return rightx;
	return NULL;

}

11.【单值二叉树】

利用分治递归子问题思想

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
> 基本思想:
1.我们不用一个一个遍历比较
2.将根节点看作校长,校长要全校做核酸,比较,我们不用校长和全校人比较。
3.只要校长和两个院长一起做核酸。一旦出现阳性就表明不同
4.分院长再和2个系主任做核酸,一旦出现阳性表明不同。
5.系主任和班长做核……班长和同学做核酸……。
6.当遇到空时,没有违法规则,返回true.
7.当一旦出现不同的则全校显示的都为阳性。

注意:在访问左子树之前要确保左子树是不为空的。

bool isUnivalTree(BTroot* root)
{
    if(root==NULL)
    {
        return true;
    }
    if(root->left&&root->val!=root->left->val)//首先要确保左子树还存在,才可以访问。校长和左院长做核酸
    {
        return false;//一旦出现阳性,则全校混合都为阳性
    }
    if(root->right&&root->val!=root->right->val)//确保右子树存在,校长和右院长做核酸
    {
        return false;//一旦出现阳性,则全校混合都为阳性
    }

    return isUnivalTree(root->left)&&isUnivalTree(root->right);//最后如果都没有阳性则表明全校都为阴性都相同。
}

12.【检查两颗树是否相同】

利用分治递归子问题思想

如果两个树在结构上相同,并且具有相同的值,则表明它们是相同的。
> 基本思想:
1.首先要确定结构上要相同
2.当为空时应该两个树都为空,当在同一个位置一棵树为空,另一个树不为空,那么这两个树一定不相同。
3.然后确定两个树应该具备相同的值
4.当在相同的位置,两个树的值不相同时,那么这两个树一定不相同。

bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
    if(p==NULL&&q==NULL)//如果遇到空,那么应该同时都遇到空这样才是符合结构相同的
    {
        return true;
    }
    if(p==NULL&&q!=NULL||q==NULL&&p!=NULL)
    {
        return false;//如果在相同的位置一棵树为空,另一棵树不为空,那么肯定不相同。
    }
    if(p->val!=q->val)//如果两棵树的值不相同那么两棵树肯定不相同。
    {
        return false;
    }
    
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);//左子树要和左子树相同,右子树要和右子树相同,当没有结构问题,值问题最后两棵树就相同。
}

13.【对称二叉树】

如何判断一棵树是否对称呢?

利用分治递归子问题思想

> 基本思想:
1.找到可以递归的点
2.如何判断一棵树是否为对称二叉树?
当根节点为空时,是对称,当根节点不为空时,要满足的条件是左右子树对称。
3.如何判断左树与右树是否对称呢?
当左树的左子树等于右树的右子树时。【结构和值必须都相同】
当左树的右子树等于右树的左子树时。【结构和值必须都相同】
4.所以递归点就找到了:
一颗树如何对称?<= = =>左右树对称<= = =>左树左孩子等于右树右孩子&&左树右孩子等于右树左孩子。
一颗树对称取决于所有的左树左孩子等于右树右孩子&&左树右孩子等于右树左孩子。

iscomp函数是用来判断左树与右树是否对称的。

bool iscomp(struct TreeNode* leftroot,struct TreeNode* rightroot)
 {
        if(leftroot==NULL&&rightroot==NULL)//左树和右树都为空时,对称
        return true;
        if(leftroot==NULL||rightroot==NULL||leftroot->val!=rightroot->val)//当左树或者右树其中有一个为空,另一个不为空时,或者左树值不等于右树值都不是对称
        return false;
        return iscomp(leftroot->left,rightroot->right)&&iscomp(leftroot->right,rightroot->left);//对左树的左子树和右树的右子树进行递归判断 对左树的右子树和右树的左子树进行判断,两个都为正,才是对称。一个出现假则不对称。
 }
bool isSymmetric(struct TreeNode* root)
{
    if(root==NULL)//如果根为空,则对称
    return true;
      //如果根不为空,就判断左树和右树是否对称
     return iscomp(root->left,root->right);
}

14.【另一颗树的子树】

利用分治递归子问题思想

看是否另一颗树是否可以看成自己的子树
如果可以那么就是子树,如果不可以那就不是。
> 基本思想:
1.可以利用两个树是否相同的思路。
2.将树1的左子树和子树比较,将树1的右子树和子树比较。
3.如果子树相同返回true
4.如果树1为空那肯定不会有子树了。


bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
    if(p==NULL&&q==NULL)
    {
        return true;
    }
    if(p==NULL&&q!=NULL||q==NULL&&p!=NULL)
    {
        return false;
    }
    if(p->val!=q->val)
    {
        return false;
    }
    
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
     if(root==NULL)
     return false;
     if(isSameTree(root,subRoot))
     {
         return true;//如果树1的子树和子树相同则返回true
     }
    return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);//左右子树中只要有一个子树满足条件即可。

}

15.【判断一棵树是否是完全二叉树】

利用满二叉树性质判断:非空节点连续

> 基本思想:
1.根据满二叉树性质:非空节点是连续出现的。
2.当一旦出现了空节点那么后面就不可以再出现非空节点
3.空节点后面一旦出现非空节点,那么该树一定不是完全二叉树。
4.层序遍历二叉树来判断空节点后面是否还会出现非空节点。

bool BTreeCompele(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)//将根节点插入到队列中
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);//出队列中的元素
		if (front == NULL)
		{
			break;
		}
		//如果front不为空,就将它的孩子插入到队列中去,空节点也插入进去,不需要讨论
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	//break 跳出来需要判断是否后面还会出现非空节点
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);//出队列中的元素
		if (front)//如果队列中出的节点不为空节点
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

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

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

相关文章

C语言之结构体内存对齐与内存的简单理解

文章目录 内存单元的理解结构体中内存对齐的规则为什么会存在内存对齐一、内存单元的理解 首先先要介绍一下C语言中一些常见的存储单元 bit 存放一个二进制位 Byte 1Byte 8 bit KB 1KB 1024 Byte MB 1MB 1024 KB GB 1GB 1024 M…

Linux对文件夹操作(复制,移动)

Linux对文件夹操作(复制,移动) 复制文件夹cp cp -a vue vue-copy将vue 文件夹下面的所有文件,复制到同目录下vue-copy文件夹下面 -a&#xff1a;相当于 -d、-p、-r 选项的集合&#xff0c;这几个选项我们一一介绍&#xff1b;-d&#xff1a;如果源文件为软链接&#xff08;…

数据库开发(Sqlite)

1、数据库开发 1.1 数据与数据管理 什么是信息&#xff1f;   信息是指对现实世界存在方式或运动状态的反应。 什么是数据&#xff1f;   数据是指存储在某一媒体上&#xff0c;能够被识别的物理符号&#xff1b;   数据的概念在数据处理领域已经被大为拓宽&#xff0c…

qt5.14.2 独立msvc环境搭建(不安装vs)

一般情况下msvc下的qt开发&#xff0c;无论你是用qtcreator还是vs都推荐安装对应vs版本&#xff0c;这样是最省事和便捷的&#xff0c;但在有些情况下不便安装vs但项目又需要使用msvc&#xff0c;则可以只安装msvc c编译器&#xff0c;步骤如下&#xff1a; 备注&#xff1a;文…

【Spring Clound】Feign底层原理分析-自动装载动态代理

文章目录前言1、什么是Feign&#xff1f;2、为什么要使用Feign&#xff1f;3、Feign依赖注入原理3.1、Feign自动装载3.2、FeignClientFactoryBean#getObject4、总结前言 是一个HTTP请求调用轻量级框架&#xff0c;可以以Java接口注解的方式调用HTTP请求&#xff0c;而不用像Ja…

Lunabot一款无需密钥魔法就能使用到ChatGPT的浏览器工具

Lunabot 简介 Lunabot是一款跨平台的ChatGPT助手&#xff0c;支持主流浏览器上的任意网页&#xff0c;可以快速处理各种任务&#xff0c;提高您的工作效率。使用 Lunabot&#xff0c;您可以轻松选择文本并使用快速命令和提示来快速处理任务&#xff0c;同时还可以保存永远不会…

Node【二】NPM

文章目录&#x1f31f;前言&#x1f31f;NPM使用&#x1f31f;NPM使用场景&#x1f31f;NPM的常用命令&#x1f31f;NPM命令使用介绍&#x1f31f; 使用NPM安装模块&#x1f31f; 下载三方包&#x1f31f; 全局安装VS本地安装&#x1f31f; 本地安装&#x1f31f; 全局安装&…

研读Rust圣经解析——Rust learn-5(所有权,强大的String)

研读Rust圣经解析——Rust learn-5&#xff08;所有权,强大的String&#xff09;所有权栈和堆相同点栈堆所有权规则作用域StringString创建String创建空字符串从字符串字面量创建&#xff08;将&str转化为String&#xff09;str特点创建str所有权转移String源码深克隆clone…

Matplotlib基本图形使用折线图-柱状图-散点图-饼图的完整代码含示例

目录 Matplotlib基本折线图的使用 1. 导入matplotlib库 2. 准备数据 3. 绘制折线图 4. 加上标签和标题 5. 自定义样式 6. 完整代码 ​编辑 绘制散点图 导入 matplotlib 库和 numpy 库 准备数据 绘制散点图 ​编辑 绘制饼图 导入 matplotlib 库和 numpy 库 准备数…

JS尺寸相关

文章目录元素偏移量 offset 系列offset 系列常用属性offset 与 style 区别获取鼠标在盒子内的坐标模态框放大镜效果元素client 系列元素client系列属性元素滚动 scroll 系列元素 scroll 系列属性页面被卷去的头部兼容性解决方案监听滚动效果仿淘宝固定侧边栏缓动动画筋斗云案例…

实战打靶集锦-015-djinn3

提示&#xff1a;本文记录了作者一次曲折的打靶提权经历 目录1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 Lighttpd探查4.1.1 浏览器探查4.1.2 EXP搜索4.1.2.1 CVE-2019-110724.1.2.2 CVE-2018-190524.2 Werkzeug探查4.2.1 浏览器探查4.2.2 EXP搜索4.2.2.1 目录遍历4.2.2…

Stable Diffusion Web UI + Anaconda环境 + 本地Windows系统部署

Stable Diffusion Web UI Anaconda环境 本地Windows系统部署 最近的很多AIGC模型层出不穷&#xff0c;Stable Diffusion 模型作为一个开源的热门生成式模型&#xff0c;或许对未来的各行各业都能产生深远的影响&#xff0c;了解这个模型并会使用可能是很多人目前想要学习的&…

车载Mini LED持续升温,各家厂商进展如何?

去年&#xff0c;Mini LED背光技术在车载显示赛道上初露头角&#xff0c;多款搭载 Mini LED 屏幕的汽车陆续发布。随着新能源车渗透率的提高&#xff0c;车载显示成为明确增长的赛道&#xff0c;为Mini LED背光进入车载带来利好。 结合今年各家厂商披露的信息来看&#xff0c…

浮点型数据在内存的存储方式

目录 大体规则 特殊规定 由于浮点型在内存中的存储方式相较于整型的要复杂一些&#xff0c;而且很容易忘掉&#xff0c;所以就将部分知识点整理了一下&#xff0c;写成一篇博客。 大体规则 根据国际标准&#xff08;电气和电子工程协会&#xff09;IEEE 754&#xff0c;任意…

【数据结构】- 初识数据结构之空间复杂度(下)

文章目录前言一、空间复杂度1.1空间复杂度简解1.2常见空间复杂度的计算举例二、常见复杂度的对比总结前言 将喜欢的一切留在身边 这便是努力的意义. 本章是关于初识数据结构之空间复杂度(下) 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、空间复…

真的干不过,00后整顿职场已经给我卷麻了,想离职了...

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&#x…

D. Li Hua and Tree(set操作)

Problem - D - Codeforces 李华有一个有n个顶点和n -1条边的树。树的根是顶点1。每个顶点i的重要性为a。将子树的大小表示为该子树中顶点的数量&#xff0c;将重要性表示为该子树中顶点的重要性之和。将非叶顶点的重子结点表示为具有最大子树大小的子结点。如果存在多个重子&am…

安全防御 --- 入侵检测 --- IDS、IPS

入侵检测 1、入侵检测经典理论 系统访问控制要针对三类用户 &#xff08;1&#xff09;合法用户 &#xff08;2&#xff09;伪装 --- 攻破[流程控制]&#xff08;超出了合法用户的行为范围&#xff09; 身份仿冒&#xff08;可能是最早提出不能仅依赖于身份认证&#xff0c;还…

STM32F4+FreeRTOS+LVGL实现嵌入式快速开发(缝合怪)

极速进行项目开发&#xff0c;只需要懂一款芯片架构一个操作系统一个GUI。各种部件程序全靠抄 &#xff0c;成为究极缝合怪。本文用stm32f407FreeRTOSlvgl演示一些demo。 原文链接&#xff1a;STM32F4FreeRTOSLVGL实现快速开发(缝合怪) lvgl官方的音乐播放器demo&#xff1a;…

微信小程序学习笔记

一、Node.js主题 1、npm&#xff1a;node.js包管理工具&#xff0c;有超过60万个JavaScript代码包可供下载 2、Node.js&#xff1a;运行在服务端的JavaScript&#xff0c;基于Chrome JavaScript建立的一个平台&#xff0c;基于Google V8引擎。 3、Nodejs安装教程&#xff1a…