【数据结构】二叉树的遍历和节点个数

目录

一,二叉树的遍历

二,节点的个数和高度

1,求二叉树的节点个数

2,求二叉树的叶子节点的个数

3,求二叉树的高度

4,求二叉树第k层的节点个数

5,二叉树中查找值为x的节点​编辑

6,二叉树的销毁

7,二叉树的层次遍历

8,判断二叉树是否为完全二叉树

六,例题:

1,单值二叉树

2,相同的树

3,判断对称二叉树

4,二叉树的先序遍历

5,另一棵树的子树

6,二叉树的遍历


一,二叉树的遍历

二叉树的遍历分为四种:1,先序遍历 2,中序遍历 3,后序遍历4,层次遍历

给定下面的二叉树,进行遍历演示:

由上面的图可知:二叉树其实是按照递归定义的,每个节点都可以分为该节点和它的左右子树。

先序遍历:先访问该节点,再访问左子树,最后访问右子树

中序遍历:先访问左子树,再访问该节点,最后访问右子树

后序遍历:先访问左子树,再访问右子树,最后访问该节点

层次遍历:一层一层从左到右依次访问节点

先序遍历过程分析:

代码的具体实现:

cpp 复制代码
typedef int BTDataType;
//节点
typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

//先序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->_data);
	PreOrder(root->_left);
	PreOrder(root->_right);
}

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->_left);
	printf("%d ", root->_data);
	InOrder(root->_right);
}
//后序遍历
void AfOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	AfOrder(root->_left);
	AfOrder(root->_right);
	printf("%d ", root->_data);
}

函数调用过程;

补充题型:

给定先序遍历和中序遍历,构造出二叉树


二,节点的个数和高度

1,求二叉树的节点个数

利用递归将树拆分为两个子条件:

1,节点为空,0个(终止条件)

2 ,节点不为空,左树节点数+右树节点数+1

代码实现:

cpp 复制代码
//求树的节点个数--利用递归
int TreeSize(BTNode* root)
{
	return root == NULL ? 0 : TreeSize(root->_left) + TreeSize(root->_right) + 1;
}

2,求二叉树的叶子节点的个数

依旧利用递归进行拆分:

1,节点的左节点和右节点不为空:左节点+右节点

2,节点的左右节点都为空,1

代码实现:

cpp 复制代码
//求树的叶子节点--利用递归
int TreeleafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return root->_left == NULL && root->_right == NULL ? 
		1 : TreeleafSize(root->_left) + TreeleafSize(root->_right);
}

3,求二叉树的高度

利用递归进行拆分:

1,节点为空,0

2,节点不为空,左右子树中最高的树的高度+1

代码实现:

cpp 复制代码
//求树的高度--利用递归 
int TreeHight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int leftHight = TreeHight(root->_left);
	int rightHight = TreeHight(root->_right);
	return leftHight > rightHight ? leftHight + 1:rightHight + 1;

}

4,求二叉树第k层的节点个数

代码实现:

cpp 复制代码
//求第k层的节点个数
int TreelevelkSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return TreelevelkSize(root->_left, k - 1) + TreelevelkSize(root->_right, k - 1);
}

5,二叉树中查找值为x的节点

代码实现:

cpp 复制代码
//查找值为a的节点
//若要求的节点存在多个时,按照先序遍历的顺序取第一个
BTNode* TreekFind(BTNode* root, int a)
{
	if (root == NULL)
		return NULL;
	if (root->_data == a)
	{
		return root;
	}
	//提前记录,防止重复计算
	//先左后右--先序,在左子树中找到就不用在右子树中找
	BTNode* leftNode = TreekFind(root->_left, a);
	if (leftNode != NULL)   //如果找到了就返回--不为空就是找到了
		return leftNode;
	BTNode* rightNode = TreekFind(root->_right, a);
	if (rightNode != NULL)
		return rightNode;
	return NULL;     //两边都为空,没找到返回NULL
}

6,二叉树的销毁

对二叉树进行销毁就是对每个结点进行销毁,销毁时要先销毁节点的左右子树,再销毁根节点。如果先销毁根节点就会找不到结点的左右子树。

代码实现:

cpp 复制代码
//二叉树的销毁
void TreeDestroy(BTNode* root)
{
	//结点为空,不需要进行销毁,直接返回
	if (root == NULL)
		return;
	//先左后右最后根
	TreeDestroy(root->_left);
	TreeDestroy(root->_right);
	free(root);
	//不需要置空,因为置空的是局部变量的值,不会影响实参
	//想在函数内对一个指针置空,需要二级指针
}

7,二叉树的层次遍历

代码实现:

需要引入相关的队列创建和初始化函数

同时队列中的元素为二叉树结点的地址

队列头文件:

不能直接使用 typedef struct BTNode* QuNodeType;

不能对重命名的名字进行重命名,只能使用初始名字

二叉树源文件:

cpp 复制代码
//二叉树的层次遍历
void TreelevelOrder(BTNode* root)
{
	Que q1;
	//队列的创建
	QuInit(&q1);
	//判断空树
	if(root)
		QuPush(&q1, root);
	while (!QuEmpty(&q1))  //如果队列不为空就继续执行
	{
		//提前通过一个变量进行接收队头元素,防止删除队头元素后丢失
		BTNode* front = QuFront(&q1);
		printf("%d ", front->_data);
		QuPop(&q1);
		//传入队头元素的左右非空结点
		if(front->_left)
		    QuPush(&q1, front->_left);
		if (front->_right)
			QuPush(&q1, front->_right);
	}
	//队列的销毁
	QuDestroy(&q1);
}

8,判断二叉树是否为完全二叉树

代码实现:

cpp 复制代码
//判断二叉树是否为完全二叉树
bool TreeCompelet(BTNode* root)
{
	Que q1;
	QuInit(&q1);
	if (root)
		QuPush(&q1, root);
	while (!QuEmpty(&q1))  //如果队列不为空就继续执行
	{
		BTNode* front = QuFront(&q1);
		//遇到第一个空节点时就跳出循环
		if (front == NULL)
		{
			break;
		}
		QuPop(&q1);
		//空节点也传入队列中
		QuPush(&q1, front->_left);
		QuPush(&q1, front->_right);
	}
	//判断第二部分队列是否有非空元素
	while (!QuEmpty(&q1))
	{
		BTNode* front = QuFront(&q1);
		if (front)
		{
			QuDestroy(&q1);
			return false;
		}
		QuPop(&q1);
	}
	QuDestroy(&q1);
	return true;
}

三,例题:

1,单值二叉树

利用递归进行拆分:

代码实现:

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
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);
}

2,相同的树

相同问题可以拆分为该节点值相同和左右子树相同

另外,如果两个节点都为空时,也属于相等。

一个为空,另一个不为空,直接不相等。

两个都不为空,且值相同时,就递归左右子树

代码实现:

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool isSameTree(struct TreeNode* p, struct TreeNode* q) 
{
    if(p==NULL && q==NULL)  //两个都为空,说明相等
    {
        return true;
    }
    if(p == NULL || q == NULL) //其中一个为空就返回 false
    {
        return false;
    }
    if(p->val != q->val) //如果不是不相等,那么就是两个节点的值就是相等
    {
        return false;
    }
    //判断两个节点的左子树和右子树是否相同
    return isSameTree(p->left,q->left) 
    && isSameTree(p->right,q->right);
}

3,判断对称二叉树

和相同的树类似:判断根节点的左右子树是否对称,变成了左节点的左子树是否和右节点的右子树相等,左节点的右子树是否和右节点的左子树相等。

代码实现:

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
 //判断两个二叉树是否对称
 bool isSameTree(struct TreeNode* p, struct TreeNode* q) 
{
    if(p==NULL && q==NULL)  //两个都为空,说明相等
    {
        return true;
    }
    if(p == NULL || q == NULL) //其中一个为空就返回 false
    {
        return false;
    }
    if(p->val != q->val) //如果不是不相等,那么就是两个节点的值就是相等
    {
        return false;
    }
    //判断两个节点的左子树和右子树是否相同
    return isSameTree(p->left,q->right) 
    && isSameTree(p->right,q->left);
}
//判断是否为对称二叉树
bool isSymmetric(struct TreeNode* root) 
{
    return isSameTree(root->left,root->right);
}

4,二叉树的先序遍历

代码实现:

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
 //计算二叉树的节点个数
 int TreeSize(struct TreeNode* root)
 {
    return root==NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
 }
 //进行先序遍历,并把数字传入数组中
 void preorderTree(struct TreeNode* root,int* a,int* pi)
 {
    if(root==NULL)
    return;
    //传入地址才能在递归中实现一个整数的不断运算
    //否则运算在每次调用子函数,值都会重置,所以要从外边传入指针参数
    a[(*pi)++] = root->val;    //将有效节点数传入到数组当中 
    preorderTree(root->left, a, pi);
    preorderTree(root->right, a, pi);
 }
 //returnSize 属于输出型参数
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* a = (int*)malloc(*returnSize * sizeof(int));
    //i为数组的下标--函数递归部分
    int i = 0;   
    preorderTree(root,a,&i);
    return a;
}

输出型参数:

returnSize 属于输出型参数,由于函数的返回值只能有一个--数组的首元素地址,但是不知道数组中元素的个数,这时使用输出型参数的形式,传入一个变量的地址,通过在函数内进行改变,即使不返回值,这个变量的值也改变了,就达到了一个函数返回两个值的效果。

传入地址:int* pi

另外注意在函数中一般不使用局部变量进行运算,这些局部变量每次进入函数就会重置为初始值,不能达到随着二叉树结点的改变而改变的效果。这时需要传入指针才能达到效果。

5,另一棵树的子树

代码实现:

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
 bool isSameTree(struct TreeNode* p, struct TreeNode* q) 
{
    if(p==NULL && q==NULL)  //两个都为空,说明相等
    {
        return true;
    }
    if(p == NULL || q == NULL) //其中一个为空就返回 false
    {
        return false;
    }
    if(p->val != q->val) //如果不是不相等,那么就是两个节点的值就是相等
    {
        return false;
    }
    //判断两个节点的左子树和右子树是否相同
    return isSameTree(p->left,q->left) 
    && isSameTree(p->right,q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot) 
{
    if(root==NULL)
    {
        return false;
    }
    if(isSameTree(root, subRoot))
    {
        return true;
    }
    return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}

6,二叉树的遍历

先根据所给的字符串先序创造一个二叉树,再中序遍历这个二叉树。

代码实现:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

//创建结点
typedef struct TreeNode
{
    char val;
    struct TreeNode* left;
    struct TreeNode* right;
}BTNode;
//先序创造二叉树
BTNode* PreCreateTree(char* a,int* pi)
{
    //为空
    if(a[*pi] == '#')
    {
        (*pi)++;
        return NULL;
    }
    //开辟结点
    BTNode* root = (BTNode*)malloc(sizeof(BTNode));
    //按照先序的顺序进行创造二叉树
    root->val = a[(*pi)++];
    root->left = PreCreateTree(a,pi);
    root->right = PreCreateTree(a,pi);
    return root;
}
//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	InOrder(root->left);
	printf("%c ", root->val);
	InOrder(root->right);
}
int main()
{
    char a[100];
    scanf("%s",a);
    int i = 0;
    //先序创造二叉树
    BTNode* node = PreCreateTree(a,&i);
    //中序遍历
    InOrder(node);
}
相关推荐
澈2072 小时前
高效查找算法详解:从顺序到哈希
数据结构·算法·哈希算法
不知名的老吴3 小时前
案例教学:最长递增子序列问题
数据结构·算法·动态规划
_小草鱼_3 小时前
【数据结构】栈和队列
数据结构·数组··队列
贾斯汀玛尔斯3 小时前
每天学一个算法--图算法(Graph Algorithms)
数据结构·算法
网安INF3 小时前
数据结构第四章复习:树与二叉树
数据结构
我是无敌小恐龙4 小时前
Java SE 零基础入门 Day02 运算符与流程控制超详细笔记
java·数据结构·spring boot·笔记·python·spring·spring cloud
念越4 小时前
算法每日一题 Day04|快慢双指针法解决环形链表问题
数据结构·算法·链表
求学的小高4 小时前
数据结构Day6(普通树、森林与二叉树的关系、哈夫曼编码、并查集)
数据结构·笔记·考研
算法鑫探4 小时前
贪心算法(C 语言实现)及经典应用
c语言·数据结构·算法·贪心算法