首页 > 其他分享 >二叉树的一些相关操作

二叉树的一些相关操作

时间:2024-11-04 21:18:51浏览次数:3  
标签:遍历 TreeNode 二叉树 相关 操作 NULL root 节点

前言

        链式结构二叉树的访问基本使用递归来进行访问,同时也可以体现出递归的暴力美学。因为有涉及到二叉树的相关操作,因此先要手动构造一个二叉树,在对其进行一些操作。

        本篇一切对二叉树的操作都基于我创建的二叉树

        这个是我构造的一颗二叉树 :

 

//二叉树节点结构
struct TreeNode
{
	int val;//存储数据
	struct TreeNode* left;//左节点地址
	struct TreeNode* right;//右节点地址
};
//创建二叉树节点
struct TreeNode* BuyNode(int x)
{
	//创建节点
	struct TreeNode* node = (struct TreeNode*)malloc(sizeof(TreeNode));
	node->val = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
//手动构造一个二叉树
struct TreeNode* CreateTree()
{
	struct TreeNode* A = BuyNode(1);
	struct TreeNode* B = BuyNode(2);
	struct TreeNode* C = BuyNode(3);
	struct TreeNode* D = BuyNode(4);
	struct TreeNode* E = BuyNode(5);
	struct TreeNode* F = BuyNode(6);
	A->left = B;
	A->right = C;
	B->left = D;
	C->left = E;
	C->right = F;
	return A;
}

 一、前序遍历

         前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发⽣在遍历其左右⼦树之前

        访问顺序为:根结点、左⼦树、右⼦树 

//前序遍历
void LastOrder(struct TreeNode* root)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	//访问顺序为:根结点、左⼦树、右⼦树 
	printf("%d ", root->val);
	LastOrder(root->left);
	LastOrder(root->right);
}

 

 二、中序遍历

        中序遍历(Inorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之中(间)

        访问顺序为:左⼦树、根结点、右⼦树 

//中序遍历
void InOrder(struct TreeNode* root)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	//访问顺序为:左⼦树、根结点、右⼦树
	InOrder(root->left);
	printf("%d ", root->val);
	InOrder(root->right);
}

 

 三、后序遍历

        后序遍历(Postorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之后

        访问顺序为:左⼦树、右⼦树、根结点 

//后序遍历
void PostOrder(struct TreeNode* root)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	//访问顺序为:左⼦树、右⼦树、根结点
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->val);
}

 

 四、二叉树节点个数

        在递归调用的同时统计不是NULL节点就行

//树中节点个数
int TreeSize(struct TreeNode* root)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		return 0;
	}
	//节点不为空就统计节点个数
	//继续访问该节点的左右子树
	return TreeSize(root->left) + TreeSize(root->right) + 1;
}

五、树中叶子节点个数 

         叶子节点即树中左右子树为空的节点

//树中叶子节点个数
int TreeLeafSize(struct TreeNode* root)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		return 0;
	}
	//左右节点为空时,表示该节点为叶子节点
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	//继续访问该节点的左右子树
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

 

六、 二叉树中第K层节点个数

         每次向下递归时,在当前的层数基础上减一,直到当前层数为1

//二叉树中第K层节点个数
int TreeLevelSize(struct TreeNode* root, int k)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		return 0;
	}
	//当前层数为1时,表示已经到了目标层数
	if (k - 1 == 0)
	{
		return 1;
	}
	//每向下访问左右子树时,同时层数减一
	return TreeLevelSize(root->left, k - 1) + TreeLevelSize(root->right, k - 1);
}

 

七、二叉树的深度 

        每次向下递归时,深度加一,分别统计左右子树返回的最大深度并返回 

//二叉树的深度
int TreeDeep(struct TreeNode* root)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		return 0;
	}
	//分别统计左右子树返回的最大深度并返回 
	return max(TreeDeep(root->left) + 1, TreeDeep(root->right) + 1);
}

 

 八、二叉树的查找

        遍历二叉树,寻找与之匹配的节点

//二叉树的查找
struct TreeNode* TreeFind(struct TreeNode* root, int n)
{
	//判断根节点是否为空
	if (root == NULL)
	{
		return NULL;
	}
	//找到就返回
	if (root->val == n)
	{
		return root;
	}
	//记录答案
	struct TreeNode* ans = NULL;
	if (ans == NULL)
	{
		ans = TreeFind(root->left, n);
	}
	if (ans == NULL)
	{
		ans = TreeFind(root->right, n);
	}
	return ans;
}

九、二叉树层序遍历 

        ⾃上⽽下,⾃左⾄右逐层访问树的结点的过程就是层序遍历

        实现层序遍历需要额外借助数据结构:队列

//二叉树层序遍历
void LevelOrder(struct TreeNode* root)
{
	//创建队列
	queue<struct TreeNode*> queue;
	//将根节点放入队列
	queue.push(root);
	//如果队列不为空就将队头节点的左右子树放入队列,前提是左右节点不为空
	//每次将队头的数据输出并输出
	while (!queue.empty())
	{
		printf("%d ", queue.front()->val);
		if (queue.front()->left != NULL)
		{
			queue.push(queue.front()->left);
		}
		if (queue.front()->right != NULL)
		{
			queue.push(queue.front()->right);
		}
		queue.pop();
	}
}

 

十、判断是否为完全二叉树 

        通过层序遍历,把NULL也入队,如果出队的是NULL,判断队列中剩余的元素是不是都是 NULL

//判断是否为完全二叉树
//层序遍历
//把NULL也入队,如果出队的是NULL,判断队列中剩余的元素是不是都是 NULL
bool IsNo(struct TreeNode* root)
{
	queue<struct TreeNode*> queue;
	queue.push(root);
	while (queue.front() != NULL)
	{
		queue.push(queue.front()->left);
		queue.push(queue.front()->right);
		queue.pop();
	}
	//判断队列中剩余的元素是不是都是 NULL
	//如果都是NULL就是完全二叉树
	//如果不只有NULL就不是完全二叉树
	while (!queue.empty())
	{
		if (queue.front() != NULL)
		{
			return false;
		}
		queue.pop();
	}
	return true;
}

 

相关练习题链接

        单值⼆叉树 965. 单值二叉树 - 力扣(LeetCode)         相同的树 . - 力扣(LeetCode)         对称⼆叉树 101. 对称二叉树 - 力扣(LeetCode)         另⼀棵树的⼦树 572. 另一棵树的子树 - 力扣(LeetCode)         前序遍历 144. 二叉树的前序遍历 - 力扣(LeetCode)         中序遍历 94. 二叉树的中序遍历 - 力扣(LeetCode)         后序遍历 145. 二叉树的后序遍历 - 力扣(LeetCode)         ⼆叉树的构建及遍历 二叉树遍历_牛客题霸_牛客网

标签:遍历,TreeNode,二叉树,相关,操作,NULL,root,节点
From: https://blog.csdn.net/LVZHUO_2022/article/details/143437395

相关文章

  • 数据库操作与数据管理——Rust 与 SQLite 的集成
    第六章:数据库操作与数据管理第一节:Rust与SQLite的集成在本节中,我们将深入探讨如何在Rust中使用SQLite数据库,涵盖从基本的CRUD操作到事务处理、数据模型的构建、性能优化以及安全性考虑等方面。SQLite是一个轻量级的关系型数据库,适合嵌入式应用和小型项目。我们将利......
  • ArkUI常用数据处理:掌握Map操作与动态数据管理
    在HarmonyOS应用开发中,ArkUI框架提供了丰富的数据处理能力,尤其是对于Map这类非线性容器的操作。本文将详细介绍ArkUI中Map的基本概念、操作方法,以及如何在实际开发中应用Map进行数据处理和动态数据管理。Map的重要性Map是非线性容器的一种,它提供了快速查找、插入和删除键值......
  • C语言版数据结构算法(考研初试版—4)--链表删除操作
    删除链表中值为m的结点(1)创建一个链表(2)打印删除前的链表(3)查找值为m的前一个结点(4)执行删除操作(5)打印删除后的链表#include<stdio.h>#include<stdlib.h>typedefstructLNode{ intdata; structLNode*next;}LNode,*LinkList;//头插法LinkListCreateList_L(){......
  • 内存函数的相关知识点
    1strerrorchar*strerror(interrnum)从语言的库函数在运行的时候,如果发生错误,就会将错误码放在一个变量中,这个变量是errnor.//strerror(errno)//fopen//FILE*fopen(constchar*filename,constchar*mode);//如果打开文件成功,就返回一个有效的指针,如......
  • 连接数据库与JDBC的简单操作
    连接数据库的步骤没有错,但是mysql--8与jar包--5的版本不匹配导致数据库连接不成功,后面导入8版本的jar包后就连接数据库成功了。但是报错:Exceptioninthread"main"java.sql.SQLException:Theservertimezonevalue'�й���׼ʱ��'isunrecognizedorrepresentsmorethanoneti......
  • 二叉树的递归遍历(前、中、后序)
    二叉树的递归遍历题目链接:前序遍历:LeetCode144中序遍历:LeetCode94后序遍历:LeetCode145描述给你二叉树的根节点root,返回它节点值的前序、中序、后序遍历。示例1:前序遍历输入:root=[1,null,2,3]输出:[1,2,3]示例2:中序遍历输入:root=[1,null,......
  • 物联网操作系统
    物联网1.操作系统的出现和迭代是时代需求和技术制约下的平衡上承落地应用,下接海量终端前端数据的收集 高速传递数据和信息 OTA升级 端云互联一体(连接管理平台确保物联网系统自主运行) 在云侧,以云平台为支撑的中间件服务、数据服务和信息服务目前现状:终端......
  • 操作字符串都有哪些类以及它们之间有什么区别
     1.**String**:  -是不可变对象。每次对String类型进行修改时都会生成一个新的对象。  -适用于不频繁修改字符串的情况。2.**StringBuilder**:  -线程不安全,效率高,多用于单线程环境。  -适用于需要频繁修改字符串的操作。3.**StringBuffer**:  -线程安全......
  • 操作字符串都有哪些类以及它们之间有什么区别
     1.**String**:  -是不可变对象。每次对String类型进行修改时都会生成一个新的对象。  -适用于不频繁修改字符串的情况。2.**StringBuilder**:  -线程不安全,效率高,多用于单线程环境。  -适用于需要频繁修改字符串的操作。3.**StringBuffer**:  -线程安全......
  • 操作系统线程的组织与调度(schedule)
    一、线程调度schedule在操作系统中,调度器(Scheduler)的主要任务是管理CPU的时间分配给各个进程和线程,以优化特定的性能指标,如响应时间、吞吐量和CPU利用率。调度器通常分为三类:长期调度器、中期调度器和短期调度器,但在现代操作系统中,最常讨论的是短期调度器,即CPU调度器。下面是......