二叉树的链式结构 - 遍历 - C语言递归实现

news2024/11/17 1:33:42
前序、中序以及后序遍历
        二叉树遍历 (Traversal) 是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次
按照规则,二叉树的遍历有: 前序/中序/后序 的递归结构遍历
  • 1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  • 2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  • 3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
        由于被访问的结点必是某子树的根, 所以 N(Node )、 L(Left subtree )和 R(Right subtree )又可解释为 根、根的左子树和根的右子树 NLR LNR LRN 分别又称为 先根遍历、中根遍历和后根遍历。
// 二叉树前序遍历
void PreOrder(BTNode* root);
// 二叉树中序遍历
void InOrder(BTNode* root);
// 二叉树后序遍历
void PostOrder(BTNode* root);
前序遍历递归图解

 //.........................................以上概念性内容均来自比特科技..........................................................

为了更直观的了解,首先手搓一颗二叉树

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


typedef int BTDataType;
typedef struct BinaryTreeNode
{
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
	BTDataType data;
}BTNode;


BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	assert(node);

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;

	return node1;
}

其次,我们使用前/中/后序来访问一下:

代码实现:

前序:

//前序
void PreOrder(BTNode* root){
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

中序:

//中序
void InOrder(BTNode* root){
	if (root == NULL)
	{
		printf("# ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

后序:

//后序
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

变形

思路:分治算法,分而治之,把大问题转换成类似规模的小问题。(一般采用递归)

1.求二叉树结点的个数

         这里有两种方法,①是定义一个全局变量,每递归一次,变量就++一次。

         ②是先求出左子树结点的个数再加上右子树结点的个数,最后再加上根。因为是递归,所以一直可以向下分割成左右子树,直到遇见NULL树,才不再分割。以下例题,多数都是这种思想。

//求二叉树结点的个数

int count = 0;
int TreeSize1(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	count++;//注:这里需要定义全局变量,因为是递归,所以不能使用局部变量

	TreeSize1(root->left);
	TreeSize1(root->right);
}

int TreeSize2(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize2(root->left) + TreeSize2(root->right) + 1;
	
	//if (root == NULL)
	//{
	//	return 0;
	//}
	//else
	//{
	//	TreeSize2(root->left) + TreeSize2(root->right) + 1;
	//}

}

2.求叶子结点的个数

思路:也是两种方法,第一种方法是在上一道例题的基础上,创建一个全局变量,用if判断。

第二种方法就是求出左树和右树的叶子结点然后相加。

//求叶子结点的个数
int TreeLeafSize1(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	if (root->left == NULL && root->right == NULL)
	{
		count++;//定义一个全局变量
	}

	TreeLeafSize1(root->left);
	TreeLeafSize1(root->right);
	
}

//方法2
int TreeLeafSize2(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

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

	return TreeLeafSize2(root->left) + TreeLeafSize2(root->right);
}

3.求第k层结点的个数

思路:转换成子问题,求左子树的k-1层+右子树的k-1层

//求第k层结点的个数
int TreeKLevel(BTNode* root, int k){
	assert(k >= 1);
	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;

	return TreeKLevel(root->left, k - 1) +
		TreeKLevel(root->right, k - 1);
}

4.求二叉树的深度

思路:求出左树的深度,再求出右树的深度,加1是加根,然后比较以下,谁大/深返回谁。

//求二叉树的深度
int TreeDepth(BTNode* root)
{
	if (root == NULL)
		return NULL;

	int leftDepth = TreeDepth(root->left)+1;
	int rightDepth = TreeDepth(root->right)+1;

	return leftDepth > rightDepth ? leftDepth  : rightDepth ;
}

5.二叉树中查找值为x结点的值

注:这里值得注意的一点是,返回值的问题以及跳出递归。用前序遍历一下就行。

//二叉树中查找值为x结点的值
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data == x)
		return root;

	BTNode* ret1 = TreeFind(root->left, x);
	if (ret1) //这里的条件是ret1不为空
		return ret1;

	BTNode* ret2 = TreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}


oj练习 - C语言解题

1. 单值二叉树 Oj链接
bool isUnivalTree(struct TreeNode* root){
    if(root == NULL)
         return true;
    
    if(root->left && root->left->val != root->val)
        return false;

     if(root->right && root->right->val != root->val)
          return false;
     
   return  isUnivalTree(root->left)&&
     isUnivalTree(root->right);
    //同时为true 才为true
    //一个为false 就返回false

    //根  和左孩子右孩子比较,如果不相同,就return false
    //如果相同,就继续向下看左子树,再看右子树
    //左子树继续向下划分根 左孩子 右孩子

}
2. 检查两颗树是否相同 OJ链接
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if( p == NULL && q == NULL)//同时为空
          return true;
    
    if(p == NULL  ||  q == NULL)//走到这里必定有一个为空,一个不为空
          return false;

    if(p->val != q->val )
       return false;

    return  isSameTree(p->left,q->left)//左子树跟左子树比较
     && isSameTree(p->right,q->right);//右子树跟右子树比较
 
}

3. 对称二叉树 OJ链接
bool isSymmetricSubTree(struct TreeNode* root1,struct TreeNode* root2)
{
    if(root1 == NULL && root2 == NULL)
        return true;

    if(root1 == NULL || root2 == NULL)//走到这里必定一个为空一个不为空
       return false;
    
    if(root1->val != root2->val)//必定同时不为空,比较val值
        return false;
    
    return isSymmetricSubTree(root1->left,root2->right)
    &&isSymmetricSubTree(root1->right,root2->left);
    //左树的左孩子比较右树的右孩子
    //左树的右孩子比较右树的左孩子
}

bool isSymmetric(struct TreeNode* root){
    
    if(root == NULL)
       return true;

    //以根为分割线 -- 镜像二叉树
    
    return isSymmetricSubTree(root->left,root->right);
    //根的左孩子右孩子传过去
}
4. 二叉树的前序遍历 OJ链接
//注意:返回的值必须要放进malloc申请的数组空间里面
 //returnSize :默认不给空间大小,也就是我们需要求出数组的大小

//首先求出malloc数组空间的大小
int TreeSize(struct TreeNode* root)
{
    return root == NULL ? 0 : TreeSize(root->left)+TreeSize(root->right)+1;
}

//这里为什么i需要传地址,因为我们需要的是一个i 而不是栈帧空间中的每一个独立的i
//因为下面子树改变i,不会影响上面根的i,这就导致数据可能会重叠,导致后面的空间出现随机数
void prevorder(struct TreeNode* root,int* arr,int* i)
{
    if(root == NULL)
        return;

    arr[(*i)++] = root->val;

    prevorder(root->left,arr,i);
    prevorder(root->right,arr,i);

}

int* preorderTraversal(struct TreeNode* root, int* returnSize){
    
    *returnSize = TreeSize(root);

    int* arr = (int*)malloc(*returnSize * sizeof(int));

    int i = 0;//数组下标
    prevorder(root,arr,&i);
 
    return arr;

}
5. 另一颗树的子树 OJ链接
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if( p == NULL && q == NULL)//同时为空
          return true;
    
    if(p == NULL  ||  q == NULL)//走到这里必定有一个为空,一个不为空
          return false;

    if(p->val != q->val )
       return false;

    return  isSameTree(p->left,q->left)//左子树跟左子树比较
     && isSameTree(p->right,q->right);//右子树跟右子树比较
 
}

 //思路:把原树中的所有子树都找出来与subRoot进行比较
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){

   if(root == NULL)
      return false;


    //遍历所有子树进行比较 ,subroot与root所有的根节点进行比较
    if(isSameTree(root,subRoot))
        return true;
   
    //角度2.
    //先与当前树进行比较,如果不是当前树的子树
    //那么就去与当前树的左子树进行比较
    //如果也不是
    //就与当前树的右子树进行比较
    //只要有一个为true 就可以,所有这里是或|| 
    return isSubtree(root->left,subRoot)
            || isSubtree(root->right,subRoot);

}

6.二叉树的构建及遍历 OJ链接
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>


typedef char BTDataType;
typedef struct BinaryTreeNode {
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
    BTDataType data;
} BTNode;


BTNode* BuyNode(BTDataType x) {
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    assert(node);

    node->data = x;
    node->left = NULL;
    node->right = NULL;

    return node;
}

BTNode* CreateTree(char* arr,int* i)//前序构建
{
    if(arr[*i] == '#')
    {
        (*i)++;
        return NULL;
    }

    BTNode* root = BuyNode(arr[(*i)++] );

    root->left = CreateTree(arr,i);//先构建左子树
    root->right = CreateTree(arr,i);//再构建右子树

    return root;//返回根节点
}

//中序遍历
void Inorder(BTNode* root)
{
    if(root == NULL)
        return ;

    Inorder(root->left);
    printf("%c ",root->data);
    Inorder(root->right);

}

int main()
{
    char arr[100]={0};
    scanf("%s",arr);

    int i=0;//数组下标
    BTNode* root = CreateTree(arr,&i);
    Inorder(root);

    return 0;
}





以上仅供参考

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

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

相关文章

2023天津Java培训学校分享!Java培训班

近年来&#xff0c;各类培训机构如雨后春笋般涌现&#xff0c;其中&#xff0c;Java培训机构可谓是风头正盛&#xff0c;许多想踏入这行的小伙伴选择这个方式来学习Java技能&#xff0c;今天我们一起来讨论一下&#xff1a;学Java有门槛吗&#xff0c;Java培训的好处&#xff0…

问题解决:VS Code环境调试多文件C++程序

在VS code环境下默认可以调试单文件的C程序&#xff0c;如果是多文件的程序&#xff0c;则会出现编译不通过的问题&#xff0c;无法进行调试 解决方法 在VS Code的工程目录下&#xff0c;有一个tasks.json文件 修改tasks.json文件 其中&#xff0c;"args"子项里面…

android app控制ros机器人三(android登录界面)

接下来是二次开发的具体环节了&#xff0c;由于存在用户需求&#xff0c;用到ros-mobile不多&#xff0c;更偏向于android开发。 用ppt画了简单的展示界面&#xff0c;与用后交流界面的功能布局。先开发一代简易版本的app&#xff0c;后续可以丰富完善。ctrlcv上线。 登录界面…

图数据库Neo4j学习三——cypher语法总结

1MATCH 1.1作用 MATCH是Cypher查询语言中用于从图数据库中检索数据的关键字。它的作用是在图中查找满足指定条件的节点和边&#xff0c;并返回这些节点和边的属性信息。 在MATCH语句中&#xff0c;通过节点标签和边类型来限定查找范围&#xff0c;然后通过WHERE语句来筛选符合…

vue+leaflet笔记之地图量测

vueleaflet笔记之地图量测 文章目录 vueleaflet笔记之地图量测开发环境代码简介插件简介与安装使用简介图形量测动态量测 详细源码(Vue3) 本文介绍了Web端使用Leaflet开发库进行距离量测的一种方法 (底图来源:天地图)&#xff0c;结合leaflet-measure-path插件能够快速的实现地…

人工智能术语翻译(四)

文章目录 摘要MNOP 摘要 人工智能术语翻译第四部分&#xff0c;包括I、J、K、L开头的词汇&#xff01; M 英文术语中文翻译常用缩写备注Machine Learning Model机器学习模型Machine Learning机器学习ML机器学习Machine Translation机器翻译MTMacro Average宏平均Macro-F1宏…

高忆管理:msci成分股什么意思?

MSCI&#xff08;Morgan Stanley Capital International&#xff09;是全球领先的金融指数提供商之一&#xff0c;其指数被广泛应用于全球资本商场的出资和危险办理。而MSCI成分股&#xff0c;是指MSCI指数中所包括的股票。那么&#xff0c;MSCI成分股具体意义是什么呢&#xf…

CTFshow-pwn入门-pwn67(nop sled空操作雪橇)

前言 本人由于今年考研可能更新的特别慢&#xff0c;不能把ctfshow的pwn入门题目的wp一一都写出来了&#xff0c;时间比较紧啊&#xff0c;只能做高数做累的时候做做pwn写写wp了&#xff0c;当然我之后只挑典型意义的题目写wp了&#xff0c;其余的题目就留到12月底考完之后再写…

Sugar BI : AI 问答,即问即答

AI 探索功能提供给所有用户自由探索和分析数据模型的能力。在 AI 探索页中&#xff0c;有授权的用户可以通过 AI 问答和字段拖拽两种方式对数据模型进行探索。 下面&#xff0c;我们将为大家详细指导如何使用 AI 探索 新建 AI 探索页 空间管理员可以在报表管理中新建「AI 探索…

短视频矩阵系统源码---开发技术源码能力

短视频矩阵系统开发涉及到多个领域的技术&#xff0c;包括视频编解码技术、大数据处理技术、音视频传输技术、电子商务及支付技术等。因此&#xff0c;短视频矩阵系统开发人员需要具备扎实的计算机基础知识、出色的编程能力、熟练掌握多种开发工具和框架&#xff0c;并掌握音视…

UE Web Remote Control call python script

UE Web Remote Control call python script UE 远程调用Python(UE Python API)脚本 Web Remote Control 在网页客户端远程操作虚幻引擎项目。 虚幻编辑器提供了一套强大的工具&#xff0c;几乎可以操纵项目内容的方方面面。但在某些情况下&#xff0c;要在大型内容编辑流程中…

使用SVM模型完成分类任务

SVM&#xff0c;即支持向量机&#xff08;Support Vector Machine&#xff09;&#xff0c;是一种常见的机器学习算法&#xff0c;用于分类和回归分析。SVM的基本思想是将数据集映射到高维空间中&#xff0c;在该空间中找到一个最优的超平面&#xff0c;将不同类别的数据点分开…

国企普通员工如何才能成为公务员,这三种途径可供参考

国企普通员工如何转变成公务员&#xff1f;作为国企普通员工&#xff0c;如果要成为国家公务员&#xff0c;其主要的路径有三个方面&#xff0c;一是符合国家公务员法规定的公务员招录条件要求的&#xff0c;可以报考国家公务员&#xff1b;二是在国有企业担任领导职务&#xf…

使用EM算法完成聚类任务

EM算法&#xff08;Expectation-Maximization Algorithm&#xff09;是一种基于迭代优化的聚类算法&#xff0c;用于在无监督的情况下将数据集分成几个不同的组或簇。EM算法是一种迭代算法&#xff0c;包含两个主要步骤&#xff1a;期望步骤&#xff08;E-step&#xff09;和最…

子网重叠测试

子网重叠的两个网络可以相互通 虽然子网掩码不同&#xff0c;但是 R1 可以 ping R2&#xff1a; <R1>ping 10.0.12.14PING 10.0.12.14: 56 data bytes, press CTRL_C to breakReply from 10.0.12.14: bytes56 Sequence1 ttl255 time50 msReply from 10.0.12.14: bytes5…

Verilog语法学习——LV4_移位运算与乘法

LV4_移位运算与乘法 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 已知d为一个8位数&#xff0c;请在每个时钟周期分别输出该数乘1/…

利用小波分解信号,再重构

function [ output_args ] example4_5( input_args ) %EXAMPLE4_5 Summary of this function goes here % Detailed explanation goes here clc; clear; load leleccum; s leleccum(1:3920); % 进行3层小波分解&#xff0c;小波基函数为db2 [c,l] wavedec(s,3,db2); %进行…

剑指 Offer 37. 序列化二叉树 / LeetCode297. 二叉树的序列化与反序列化(二叉树遍历(深度优先搜索))

题目&#xff1a; 链接&#xff1a;剑指 Offer 37. 序列化二叉树&#xff1b;LeetCode 297. 二叉树的序列化与反序列化 难度&#xff1a;困难 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0…

【雕爷学编程】Arduino动手做(99)---8X32 LED点阵屏模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

基于Java+SpringBoot+vue前后端分离新闻推荐系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…