十道题带你手撕二叉树

这篇博客详细介绍了多种二叉树相关的算法,包括单值二叉树、相同树的判断、对称二叉树的检测、二叉树的前序、中序和后序遍历、二叉树子树的查找以及二叉树的销毁。通过递归思想,展示了如何高效地解决这些二叉树问题,并提供了清晰的代码实现。此外,还讨论了二叉树销毁时需采用后序遍历的原因。
摘要由CSDN通过智能技术生成

点击蓝字

2e352373ec02f71a3ead6f807caebe03.png

关注我们

1、单值二叉树

题目:

e3f6b1f8fe9f6fe29a73b113725de22a.png

思路一:(遍历的方法)

将根节点的值与二叉树中的每一个节点存储的val值进行比较,如果不同就返回false,如果全部相同,就返回true。

代码:

bool _isUnivalTree(struct TreeNode*root,int num)//辅助函数
{
if(root == NULL)//只有一个节点或者递归调用到叶子节点的字节点时
return true;
else if(root->val == num)//当前根节点的值与存储的初始根节点的num相同的时候,此时就不需要返回true,因为当前根节点存储的值与初始节点存储的值相同并不代表后续节点的值也相同
    {
return _isUnivalTree(root->left,num) && _isUnivalTree(root->right,num);
    }
else//此情况就是root->val!=num的时候
return false;
}
bool isUnivalTree(struct TreeNode* root)
{
return _isUnivalTree(root,root->val);//调用辅助函数
}

思路二:

判断当前根节点的值是否与其左右子节点的值相等,如果不相等就返回false,同样,如果递归调用到了节点为空节点时就返回true。

代码:

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

问:为什么只需要判断根节点和左右子节点是否一样就可以了?

答:注意这个-传递性,如果根节点和其左右子节点分别相同了,那么在递归的时候,之前的左右子节点就会变成根节点,此时就是在保持前面一样的前提下来判断新的根节点和新的根节点的左右子节点是否相同,优点类似链表,就是说链表的第一和第二个节点是相同的之后,然后判断第二和第三个节点是否是相同的,如果第二和第三个节点是相同的之后,那么第一和第三个节点也必然是相同的,依此类推,当这样的每个比较都成立之后,就说明整个二叉树就是等值二叉树。

当然,还有一种不是很好的写法,这种写法虽然也能通过,和上面的思路来说本质上并没有太大的区别(甚至定义的那个全局变量还显得有些多余),并不推荐这种写法,因为这种写法定义了一个全局变量,在OJ题中最好不要定义全局变量和静态变量,因为后台程序可能会多次调用这个程序,flag中可能还存储着上一次的结果,在下一次调用时容易出问题。

代码如下:

bool flag = true;//默认最开始就是单值二叉树
bool isUnivalTree(struct TreeNode* root){
if(root==NULL)
    {
        flag = true;
return flag;
    }


if(root->left && root->val!=root->left->val)
    {
        flag = false;
return flag;
    }
if(root ->right &&root->val!=root->right->val )
    {
        flag = false;
return flag;
    }
return isUnivalTree(root->left)&&isUnivalTree(root->right);
}

2、相同的树

题目:

f36213726c0b4c8d50a1b8eb74e8c6de.png

代码:

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、对称二叉树

题目:

8d97239822e77ce34f6189a63d680fc8.png

代码:

bool isSymmetricTree(struct TreeNode* p,struct TreeNode* q)//辅助函数
{
//两个节点均为空的情况
if(p==NULL && q==NULL)
    {
return true;
    }


//两个节点有一个不为空的情况
if(p == NULL || q == NULL)
    {
return false;
    }


//两个节点是否相等的情况并对两个节点进行递归判断,注意镜像相反
return p->val == q->val && isSymmetricTree(p->left,q->right)&&isSymmetricTree(p->right,q->left);
}


bool isSymmetric(struct TreeNode* root){
//根节点为空的情况
if(root == NULL)
    {
return true;
    }


//根节点不为空的情况
return isSymmetricTree(root->left,root->right);
}

4、二叉树的前序遍历

题目:

48f9721ba55e51f7532ad7c83f314839.png

代码:

int BTreeSize(struct TreeNode* root)//计算二叉树元素的数目
{
return root == NULL ? 0 : 1 + BTreeSize(root->left) + BTreeSize(root->right);
}


void _preorder(struct TreeNode* root,int *a,int *i)//辅助函数
{
if(root == NULL)//空的时候直接返回
    {
return;
    }
//1.先遍历根节点
    a[*i] = root->val;//将有值得存储到开辟得数组空间中
    (*i)++;//数组下标进行自增操作
//2.遍历左子树
    _preorder(root->left,a,i);//对左子树进行操作
//3.遍历右子树
    _preorder(root->right,a,i);//对右子树进行操作
}


int* preorderTraversal(struct TreeNode* root, int* returnSize){
int count = BTreeSize(root);//计算题目所给二叉树元素得数目
int *a = malloc(sizeof(int)*count);//开辟数组存储二叉树元素
    assert(a);//防止malloc开辟失败
    *returnSize = count;//存储数组元素得数目
int i = 0;//当作数组下标来存储二叉树元素
    _preorder(root,a,&i);
return a;
}

5、二叉树的中序遍历

题目:

41c2e6b1b9960540aa00ce03aa47eee7.png

代码:

int BTreeSize(struct TreeNode* root)//计算二叉树元素的数目
{
return root == NULL ? 0 : 1 + BTreeSize(root->left) + BTreeSize(root->right);
}


void _inorder(struct TreeNode* root,int *a,int *i)//辅助函数
{
if(root == NULL)//空的时候直接返回
    {
return;
    }
//1. 遍历左子树
    _inorder(root->left,a,i);//对左子树进行操作
//2.遍历根节点
    a[*i] = root->val;//将有值得存储到开辟得数组空间中
    (*i)++;//数组下标进行自增操作
//3.遍历右子树
    _inorder(root->right,a,i);//对右子树进行操作


}


int* inorderTraversal(struct TreeNode* root, int* returnSize){
int count = BTreeSize(root);//计算题目所给二叉树元素得数目
int *a = malloc(sizeof(int)*count);//开辟数组存储二叉树元素
    assert(a);//防止malloc开辟失败
    *returnSize = count;//存储数组元素得数目
int i = 0;//当作数组下标来存储二叉树元素
    _inorder(root,a,&i);
return a;
}

6、二叉树的后序遍历

2d0696403785762b880c7504f447854d.png

代码

int BTreeSize(struct TreeNode* root)//计算二叉树元素的数目
{
return root == NULL ? 0 : 1 + BTreeSize(root->left) + BTreeSize(root->right);
}


void _postorder(struct TreeNode* root,int *a,int *i)//辅助函数
{
if(root == NULL)//空的时候直接返回
    {
return;
    }
//1. 遍历左子树
    _postorder(root->left,a,i);//对左子树进行操作
//2.遍历右子树
    _postorder(root->right,a,i);//对右子树进行操作
//3.遍历根节点
    a[*i] = root->val;//将有值得存储到开辟得数组空间中
    (*i)++;//数组下标进行自增操作
}


int* postorderTraversal(struct TreeNode* root, int* returnSize){
int count = BTreeSize(root);//计算题目所给二叉树元素得数目
int *a = malloc(sizeof(int)*count);//开辟数组存储二叉树元素
    assert(a);//防止malloc开辟失败
    *returnSize = count;//存储数组元素得数目
int i = 0;//当作数组下标来存储二叉树元素
    _postorder(root,a,&i);
return a;
}

7、另一棵树的子树

3786b2e95e9bce7886224964146abf93.png

代码:

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);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root == NULL)
return false;
return isSameTree(root,subRoot) || isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}

8、二叉树的遍历

题目

6afd8e46741249b5e8023feca381ffdf.png

代码:(思路仍然是利用递归的思想)

#include<stdio.h>
#include<stdlib.h>
typedef struct BTreeBode
{
char data;
struct BTreeNode*left;
struct BTreeNode*right;
}BTNode;


BTNode* CreateTree(char *a,int *pi)
{
if(a[*pi]== '#')
    {
        (*pi)++;
return NULL;
    }
    BTNode* root = malloc(sizeof(BTNode));//创建节点
    root->data = a[(*pi)++];


    root->left = CreateTree(a,pi);
    root->right = CreateTree(a,pi);


return root;
}
void InOrder(BTNode* root)
{
if (root == NULL)
  {
return;
  }
  InOrder(root->left);
printf("%c ", root->data);
  InOrder(root->right);
}
int main()
{
char str[100];
scanf("%s",str);
int i = 0;
    BTNode* tree = CreateTree(str,&i);
    InOrder(tree);
return 0;
}

9、翻转二叉树

思路:是利用递归的思想,就是将每个二叉树看成是左子树 + 根节点 + 右子树
左子树又可以看成是:左子树 = 左子树 + 根节点 + 右子树
右子树又可以看成是:右子树 = 左子树 + 根节点 + 右子树

然后将左右子树分别互换即可,从最小的子树开始思考:

175014b283941453a56fbbaf59a530e3.png

上面的子树中,2有两个子节点3和1,所以将3节点和1节点进行互换即可,然后对于整个二叉树来说,也都是将左子树和右子树进行互换即可。

edc4b88212c7767cbb1db36a2cc7076f.png

代码

struct TreeNode* invertTree(struct TreeNode* root){
if(root == NULL)//空指针时返回
    {
return NULL;
    }
struct TreeNode* left = invertTree(root->left);//左子树
struct TreeNode* right = invertTree(root->right);//右子树
    root->left = right;
    root->right = left;
return root;
}

10、二叉树的销毁

注意:二叉树的销毁只能采取后序遍历的方式进行销毁,因为一旦根节点被销毁后,就无法找到子节点的地址了。

void BinaryTreeDestory(BTNode** root)
{
if ((*root) == NULL)//判断是否为空,如果为空直接返回
  {
return;
  }
//此处采用后序遍历的方法,因为根必须留在最后销毁
  BinaryTreeDestory(&((*root)->left));
  BinaryTreeDestory(&((*root)->right));
free(*root);
  *root = NULL;
}

*声明:本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

a03a0244b8a43e7c2f5d4c9f3e81ddd4.png

166aeeba85a39f818791dfd5943fa821.gif

戳“阅读原文”我们一起进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值