草庐IT

十道题带你手撕二叉树

鹿九丸 2023-06-07 原文

十道题带你手撕二叉树

1. 单值二叉树

题目:

思路一:(遍历的方法

将根节点的值与二叉树中的每一个节点存储的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. 相同的树

题目:

代码:

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

题目:

代码:

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. 二叉树的前序遍历

题目:

代码:

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. 二叉树的中序遍历

题目:

代码:

 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. 二叉树的后序遍历

题目:

代码:

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. 另一棵树的子树

题目:

代码:

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. 二叉树的遍历

题目:

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

#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. 翻转二叉树

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

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

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

代码:

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

有关十道题带你手撕二叉树的更多相关文章

  1. ruby - 在 Ruby 中实现二叉树 - 2

    我一直在尝试在Ruby中实现BinaryTree类,但我得到了stackleveltoodeep错误,尽管我似乎没有在该特定代码段中使用任何递归:1.classBinaryTree2.includeEnumerable3.4.attr_accessor:value5.6.definitialize(value=nil)7.@value=value8.@left=BinaryTree.new#stackleveltoodeephere9.@right=BinaryTree.new#andhere10.end11.12.defempty?13.(self.value==nil)?true:

  2. 【JavaScript】手撕前端面试题:对象参数浅拷贝 | 简易深拷贝 | 完整深拷贝 - 2

    🖥️NodeJS专栏:Node.js从入门到精通🖥️博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述)🖥️TypeScript知识总结:TypeScript从入门到精通(十万字超详细知识点总结)🧑‍💼个人简介:大三学生,一个不甘平庸的平凡人🍬👉你的一键三连是我更新的最大动力❤️!文章目录1、浅拷贝要求思路代码2、简易深拷贝要求思路代码3、完整深拷贝要求思路代码1、浅拷贝要求补全JavaScript代码,要求实现一个对象参数的浅拷贝并返回拷贝之后的新对象。注意:参数可能包含函数、正则、日期、ES6新对象是对对象的参数进行浅拷贝,并不是直接对整个对象进行浅拷贝(整个

  3. 【C语言进阶】还说不会?一文带你全面掌握计算机预处理操作 - 2

    目录🍊前言🍊:🍈一、宏与函数🍈:        1.宏与函数对比:    2.宏与函数的命名约定:🍓二、预处理操作符🍓:    1.预处理操作符"#":    2.预处理操作符"##":🥝三、条件编译🥝:    1.简述条件编译指令:    2.常见条件编译指令:🍒总结🍒:🛰️博客主页:✈️銮同学的干货分享基地🛰️欢迎关注:👍点赞🙌收藏✍️留言🛰️系列专栏:💐【进阶】C语言学习            🧧  C语言学习🛰️代码仓库:🎉VS2022_C语言仓库    家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!    

  4. 一文带你通俗理解23种软件设计模式(推荐收藏,适合小白学习,附带C++例程完整源码) - 2

    作者:翟天保Steven版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处一、设计模式是什么?    设计模式是为了解决在软件开发过程中遇到的某些问题而形成的思想。同一场景有多种设计模式可以应用,不同的模式有各自的优缺点,开发者可以基于自身需求选择合适的设计模式,去解决相应的工程难题。    良好的软件设计和架构,可以让代码具备良好的可读性、可维护性、可扩展性、可复用性,让整个系统具备较强的鲁棒性和性能,减少屎山代码出现的概率。    想要熟练运用设计模式,提高自己的编程能力和架构能力,只有在自己工作中,结合自身工作内容,多思考多实践。本文只能通过举一些通俗的例子,来

  5. javascript - 与二维碰撞有关的四叉树 - 2

    我一直在研究这个:https://github.com/mikechambers/ExamplesByMesh/blob/master/JavaScript/QuadTree/src/QuadTree.js我相信我理解四叉树的一般概念,尽管我对它们的工作原理和上面的实现有两个问题:难道你不需要每隔几毫秒重建整个树吗?在Javascript中,这不会非常慢吗?如果我有这样的东西:http://davzy.com/screenshots/skitched-20120318-180324.png,那么很容易找到同一个四边形中的其他点,但我有一个矩形与3个不同的四边形相交,有没有办法让它显示为

  6. Linux - 一篇带你读懂 Curl Proxy 代理模式 - 2

    curl是一个很有名的处理网络请求的类Unix工具。出于某种原因,我们进行网络请求,需要设置代理。本文讲全面介绍如何为curl设置代理设置代理参数基本用法-x,--proxy[protocol://]host[:port]设置HTTP代理下面两种设置代理的方式是可以的curl-x"http://user:pwd@127.0.0.1:1234""http://httpbin.org/ip"curl--proxy"http://user:pwd@127.0.0.1:1234""http://httpbin.org/ip"由于代理地址的默认协议为 HTTP,所以可以省略,按照下面的形式也是可以的cu

  7. 数据结构之线索二叉树详细解释 - 2

    1.1线索二叉树的原理我们现在倡导节约型社会,一切都应该以节约为本。但当我们创建二叉树时我们会发现其中一共有两个指针域,有的指针域指向的结构为空,这也就浪费了很多空间。所以为了不去浪费这些空间我们采取了一个措施。就是利用那些空地址,存放指向结点在某种遍历次序之下的前驱和后继结点的地址。就好像GPS导航仪一样,它可以告诉我们下一站是哪里,我们是从那里来的。我们把这种指向前驱和后继的指针成为线索,加上线索的二叉链表称为线索链表,相应的二叉树就成为线索二叉树。我们将对二叉树以某种次序遍历使其变为线索二叉树的过程称为线索化。下图是线索化结束的图:这里存在一个问题,我们怎么知道某一个结点的lchild是

  8. 【树】你真的会二叉树了嘛? --二叉树LeetCode专题Ⅳ - 2

     Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法......感兴趣就关注我吧!你定不会失望。🌈个人主页:主页链接🌈算法专栏:专栏链接     我会一直往里填充内容哒!🌈LeetCode专栏:专栏链接     目前在刷初级算法的LeetBook。若每日一题当中有力所能及的题目,也会当天做完发出🌈代码仓库:Gitee链接🌈点击关注=收获更多优质内容🌈目录题目:111. 二叉树的最小深度题解:代码实现:题目:700. 二叉搜索树中的搜索题解:代码实现:题目:701. 二叉搜索树中的插入操作题解:代码实现:题目:450. 删除二叉搜索树中的节点题解:代码实现:完结撒花:人生苦短,

  9. 【数据结构与算法】一套链表 OJ 带你轻松玩转链表 - 2

    ✨个人主页:bitme✨当前专栏:数据结构✨刷题专栏:基础算法链表OJ🏳️一.移除链表元素🏴二.反转链表🏁三.链表的中间结点🚩四.链表中倒数第k个结点🏳️‍🌈五.合并两个有序链表🏳️‍⚧️六.链表的回文结构🏴‍☠️七.链表分割🏴󠁧󠁢󠁷󠁬󠁳󠁿八.相交链表🏳️‍🌈九.环形链表🍹十.环形链表II 🏳️一.移除链表元素简介:给你一个链表的头节点head和一个整数val,请你删除链表中所有满足Node.val==val的节点,并返回新的头节点。示例1:输入:head=[1,2,6,3,4,5,6],val=6输出:[1,2,3,4,5]示例2:输入:head=[],val=1输出:[]示例3:输入:he

  10. algorithm - 为什么这个二叉树搜索比插入花费的时间长得多? - 2

    我正在尝试学习/理解一些基本算法,今天我决定用Go编写一个二叉树。这是结构的样子:typeNodestruct{ValueintLeft*NodeRight*Node}这是我检查树是否包含int的函数:func(tree*Node)Contains(valint)bool{ifval==tree.Value{returntrue}elseifval>tree.Value{iftree.Right!=nil{returntree.Right.Contains(val)}else{returnfalse}}elseifval我写了一个测试函数来测试对树的不同操作需要多长时间。我的Inser

随机推荐