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

二叉树——进阶

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

二叉树的递归创建

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;
}

如果有帮助就点个赞吧!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

金融信贷风控系统设计模式应用之模版方法

背景介绍 风控系统每种场景 如个人消费贷 都需要跑很多规则 规则1 申请人姓名身份证号实名验证规则2 申请人手机号码实名认证规则3 银行卡预留手机号码实名认证规则4 申请人银行卡预留手机号码在网状态检验规则5 申请人银行借记卡有效性核验规则6 户籍地址与身份证号归属地比…

微信小程序知识点1

一. 页面样式和结构 1.1 小程序组件(html) (1) 区域布局组件 view 定义块级区域&#xff0c;相当于网页中的 div 标签text 定义行内区域&#xff0c;相当于网页中的 span标签 (2) 链接跳转组件 navigator 组件相当于网页中的 a 标签&#xff0c;用来实现页面之间的跳转。 …

基于卷积神经网络的交通标志识别(pytorch,opencv,yolov5)

文章目录 数据集介绍&#xff1a;resnet18模型代码加载数据集&#xff08;Dataset与Dataloader&#xff09;模型训练训练准确率及损失函数&#xff1a;resnet18交通标志分类源码yolov5检测与识别&#xff08;交通标志&#xff09; 本文共包含两部分&#xff0c; 第一部分是用re…

力扣刷题---2206. 将数组划分成相等数对【简单】

题目描述&#x1f357; 给你一个整数数组 nums &#xff0c;它包含 2 * n 个整数。 你需要将 nums 划分成 n 个数对&#xff0c;满足&#xff1a; 每个元素 只属于一个 数对。 同一数对中的元素 相等 。 如果可以将 nums 划分成 n 个数对&#xff0c;请你返回 true &#xf…

高开高走的续作,可不止《庆余年2》

说起最近霸屏的影视剧&#xff0c;莫过于《庆余年2》。火爆全网的讨论度总归是没有辜负观众们五年的等待&#xff0c;在五月的影视市场独占鳌头已成定局。张若昀、陈道明、李沁等一众演员稳定发挥&#xff0c;剧情节奏随着故事发展渐入佳境&#xff0c;评分一路高涨。 对影视作…

【网络安全】社会工程学攻击与防范

一、社会工程学概述 1、社会工程学的定义 通过利用人们的心理弱点、本能反应、好奇心、信任、贪婪等一些心理陷阱进行的诸如欺骗、伤害、信息盗取、利益谋取等对社会及人类带来危害的行为或方法。 当网络恶意攻击者无法通过纯粹的计算机技术达到目的时&#xff0c;高超的情商…

统计信号处理基础 习题解答10-4

题目&#xff1a; 重复习题10.3&#xff0c;但条件PDF变为&#xff1a; 以及均匀先验。如果非常大&#xff0c;这样先验知识很少&#xff0c;则会出现什么情况。 解答&#xff1a; 如果记 那么&#xff0c;根据条件独立性质&#xff0c;得到&#xff1a; 其中&#xff0c;&am…

Web3 游戏平台 Creo Engine 销毁代币总量的20%,以促进长远发展

Creo Engine 5月16日进行了第三次代币销毁&#xff0c;这次的销毁占代币总量的 20%。一共销毁了2亿 $CERO 代币&#xff0c;市场价值接近 2000 万美元。 Creo Engine 致力于连接世界、为玩家提供一站式游戏中心&#xff0c;并提升 Web3 游戏体验。 Creo Engine 发布于2022年&am…

Python学习---基于TCP协议的网络通信程序案例

TCP简介&#xff1a; ●TCP 面向连接、可靠的、基于字节流的传输控制协议 ●TCP的特点 ○面向连接 ○可靠传输 ■应答机制 ■超时重传 ■错误校验 ■流量管控 ●TCP通信模型 TCP严格区分客户…

运维Tips | Linux系统文件命令执行时inode表如何变化?

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路。 ] 大家好&#xff0c;我是【WeiyiGeek/唯一极客】一个正在向全栈工程师(SecDevOps)前进的技术爱好者 作者微信&#xff1a;WeiyiGeeker 公众号/知识星球&#xff1a;全栈工程师修炼指南 主页博…

Linux下自旋锁的学习使用

前言 前面我们讲到定时器的使用,本篇讲下自旋锁的使用。想第一时间看我的文章的话可以点击公众号主页右上角有个设为星标&#xff0c;以免错过好文。本文源码采用Linux内核5.10 自旋锁简介 自旋锁是Linux内核里最常用的锁之一&#xff0c;自旋锁的概念很简单&#xff0c;就是…

误差反向传播简介与实现

误差反向传播 导语计算图反向传播链式法则 反向传播结构加法节点乘法节点 实现简单层加法乘法 激活函数层实现ReLUSigmoid Affine/Softmax层实现Affine基础版批版本 Softmax-with-Loss 误差反向传播实现梯度确认总结参考文献 导语 书上在前一章介绍了随机梯度下降法进行参数与…

ubuntu20.04 开机自动挂载外加硬盘

文章目录 一、问题描述二、操作1. 查找新添盘符2. 格式化硬盘文件系统3. 挂载硬盘4. 开机自动挂载5. 取消挂载6. 查看挂载的硬盘信息 一、问题描述 因电脑使用一段时间后自身硬盘不足&#xff0c;需外加硬盘使得电脑自动识别加载。 二、操作 1. 查找新添盘符 sudo blkid自己…

linux中的arch命令使用

arch 显示当前主机的硬件架构类型 概要 arch [OPTION]...主要用途 打印机器架构信息&#xff1b;arch 命令输出结果有&#xff1a;i386、i486、i586、alpha、sparc、arm、m68k、mips、ppc、i686等。 选项 --help 显示帮助信息并退出。 --version 显示版本信息并…

windows7的ie11降级到ie8

重点是要在程序管理窗口中“查看已安装的更新”打开当前系统中已安装更新列表&#xff0c;找到两个IE11的更新&#xff08;见下图“卸载文件“&#xff09;并卸载掉&#xff0c;这样windows功能中的ie11才会变成ie8. 打开控制面板 进入面板&#xff0c;点击程序&#xff0c;进…

hot100 -- 回溯(上)

目录 &#x1f35e;科普 &#x1f33c;全排列 AC DFS &#x1f6a9;子集 AC DFS &#x1f382;电话号码的字母组合 AC DFS &#x1f33c;组合总和 AC DFS &#x1f35e;科普 忘记 dfs 的&#xff0c;先看看这个&#x1f447; DFS&#xff08;深度优先搜索&#xf…

ping 探测网段哪些地址被用

#!/bin/bash# 遍历192.168.3.1到192.168.3.254 for i in {1..254} doip"192.168.3.$i"# 对每个IP地址进行三次ping操作if ping -c 3 -W 1 $ip > /dev/null 2>&1thenecho "$ip: yes"fi done$ sh test.sh 192.168.3.1: yes 192.168.3.95: yes 192.…

MySQL中的sql语句

MySQL中的sql语句 DML、 DDL、 DCL DML(Data Manipulation Language)&#xff0c;用于对数据库中的数据进行操作&#xff0c;包括插入、查询、更新和删除数据等操作。常见的 DML 命令包括 SELECT&#xff08;查询&#xff09;、INSERT&#xff08;插入&#xff09;、UPDATE&a…

Windows系统安装dlib及face_recognition搭建人脸识别环境

关于face_recognition face_recognition被称为世界上最简洁的人脸识别库&#xff0c;借助face_recognition库&#xff0c;我们可以使用Python和命令行提取、识别、操作人脸。 face_recognition的人脸识别是基于业内领先的C开源库 dlib中的深度学习模型&#xff0c;用Labeled …

数据结构——栈(详细分析)

目录 &#x1f349;引言 &#x1f349;栈的本质和特点 &#x1f348;栈的基本操作 &#x1f348;栈的特点 &#x1f34d;后进先出 &#x1f34d;操作受限 &#x1f34d;动态调整 &#x1f348;栈的优缺点 &#x1f34d;优点 &#x1f34d;缺点 &#x1f349;栈的应用…