十道题带你手撕二叉树

Posted 鹿九丸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十道题带你手撕二叉树相关的知识,希望对你有一定的参考价值。

十道题带你手撕二叉树

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;

以上是关于十道题带你手撕二叉树的主要内容,如果未能解决你的问题,请参考以下文章

风哥带你手撕算法之吃透二叉树

手撕二叉树

手撕二叉树的4种遍历:前序中序后序层序

手撕二叉树的4种遍历:前序中序后序层序

还不知道层序遍历有多强?带你一口气打穿十道题(动图理解)

图解 | 4道题带你初步了解浏览器的EventLoop