数据结构-二叉树中的递归

目录

前言

简单手撕二叉树

二叉树节点的求解

二叉树叶子节点的求解

二叉树高度

二叉树第K层节点的个数

二叉树查找值为X的节点

结束语


前言

在这里说声抱歉,好久没更新数据结构了,二叉树的相关内容还没有更新完,是小编的失职,接下来,小编将对二叉树的内容进行补充完善。

前期我们讲解了二叉树的顺序结构(堆的实现),二叉树的遍历进行讲解,本节内容将对二叉树的节点,高度等的访问求解进行讲解。而这些问题都要用到递归的思想,一步步拆成小问题进行解答。

简单手撕二叉树

首先我们将简单手撕一个二叉树,一个节点包括值和孩子兄弟的指针,在将一个个节点连接起来就可以构造一个简单的二叉树。

typedef struct BTnode {int val;struct BTnode* left;struct BTnode* right;
}Node;//节点创建
Node* BuyNode(int x) {Node* node = (Node*)malloc(sizeof(Node));if (node == NULL) {perror("node fail");return NULL;}node->val = x;node->left = NULL;node->right = NULL;return node;
}
//树的创建
Node* CreatTree() {Node* node1 = BuyNode(1);Node* node2 = BuyNode(2);Node* node3 = BuyNode(3);Node* node4 = BuyNode(4);Node* node5 = BuyNode(5);Node* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}

二叉树节点的求解

如果树是空树,则返回0,不是空树,可以拆成左子树+右子树+1(递归调用)

int TreeSize(Node* root)
{static int size = 0;if (root == NULL)return 0;else++size;TreeSize(root->left);TreeSize(root->right);return size;
}

来看这段代码,思路上没有什么问题,第一次运行的结果也将是正确的,担当我们连续求就会出问题,结果是递增的,就会出错。

由于size是静态变量,它在整个程序的生命周期内只会被初始化一次。这意味着,如果在计算不同二叉树的节点数量时调用这个函数,size的值将不会重置为0,而是会从上一次调用结束时的值继续增加。这会导致计算结果不正确。

故正确的做法是在每次计算之前将size重置为0。

衍生出下面得到代码形式,设置成全局变量或者多传一个参数。

int size = 0;
int TreeSize(Node* root)
{if (root == NULL)return 0;else++size;TreeSize(root->left);TreeSize(root->right);return size;
}void TreeSize(Node* root, int* psize)
{if (root == NULL)return 0;else++(*psize);TreeSize(root->left, psize);TreeSize(root->right, psize);
}

两个代码原理其实都是差不多的,只是后面一个将size变成了指针形参。当然也可以直接返回,将++size直接结合到递归中。

int TreeSize(Node* root) {if (root == NULL) {return 0;}return TreeSize(root->left) + TreeSize(root->right) + 1;
}

二叉树叶子节点的求解

二叉树叶子节点求解是一样的思路,1.空树->0  2.非空 左子树和右子树是否为空

同样递归的思想。

  • if (root == NULL):检查当前节点是否为NULL,如果是,则表示已经到达了树的末尾,没有叶子节点,返回0。

  • if (root->left == NULL && root->right == NULL):检查当前节点是否是叶子节点,即它的左右子节点都为NULL。如果是,返回1,表示找到了一个叶子节点。

  • return TreeLeafSize(root->left) + TreeLeafSize(root->right);:如果当前节点不是叶子节点,那么递归调用TreeLeafSize函数,分别计算当前节点的左子树和右子树中的叶子节点数量,并将这两个数量相加返回。

int TreeLeafSize(Node* root) {if (root == NULL) {return 0;}if (root->left == NULL && root->right == NULL)return 1;return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

二叉树高度

对于二叉树的高度,我们可以一层层加,转化为子问题 ,就是在左右子树中高的子树高度加1即可。

int height(Node* root) {if (root == NULL) {return 0;}return (height(root->left)>height(root->right)?height(root->left):height(root->right)) + 1;
}

上面这个版本虽然能计算出二叉树的高度,但是我们会发现它会冗余的进行递归,在进行比较后,又会递归求解比较高的子树,然后又会进行递归,所以我们可以每次递归后将树的高度用变量保存起来。 

int Height(Node* root) {if (root == NULL) {return 0;}int left = Height(root->left);int right = Height(root->right);return (left > right ? left : right) + 1;
}

二叉树第K层节点的个数

int TreeKSize(Node* root, int k) {if (root == NULL)return 0;if (k == 1)return 1;return(TreeKSize(root->left, k - 1) + TreeKSize(root->right, k - 1));}

通过递归的方式,一层层地向下遍历树,直到达到深度 k,然后统计该层上的节点数量。最终,函数返回所有深度为 k 的节点总数。

二叉树查找值为X的节点

最先想到的肯定是遍历二叉树,如果值相等,就返回节点。

我们同样可以采用递归的方式

Node* Treefind(Node* root, int x) {if (root == NULL)return NULL;if (root->val == x)return root;Node* ret1 = Treefind(root->left, x);if (ret1)return ret1;Node* ret2 = Treefind(root->right, x);if (ret2)return ret2;return NULL;}

递归展开图

 

上面代码简化一下: 

Node* TreeFind(Node* root, int x) {if (root == NULL) {return NULL;}if (root->val == x) {return root;}Node* leftResult = TreeFind(root->left, x);if (leftResult != NULL) {return leftResult;}return TreeFind(root->right, x);
}
  • if (root == NULL) { return NULL; }:首先检查当前节点是否为 NULL。如果是,表示已经到达了树的末尾,没有找到值为 x 的节点,因此返回 NULL

  • if (root->val == x) { return root; }:检查当前节点的值是否等于 x。如果等于,表示找到了目标节点,返回当前节点的指针。

  • Node* leftResult = TreeFind(root->left, x);:递归地在左子树中查找值为 x 的节点。

  • if (leftResult != NULL) { return leftResult; }:如果左子树中找到了值为 x 的节点(即 leftResult 不为 NULL),则直接返回该节点的指针。

  • return TreeFind(root->right, x);:如果左子树中没有找到,那么递归地在右子树中查找值为 x 的节点,并返回结果。

这个函数使用了深度优先搜索(DFS)策略,优先在左子树中查找,如果左子树中没有找到,则继续在右子树中查找。一旦找到值为 x 的节点,就立即返回,不再继续搜索。

这个函数是有效的,并且它的效率取决于树的结构。在最坏的情况下,如果树是完全不平衡的,例如退化成一条链表,那么时间复杂度将是 O(n),其中 n 是树中节点的数量。在平衡二叉树的情况下,时间复杂度将是 O(log n)。(了解)

补充:

深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。在这种算法中,沿着一个分支遍历,直到这个分支的末端,然后回溯并沿着另一分支进行遍历,直到所有的节点都被访问过。

以下是深度优先搜索的基本步骤:

  1. 选择一个起始节点:从图或树的某个节点开始。

  2. 探索:从当前节点出发,选择一个相邻的节点进行深入访问。这个相邻节点必须是未被访问过的。

  3. 标记:在访问一个节点时,将其标记为已访问,以避免重复访问。

  4. 回溯:如果当前节点没有未访问的相邻节点,或者所有的相邻节点都已被访问过,那么算法回溯到上一个节点,继续寻找下一个未访问的相邻节点。

  5. 重复步骤2-4:直到所有的节点都被访问过。

深度优先搜索可以使用递归或栈(迭代方式)来实现。

结束语

本节内容就到此结束了,大家对于递归的理解可以通过画图来理解,递归是一个很重要的思想。

最后感谢各位友友的支持!!! 

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

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

相关文章

在基于AWS EC2的云端k8s环境中 搭建开发基础设施

中间件下载使用helm,这里部署的都是单机版的 aws-ebs-storageclass.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:name: aws-ebs-storageclass provisioner: kubernetes.io/aws-ebs parameters:type: gp2 # 选择合适的 EBS 类型,如 gp2、io1…

Apache Calcite - 查询优化之自定义优化规则

RelOptRule简介 为了自定义优化规则,我们需要继承RelOptRule类。org.apache.calcite.plan.RelOptRule 是 Apache Calcite 中的一个抽象类,用于定义优化规则。优化规则是用于匹配查询计划中的特定模式,并将其转换为更优化的形式的逻辑。通过继…

2024网鼎杯青龙组wp:Crypto1

题目 附件内容如下 from Crypto.Util.number import * from secret import flag from Cryptodome.PublicKey import RSAp getPrime(512) q getPrime(512) n p * q d getPrime(299) e inverse(d,(p-1)*(q-1)) m bytes_to_long(flag) c pow(m,e,n) hint1 p >> (51…

Python 单元测试中的 Mocking 与 Stubbing:提高测试效率的关键技术

在软件开发过程中,单元测试是确保代码质量的重要环节。为了实现高效的单元测试,我们常常需要隔离待测试的代码与其外部依赖。这时候,Mocking(模拟)和 Stubbing(桩)技术就显得尤为重要。这两种技…

Golang | Leetcode Golang题解之第528题按权重随机选择

题目&#xff1a; 题解&#xff1a; type Solution struct {pre []int }func Constructor(w []int) Solution {for i : 1; i < len(w); i {w[i] w[i-1]}return Solution{w} }func (s *Solution) PickIndex() int {x : rand.Intn(s.pre[len(s.pre)-1]) 1return sort.Searc…

3D打印机 屏幕的固定挂钩断后的一次自己修复经历

引子 3D打印机的屏幕固定挂钩断了 这次确实不知道咋断的&#xff0c;这可咋办呢&#xff0c;到网上看了一下&#xff0c;一个屏幕要2佰多&#xff0c;有些小贵&#xff0c;要不就自己修修吧&#xff0c;打个挂钩按上&#xff0c;说干就干。 正文 freecad的设计图如下【其中各…

PHP合成图片,生成海报图,poster-editor使用说明

之前写过一篇使用Grafika插件生成海报图的文章&#xff0c;但是当我再次使用时&#xff0c;却发生了错误&#xff0c;回看Grafika文档&#xff0c;发现很久没更新了&#xff0c;不兼容新版的GD&#xff0c;所以改用了intervention/image插件来生成海报图。 但是后来需要对海报…

Java基于微信小程序的美食推荐系统(附源码,文档)

博主介绍&#xff1a;✌程序猿徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Linux的IP网路命令: 用于显示和操作网络接口(网络设备)的命令ip link详解

目录 一、概述 二、用法 1、基本语法 2、常用选项 3、常用参数 4、获取帮助 三、示例 1. 显示所有网络接口的信息 &#xff08;1&#xff09;命令 &#xff08;2&#xff09;输出示例 &#xff08;3&#xff09;实际操作 2. 启动网络接口 3. 停止网络接口 4. 更改…

【驱动】地平线X3交叉编译工具搭建、源码下载

1、交叉编译工具搭建 1)安装依赖包 sudo apt-get install -y build-essential make cmake libpcre3 libpcre3-dev bc bison \ flex python3-numpy mtd-utils zlib1g-dev debootstrap \ libdata-hexdumper-perl libncurses5-dev zip qemu-user-static \ curl repo git liblz4…

服务器上清理Docker容器运行日志的正确方法

为啥要清理服务器上docker容器的日志 因为是服务器的磁盘空间资源有限&#xff0c;由于docker容器在启动的时候没有限制&#xff0c;导致运行的docker容器随着时间的推移产生的日志越来越多&#xff0c;最后把服务磁盘资源耗尽&#xff0c;服务器的磁盘满了会导致服务器的应用无…

C语言 | Leetcode C语言题解之第526题优美的排列

题目&#xff1a; 题解&#xff1a; int countArrangement(int n) {int f[1 << n];memset(f, 0, sizeof(f));f[0] 1;for (int mask 1; mask < (1 << n); mask) {int num __builtin_popcount(mask);for (int i 0; i < n; i) {if (mask & (1 <<…

SpringBoot篇(自动装配原理)

目录 一、自动装配机制 1. 简介 2. 自动装配主要依靠三个核心的关键技术 3. run()方法加载启动类 4. 注解SpringBootApplication包含了多个注解 4.1 SpringBootConfiguration 4.2 ComponentScan 4.3 EnableAutoConfiguration 5. SpringBootApplication一共做了三件事 …

3D Gaussian Splatting代码详解(二):模型构建

3 模型构建 gaussians GaussianModel(dataset.sh_degree) 3.1 初始化函数 __init__ 构造函数 构造函数 __init__ 的主要作用是初始化 3D 高斯模型的各项参数和激活函数&#xff0c;用于生成 3D 空间中的高斯表示。 初始化球谐函数的参数&#xff1a; self.active_sh_degre…

HarmonyOS-权限管理

一. 权限分类 1. system_grant system_grant 为系统授权&#xff0c;无需询问用户&#xff0c;常用的权限包括网络请求、获取网络信息、获取wifi信息、获取传感器数据等。 /* system_grant&#xff08;系统授权&#xff09;*/static readonly INTERNET ohos.permission.INTE…

如何在 linux 中使用 /etc/fstab 挂载远程共享 ?

在 Linux 领域&#xff0c;高效的管理文件系统和数据存储对于用户和管理员来说&#xff0c;是一项基本技能。 有一种特别有用的技术涉及自动建立远程共享&#xff0c;允许无缝访问网络存储&#xff0c;就好像是本地的一样。 本指南将引导您完成使用 /etc/fstab 文件以自动远程…

Discussion can be found at https://github.com/pypa/pip/issues/10825

这个警告是说你正在使用的 PyPI 镜像&#xff08;在这个例子中是阿里云的镜像&#xff09;返回了一个不符合 HTML 5 标准的网页。这在未来的 pip 版本中可能会导致问题&#xff0c;因为 pip 计划在 22.2 版本中强制执行这一标准。 简单来说&#xff0c;虽然现在这个警告不会阻…

iOS用rime且导入自制输入方案

iPhone 16 的 cantonese 只能打传统汉字&#xff0c;没有繁简转换&#xff0c;m d sh d。考虑用「仓」输入法 [1] 使用 Rime 打字&#xff0c;且希望导入自制方案 [2]。 仓输入法有几种导入方案的方法&#xff0c;见 [3]&#xff0c;此处记录 wifi 上传法。准备工作&#xff1…

ts:常见的运算符

ts&#xff1a;常见的运算符 1 主要内容说明2 表格2.1 算数运算符2.2 赋值运算符2.3 比较运算符2.4 逻辑运算符2.5 位运算符2.6 三元运算符 3 例子3.1 位运算符3.1.1 源码1 &#xff08;位运算符&#xff09;3.1.2 源码1运行效果 3.结语4.定位日期 1 主要内容说明 ts中的各种运…

unity搭建场景学习

unity搭建场景学习 创建场景创建gameobject创建材质&#xff0c;用于给gameobject上色拖拽材质球上色上色原理设置多个材质方式设置贴图的方式 效果设置光滑度一些预览设置菜单渲染模型与碰撞模型网格渲染参数1. materials(材质)2. lighting(光照)3. reflection probes(反射探针…