大家好!这篇就讲一讲二叉树的遍历和一些相关操作,为什么不说二叉树的增删查改,因为二叉树的增删查改没有价值。我们要学会一些二叉树的操作,为以后学习打好基础。
文章目录
1. 二叉树的遍历
1.1 二叉树遍历的概念
二叉树遍历是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
1.2 前序/中序/后序的递归结构遍历
前序遍历(先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
访问顺序就是:根->左子树->右子树
中序遍历——访问根结点的操作发生在遍历其左右子树之中(间)。
访问顺序就是:左子树->根->右子树
后序遍历——访问根结点的操作发生在遍历其左右子树之后。
访问顺序就是:左子树->右子树->根
补充:还有一种遍历叫做层序遍历:从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
下面举个实例看一下:
我来解释一下:
前序遍历:首先访问根,也就是1,然后访问左子树:
访问左子树,首先访问的也是根,也就是2,然后再访问左子树:
首先访问的还是根,也就是3,然后访问左子树,是NULL,是NULL就返回,
左子树访问结束,访问右子树,右子树是NULL,返回。
所以到这里,图2中红色框的树就访问完了,也就是2的左子树访问完了,就开始访问2的右子树,是NULL,就返回。
这样,图1中红色框的树就访问完了,也就是1的左子树访问完了,然后我们开始访问1的右子树:
首先,访问的是根,也就是4,然后访问4的左子树:
然后首先访问根,也就是5,然后访问5的左子树,是NULL,是NULL就返回,左子树访问完了,访问5的右子树,是NULL就返回。
这样图4里蓝色框的树就访问完了,也就是4的左子树访问完了,然后访问4的右子树:
一样的,先访问6,然后6的左子树是NULL,是NULL就返回,左子树访问完了,访问右子树,是NULL就返回。
这样4的右子树也访问完了,到这里1的右子树也访问完了。所以前序遍历就结束了。
其它的大家可以根据这个思路试一试。下面我还用这个图来说一下它的代码实现。
1.2.1 用代码实现前序遍历
此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
先序遍历的代码如下:
//前序遍历
void PrevOrder(BTNode* root) {
if (root == NULL) {
printf("NULL ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
下面画图来看一下:
1.2.2 用代码实现中序遍历
中序遍历的代码如下:
//中序遍历
void InOrder(BTNode* root) {
if (root == NULL) {
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
下面画图来看一下:
1.2.3 用代码实现后序遍历
后序遍历的代码如下:
// 后序遍历
void NextOrder(BTNode* root) {
if (root == NULL) {
printf("NULL ");
return;
}
NextOrder(root->left);
NextOrder(root->right);
printf("%d ", root->data);
}
图解:
2. 二叉树的相关操作
我们还用上面的二叉树来举例:
2.1 求二叉树结点的个数
2.1.1 思路1:用局部变量count计数
这种方法是不行的,因为count是局部变量,每次递归count都会被置为0,所以结果只会是1。
2.1.2 思路2:静态的局部变量
这里就第一次可以,多次调用会有问题,没办法每次初始化为0。
2.1.3 思路3:用全局变量count计数
这样,在每次使用时,都给count置为0,就可以使用了。但这个会有线程安全的问题,这个以后linux就知道了。
2.1.4 思路4:遍历+计数
这样把count作为参数传进去,在遍历的时候增加,但每次也要自己置为0。
2.1.5 思路5:分治思想
什么是分治呢?分治就是:把复杂的问题,分成更小规模的子问题,子问题分成更小规模的子问题,直到子问题不能被分割,直接能出结果。
那么这道题,我们可以这样去思考:
1.空树,最小规模子问题,结点个数返回0。
2.非空,左子树结点个数+右子树结点个数+1(自己)。
首先,是1的结点,不是空树,所以我们要算1结点的左子树和右子树结点的个数,然后加上自己。
第二,我们算1的左子树结点,不是空树,我们要算2结点的左子树和右子树结点的个数,然后加上自己。
第三,2的左子树不是空树,我们要算3的左子树和右子树。然后加上自己,3的左子树和右子树都是空树,两边都返回0,加上自己就是1,所以2的左子树结点的个数为1。然后,我们要算2的右子树的结点的个数,是空树,返回0。
到这里,1的左子树就计算完了,为2个节点,然后我们要计算1的右子树。
1的右子树不是空树,所以我们要计算4的左子树和右子树,后面和前面的思路一样。
代码如下:
int BTreeSize(BTNode* root) {
return root == NULL ? 0 :BTreeSize(root->left)+ BTreeSize(root->right) + 1;
}
2.2 求二叉树叶子节点的个数
2.2.1 思路1:遍历+计数
这个和上面的思路是一样的,直接看代码:
void BTreeLeafSize(BTNode* root, int* pCount)
{
if (root == NULL)
return;
if (root->left == NULL && root->right == NULL)
++(*pCount);
BTreeLeafSize(root->left, pCount);
BTreeLeafSize(root->right, pCount);
}
2.2.2 思路2:分治
也是和上面的思路一样,分别统计左子树和右子树叶子节点的个数。当左子树和右子树都为空就返回。
int BTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}
2.3 求第k层的节点的个数,k >= 1
解题思路:
1.空树,返回0
2.非空,且k==1,返回1
3.非空,且k>1,转换成左子树第k-1层节点的个数+右子树k-1层节点的个数
假设,我们要求这棵二叉树的第3层节点的个数。就可以转换成左子树第二层节点的个数+右子树第二层节点的个数(A的第三层就相当于它子树的第二层)
B,C的第二层又可以转换成它们子树的第一层。当k==1,我们就可以返回1了。
代码如下:
int BTreeKLevelSize(BTNode* root, int k)
{
assert(k >= 1);
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BTreeKLevelSize(root->left, k - 1)
+ BTreeKLevelSize(root->right, k - 1);
}
图解如下:
2.4 求二叉树的深度
解题思路:左子树高度和右子树高度比较,大的那个+1
代码如下:
int BTreeDepth(BTNode* root)
{
if (root == NULL)
return 0;
int leftDepth = BTreeDepth(root->left);
int rightDepth = BTreeDepth(root->right);
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
图解如下:
2.5 二叉树查找值为x的结点
解题思路:
假设,我们要在下面的二叉树中找H
首先,我们要在根找,根不是,我们要在它左子树找。
如果在左子树找到了,就不用在右子树找。如果没在左子树找,就去右子树找。那我们该如何找。
先找B,不是,然后去B的左子树找,D不是,然后去D的左子树找,D的左子树为空,返回一个NULL,然后去D的右子树找,D的右子树为空,返回一个NULL。所以,到这里B的左子树就没找到,D就会返回NULL给B,然后去B的右子树去找。
后面的思路一样。如果找到了就返回。
代码如下:
BTNode* BTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* ret1 = BTreeFind(root->left, x);
if (ret1)
return ret1;
BTNode* ret2 = BTreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
}
到这里,一些二叉树基本的操作就差不多,后面我们说一些OJ题来看一下。如果大家觉得有帮助,希望能够多多支持!谢谢大家!