二叉树——进阶(递归创建,非递归,广度优先,翻转,深度,对称)

news2024/11/20 2:32:46

二叉树——进阶

  • 二叉树的递归创建
  • 非递归前中后序遍历
    • 非递归前序遍历
    • 非递归中序遍历
    • 非递归后序遍历
  • 广度优先遍历二叉树(层序遍历)
    • 翻转二叉树
  • 二叉树深度
    • 最大深度
    • 最小深度
  • 对称二叉树

二叉树的递归创建

1,二叉树是一种结构相对固定的数据,分为根,左节点,右节点,把一颗大树拆分开每部分都是如此,这就有了递归创建的条件。

2,采用递归,第一时间需要思考递归的三步骤。

  • 基本情况(Base Case): 定义递归算法终止的条件。在递归的过程中,需要确定一个或多个基本情况,当满足这些情况时,递归将停止并返回结果,避免进入无限递归的循环。

  • 递归调用(Recursive Call): 在算法的定义中,通过调用自身来解决问题的步骤。递归调用必须逐渐朝着基本情况靠近,以确保算法能够最终终止。

  • 向基本情况靠近(Making Progress Towards Base Case): 确保递归调用的每一步都朝着基本情况的方向迈进。这意味着在每次递归调用中,问题的规模都会减小,最终达到一个基本情况。

3,这里采用先序创建二叉树,思考递归的终止条件,显而易见,就是遇见空节点时,数据采用整数,每层需要自己输入,还需要返回值,返回根节点。

代码实现

typedef struct Binary_Tree
{
	DataType val;

	struct Binary_Tree* left;
	struct Binary_Tree* right;

	struct Binary_Tree() {}
	struct Binary_Tree(int v)
	{
		val = v;
		left = NULL;
		right = NULL;
	}
}TreeNode;

//递归先序创建树
TreeNode* create_Tree()
{
	int x;
	cin >> x;

	if (x == -1)  //终止条件
	{
		return NULL;
	}
	
	//数据处理,单层的逻辑执行
	TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
	if (root == NULL)
		exit(-1);

	root->val = x;
	
	//递归创建左右树
	root->left = create_Tree();
	root->right = create_Tree();
	
	//返回结果
	return root;
}

非递归前中后序遍历

1, 非递归遍历树,需要用到数据结构——栈来模拟递归时的过程,并且更加复杂一些,需要判断循环终止条件,循环中对树节点的处理,需要多种数据辅助。

非递归前序遍历

非递归先序遍历二叉树的要点通常包括以下几个步骤

1,使用栈数据结构: 在非递归算法中,通常会使用栈来模拟递归调用的过程。栈用于存储待访问的节点,以便稍后访问它们的子节点。

2,遍历顺序: 先序遍历的顺序是先访问根节点,然后递归地访问左子树和右子树。因此,在非递归算法中,首先将根节点入栈。

3,迭代过程:

  • 弹出栈顶元素,访问该节点。
  • 将该节点的右子节点(如果存在)入栈,然后将左子节点(如果存在)入栈。注意要先将右子节点入栈,再将左子节点入栈,以保证左子节点在栈中靠近栈顶,右子节点在栈中靠近栈底,这样才能保证先访问左子树。
  • 重复以上步骤,直到栈为空。

4,循环条件: 在循环中,应该判断栈是否为空,以确定是否继续遍历。当栈为空时,表示已经遍历完整棵树。

代码实现

//非递归先序遍历
void non_recursion_PreorderTree(TreeNode* root)
{
	if (root == NULL)
	{
		return;
	}

	//栈结构
	stack<TreeNode*> Tree;

	Tree.push(root);

	while (!Tree.empty())
	{
		TreeNode* tem = Tree.top();

		cout << tem->val << " ";
		Tree.pop();

		if (tem->right != NULL)
			Tree.push(tem->right);

		if (tem->left != NULL)
			Tree.push(tem->left);
	}
}

非递归中序遍历

非递归中序遍历二叉树的要点包括以下几个步骤

1,使用栈数据结构: 与先序遍历类似,在非递归算法中也会使用栈来模拟递归调用的过程。栈用于存储待访问的节点,以便稍后访问它们的子节点。

2,遍历顺序: 中序遍历的顺序是先访问左子树,然后访问根节点,最后访问右子树。因此,在非递归算法中,首先将根节点入栈,并将当前节点指向根节点的左子节点。

3,迭代过程:

  • 将当前节点的所有左子节点入栈,直到没有左子节点为止。
  • 弹出栈顶元素,访问该节点。
  • 将当前节点指向该节点的右子节点。
  • 重复以上步骤,直到栈为空且当前节点为空。

4,循环条件: 在循环中,应该判断栈是否为空或当前节点是否为空,以确定是否继续遍历。当栈为空且当前节点为空时,表示已经遍历完整棵树。

代码实现

//非递归中序遍历
void non_recursion_MidorderTree(TreeNode* root)
{
	if (root == NULL)
	{
		return;
	}

	stack<TreeNode*> Tree;
	TreeNode* cur = root;
	Tree.push(cur);
	while (!Tree.empty() || cur)
	{
		if (cur && cur->left != NULL)
		{
			Tree.push(cur->left);
			cur = cur->left;
		}
		else
		{
			cur = Tree.top();
			cout << cur->val << " ";
			Tree.pop();
			if (cur->right)
			{
				Tree.push(cur->right);
			}
			cur = cur->right;
		}
	}
}

非递归后序遍历

非递归后序遍历二叉树的要点包括以下几个步骤

1,首先,检查根节点是否为空。如果为空,则直接返回,因为空树无需遍历。

2,创建一个栈 s 用于存储待访问的节点。

3,初始化当前节点 cur 和前一个访问过的节点 pre 为根节点 root。

4,进入循环,条件是当前节点不为空或者栈不为空。这保证了遍历能够进行到所有节点都被访问过为止。

5,在循环中,首先进行左子树的遍历。从根节点开始,将当前节点和其所有左子节点依次入栈,直到当前节点为空。

6,接着,检查栈顶元素(即当前节点)的右子树情况。如果右子树存在且未被访问过,则将当前节点指向右子节点,并重新进入左子树遍历的步骤。

7,如果当前节点的右子树为空或者已经被访问过,则说明当前节点是可以访问的。输出当前节点的值,并将当前节点标记为上一个访问过的节点 pre,然后将当前节点设为 NULL,表示已经访问过。最后,将当前节点出栈。

8,重复步骤 4 到步骤 7,直到栈为空,表示所有节点都已经遍历完成。

代码实现

//非递归后序遍历
void non_recursion_rearorderTree(TreeNode* root)
{
	if (root == NULL)
	{
		return;
	}

	TreeNode* cur = root;
	TreeNode* pre = root;
	stack<TreeNode*> s;
	while (cur || !s.empty())
	{
		//先入左树,后入右树
		while (cur)
		{
			s.push(cur);
			cur = cur->left;
		}

		//检查右树
		cur = s.top();
		if (cur->right && cur->right != pre)  //右节点为空或者已访问则访问根节点
		{
			cur = cur->right;
		}
		else
		{
			cout << cur->val << " ";
			pre = cur;
			cur = NULL;
			s.pop();
		}
	}
}

广度优先遍历二叉树(层序遍历)

层序遍历是一种广度优先搜索(BFS)的方法,它按照树的层级顺序逐层访问节点。以下是层序遍历的关键步骤:

1,创建一个队列(通常使用队列来实现 BFS),并将根节点入队。

2,进入循环,直到队列为空。循环的条件是队列不为空,这保证了所有节点都能被访问到。

3,在循环中,首先从队列中取出队首节点,并访问该节点。这个节点是当前层级要访问的节点。

4,将当前节点的所有子节点(左子节点和右子节点)依次入队。这样可以保证下一轮循环时,访问的是下一层级的节点。

5,重复步骤 2 到步骤 4,直到队列为空,表示所有层级的节点都已经访问完毕。

代码实现

void BinaryTreeLevelOrder(TreeNode* root)
{
	if (root == nullptr)
	{
		return;
	}
	queue<TreeNode*> q;
	q.push(root);
	while (!q.empty())
	{
		int len = q.size();
		for (int i = 0; i < len; i++)
		{
			TreeNode* p = q.front();
			q.pop();
			cout << p->val << " ";

			if (p->left)
				q.push(p->left);
			if (p->right)
				q.push(p->right);
		}
	}
}

翻转二叉树

在这里插入图片描述

题目链接:翻转二叉树

递归方法

1,递归终止条件: 如果当前节点为空,直接返回。

2,递归反转: 交换当前节点的左右子节点。

3,递归调用: 分别对当前节点的左右子节点进行递归反转。

4,返回根节点: 最后返回根节点,作为反转后的二叉树的根。

//递归翻转二叉树(左右树节点交换)
void Filp_Tree(TreeNode* root)
{
	if (root == NULL)
	{
		return;
	}

	swap(root->left, root->right);//函数
	Filp_Tree(root->left);
	Filp_Tree(root->right);
}

二叉树深度

最大深度

在这里插入图片描述
题目链接:二叉树的最大深度

本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

理解如何计算二叉树的最大深度可以通过以下步骤来实现

1,递归终止条件: 如果当前节点为空,则返回深度 0。

2,递归计算: 递归地计算左子树和右子树的最大深度。

3,深度计算: 将左右子树的最大深度中较大的值加 1,表示当前节点的深度。

4,返回结果: 返回左右子树中较大深度加上当前节点深度作为结果。

#include <stdio.h>
#include <stdlib.h>

// 定义二叉树节点结构体
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

// 定义一个函数来计算二叉树的最大深度
int maxDepth(struct TreeNode* root) {
    // 如果根节点为空,说明树为空,最大深度为0
    if (root == NULL) {
        return 0;
    }
    
    // 递归计算左右子树的最大深度
    int left_depth = maxDepth(root->left);
    int right_depth = maxDepth(root->right);
    
    // 返回左右子树中较大深度加上根节点的深度(1)
    return left_depth > right_depth ? left_depth + 1 : right_depth + 1;
}

int main() {
    // 示例:构建一个二叉树
    struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->val = 1;
    root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->left->val = 2;
    root->left->left = NULL;
    root->left->right = NULL;
    root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->right->val = 3;
    root->right->left = NULL;
    root->right->right = NULL;

    // 计算二叉树的最大深度
    int depth = maxDepth(root);
    printf("二叉树的最大深度为:%d\n", depth);

    // 释放二叉树内存
    free(root->left);
    free(root->right);
    free(root);

    return 0;
}

最小深度

在这里插入图片描述

题目链接:最小深度

计算二叉树的最小深度是指从根节点到最近叶子节点的最短路径上的节点数。与计算最大深度类似,但有一些特殊情况需要处理。以下是计算二叉树最小深度的步骤:

1,递归终止条件: 如果当前节点为空,则返回深度 0。

2,处理只有一个子树的情况: 如果当前节点只有一个子树,那么需要递归计算另一侧子树的深度,并返回不为空的子树深度加 1。

3,递归计算: 递归地计算左子树和右子树的最小深度。

4,深度计算: 将左右子树的最小深度中较小的值加 1,表示当前节点的深度。

5,返回结果: 返回左右子树中较小深度加上当前节点深度作为结果。

代码实现

 int minDepth(TreeNode *root) {
        if (root == nullptr) {
            return 0;
        }

        if (root->left == nullptr && root->right == nullptr) {
            return 1;
        }

        int min_depth = INT_MAX;
        if (root->left != nullptr) {
            min_depth = min(minDepth(root->left), min_depth);
        }
        if (root->right != nullptr) {
            min_depth = min(minDepth(root->right), min_depth);
        }

        return min_depth + 1;
    }


对称二叉树

在这里插入图片描述
题目链接:对称二叉树

检查对称二叉树的思路主要是比较二叉树的左右子树是否镜像对称。以下是检查对称二叉树的步骤

1,递归终止条件: 如果当前节点为空,则返回 true。

2,递归比较: 递归地比较左右子树的对称节点。

3,比较规则: 对称节点的比较规则是左子树的左节点与右子树的右节点比较,左子树的右节点与右子树的左节点比较。

4,返回结果: 如果所有对称节点都满足比较规则,则返回 true,否则返回 false。

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

// 定义二叉树节点结构体
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

// 递归函数检查对称性
bool isMirror(struct TreeNode* left, struct TreeNode* right) {
    // 如果左右节点都为空,则对称
    if (left == NULL && right == NULL) {
        return true;
    }

    // 如果左右节点有一个为空,则不对称
    if (left == NULL || right == NULL) {
        return false;
    }

    // 如果左右节点的值不相等,则不对称
    if (left->val != right->val) {
        return false;
    }

    // 递归比较左子树的左节点与右子树的右节点,左子树的右节点与右子树的左节点
    return isMirror(left->left, right->right) && isMirror(left->right, right->left);
}

// 检查对称二叉树
bool isSymmetric(struct TreeNode* root) {
    // 调用递归函数检查对称性
    return isMirror(root, root);
}

int main() {
    // 示例:构建一个对称二叉树
    struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->val = 1;
    root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->left->val = 2;
    root->left->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->left->left->val = 3;
    root->left->left->left = NULL;
    root->left->left->right = NULL;
    root->left->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->left->right->val = 4;
    root->left->right->left = NULL;
    root->left->right->right = NULL;
    root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->right->val = 2;
    root->right->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->right->left->val = 4;
    root->right->left->left = NULL;
    root->right->left->right = NULL;
    root->right->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->right->right->val = 3;
    root->right->right->left = NULL;
    root->right->right->right = NULL;

    // 检查对称二叉树
    if (isSymmetric(root)) {
        printf("二叉树是对称的\n");
    } else {
        printf("二叉树不是对称的\n");
    }

    // 释放二叉树内存
    free(root->left->left);
    free(root->left->right);
    free(root->left);
    free(root->right->left);
    free(root->right->right);
    free(root->right);
    free(root);

    return 0;
}

如果有帮助就点个赞吧!

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

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

相关文章

SpringBoot接入Knife4j接口文档

0.介绍 1&#xff09; Knife4j是什么 Knife4j是Java MVC框架集成Swagger生成Api文档的增强解决方案&#xff0c;前身是swagger-bootstrap-ui&#xff0c;有着比Swagger更为美观的UI以及功能。 例如以下效果图&#xff1a; 2&#xff09; 官方链接 官网&#xff1a;Knife4j …

Mujava 工具的简单使用

首先下载openjava.jar和mujava.jar&#xff0c;以及自己手写一个mujava.config指向存放mujava的目录&#xff0c;并将这些文件放在mujava目录下。此时&#xff0c;基本的mujava环境就搭建好了。 分别创建src&#xff08;存放源码文件&#xff09;、classes&#xff08;存放源码…

使用pygame绘制图形

参考链接&#xff1a;https://www.geeksforgeeks.org/pygame-tutorial/?reflbp 在窗口中绘制单个图形 import pygame from pygame.locals import * import sys pygame.init()window pygame.display.set_mode((600,600)) window.fill((255,255,255))# pygame.draw.rect(wind…

针对上一篇微信同声传译语音播报部分坑的解决和优化

1. 上一篇语音播报其实是不完美的&#xff0c;就是如何停止上一个音频开始下一个音频的问题&#xff0c;我在此做一下修改 比如说&#xff1a;现在正在播放1&#xff0c;我点击2让2开始播放&#xff0c;1停止播放&#xff0c;我上面的写法是有问题的&#xff1a; 通过 innerAu…

编译器 编译过程 compiling 动态链接库 Linking 接口ABI LTO PGO inline bazel增量编译

编译器 编译过程 compiling 动态链接库 Linking 接口ABI LTO PGO Theory Shared Library Symbol Conflicts (on Linux) 从左往右查找:Note that the linker only looks further down the line when looking for symbols used by but not defined in the current lib.Linux 下…

【全部更新完毕】2024电工杯B题详细思路代码成品文章教学:大学生平衡膳食食谱的优化设计及评价

大学生平衡膳食食谱的优化设计及评价 摘要 大学阶段是学生获取知识和身体发育的关键时期&#xff0c;也是形成良好饮食习惯的重要阶段。然而&#xff0c;当前大学生中存在饮食结构不合理和不良饮食习惯的问题&#xff0c;主要表现为不吃早餐或早餐吃得马虎&#xff0c;经常食用…

JVM学习-垃圾收集器(三)

G1回收器-区域化分代式 为了适应不断扩大的内存和不断增加的处理器数量&#xff0c;进一步降低暂停时间&#xff0c;同时兼顾良好的吞吐量官方给G1设定的目标&#xff1a;延迟可控的情况下获得尽可能高的吞吐量&#xff0c;所以才担当起“全功能收集器”的重任与期望G1是一款面…

构建数字未来:探索Web3在物联网中的新视角

引言 随着Web3时代的来临&#xff0c;物联网技术正迎来一场新的变革。在这个数字化时代&#xff0c;Web3所带来的技术创新将为物联网的发展开辟新的视角。本文将深入探讨Web3在物联网领域的应用&#xff0c;揭示其在构建数字未来中的重要性和影响。 Web3与物联网的融合 区块链…

Docker学习笔记(二)Dockerfile自定义镜像、DockerCompose、Docker私有镜像仓库

文章目录 前言3 Dockerfile自定义镜像3.1 镜像结构3.2 Dockerfile文件3.3 构建自定义镜像3.3.1 基于Ubuntu构建Java项目3.3.2 基于Java8构建Java项目 3.4 小结 4 DockerCompose4.1 安装DockerCompose4.2 部署微服务集群 5 Docker私有镜像仓库 前言 Docker学习笔记(一)安装Dock…

ctfhub中的SSRF的相关例题(下)

目录 URL Bypass 知识点 相关例题 数字IP Bypass 相关例题 方法一&#xff1a;使用数字IP 方法二&#xff1a;转16进制 方法三&#xff1a;用localhost代替 方法四&#xff1a;特殊地址 302跳转 Bypass ​编辑 关于localhost原理: DNS重绑定 Bypass 知识点&…

每日练习之数学——砝码和天平

砝码和天平 题目描述 运行代码 #include<iostream> using namespace std; int main() {int w,m,T;cin>>T;while(T--){cin>>w>>m;while(m){if((m-1)%w0)m(m-1)/w;else if((m1)%w0)m(m1)/w;else if(m%w0)m/w;else break;}if(!m)cout<<"YES&…

「职场必备」让你摆脱思维混乱的7个工具

1. 升维思考&#xff0c;降维拆解 解决复杂问题时&#xff0c;有两个关键的阶段&#xff0c;能让我们事半功倍。 第一个阶段是思考阶段&#xff0c;要自下而上进行“升维思考”&#xff0c;明确问题的本质是什么。第二阶段是行动阶段&#xff0c;要自上而下进行“降维拆解”&am…

Excel查找匹配函数(VLOOKUP):功能与应用解析

文章目录 概述VLOOKUP函数语法查询并返回单列结果查找并返回多列结果MATCH函数VLOOKUPMATCH 从右向左逆向查找&#xff1a;INDEX函数INDEXMATCH 函数匹配方式查找匹配注意事项函数名称错误: #NAME?值错误&#xff1a;#VALUE!引用错误&#xff1a;#REF!找不到数据&#xff1a;#…

1、NLP分词

分词处理 1、token&#xff08;词汇单元&#xff09;2、Tokenizer&#xff08;分词&#xff09;3、ElasticSearch 分词器&#xff08;Analyzer&#xff09;4、分词工具停用词&#xff08;Stop words&#xff09; 1、token&#xff08;词汇单元&#xff09; “token”主要用于文…

AI早班车5.25

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d; 一位上进心十足的【Java ToB端大厂…

51-53 DriveWorld:通过自动驾驶世界模型进行 4D 预训练场景理解 (含模型数据流梳理)

24年5月&#xff0c;北京大学、国防创新研究院无人系统技术研究中心、中国电信人工智能研究院联合发布了DriveWorld: 4D Pre-trained Scene Understanding via World Models for Autonomous Driving。 DriveWorld在UniAD的基础上又有所成长&#xff0c;提升了自动驾驶目标检测…

linux之防火墙工具

netfilter Linux防火墙是由Netfilter组件提供的&#xff0c;Netfilter工作在内核空间&#xff0c;集成在linux内核中。 Netfilter在内核中选取五个位置放了五个hook(勾子) function(INPUT、OUTPUT、FORWARD、PREROUTING、POSTROUTING)&#xff0c;而这五个hook function向用户…

人工智能应用-实验8-用生成对抗网络生成数字图像

文章目录 &#x1f9e1;&#x1f9e1;实验内容&#x1f9e1;&#x1f9e1;&#x1f9e1;&#x1f9e1;代码&#x1f9e1;&#x1f9e1;&#x1f9e1;&#x1f9e1;分析结果&#x1f9e1;&#x1f9e1;&#x1f9e1;&#x1f9e1;实验总结&#x1f9e1;&#x1f9e1; &#x1f9…

Stable Diffusion【艺术特效】【霓虹灯】:霓虹灯像素化马赛克特效

提示词 Neon pixelated mosaic of [Subject Description],highly detailed [主题]的霓虹灯像素化马赛克&#xff0c;高度详细 参数设置 大模型&#xff1a;万享XL_超写实摄影V8.2 采样器&#xff1a;Euler a 采样迭代步数&#xff1a;25 CFG&#xff1a;3 反向提示词&#x…

Docker Desktop安装和如何在WSL2中使用Docker

最近在使用WSL的过程中&#xff0c;想使用docker遇到了一些问题&#xff0c;在WSL中安装Linux版本的docker&#xff0c;启动镜像之后不能从Windows机器的端口映射出来&#xff0c;查了一圈之后&#xff0c;发现应该使用Docker Desktop软件&#xff0c;下面是安装和使用的方式 …