二叉树LeetCode题目总结C++

二叉树相关(深度、是否相同、是否对称、翻转、合并、是否平衡的递归和非递归方法)


二叉树的遍历非常重要,主要包括前中后序遍历的递归和非递归写法,可以见我写的另外一篇博客 二叉树的遍历。关于二叉树本身还有一些非常重要的难度不太高的题目,我选取了leetcode上的一些题目,做一个总结。
关于二叉树的问题,我们通常可以考虑递归和非递归的两种思路,递归难在找到递归的逻辑,但是编程比较简单,非递归主要利用不同的数据结构解决问题,常用的比如队列、栈等等。
备注:本文使用LeetCode定义的二叉树的节点的数据结构

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(NULL), right(NULL) {}
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 };

1.二叉树的深度

利用递归的思路,一个二叉树的深度实际是其左、右子树的深度的最大值+1;递归终止条件就是根节点为空时,深度为0;有了这个思路,代码就很容易写出来了。

int maxDepth(TreeNode* root){
    if(!root)return 0;
    return max(maxDepth(root->left),maxDepth(root->right))+1;//需要#include <algorithm>
}

如果不利用递归的话,可以考虑二叉树层序遍历的思路,见链接二叉树的遍历中的第3点的层序遍历输出二维数组的思路,层序遍历的次数就是二叉树的深度了。

int maxDepth(TreeNode* root){
    int ans=0;
    if(!root)return ans;
    queue<TreeNode*>q;
    q.push(root);
    int size=1;//表示每层的叶子节点的个数
    while(!q.empty()){
        ans++;//每遍历一次,二叉树的层数+1
        int size=q.size();
        for(int i=0;i<size;i++){
            if(q.front()->left)q.push(q.front()->left);
            if(q.front()->right)q.push(q.front()->right);
            q.pop();
        }
    }
    return ans;
}

2.判断两棵二叉树是否相同

LeetCode100.相同的树
若是用递归,思路就是两棵树根节点相同且左右子树都相同才是相同的,终止条件要考虑根节点为空的情况,若函数输入的两个节点均为空,返回真;一个为空另一个不为空,返回假,代码如下:

bool isSameTree(TreeNode* p, TreeNode* q) {
    if(p&&!q)return false;//一个为空一个不为空,不相同
    if(!p&&q)return false;//同上
    if(!p&&!q)return true;//两个均为空,相同
    return (p->val==q->val) && isSameTree(p->left,q->left) && isSameTree(p->right,q->right);//根节点相同且左右子树相同,则两个二叉树是相同的
    }

非递归的思路依旧可以参照二叉树的层序遍历,利用两个队列,模拟层序遍历,每次循环的队首元素都要相同,注意需要多考虑一边节点为空另一边节点不空的情况。
注意:其实有空间上更优的方法,用一个队列即可,代码附在这种方法的后面。

bool isSameTree(TreeNode* p, TreeNode* q) {
    queue<TreeNode*>pp;
    queue<TreeNode*>qq;
    if(!q&&!p)return true;//均为空树,返回真
    if(q&&!p)return false;//一个为空树,一个不为空树,返回假
    if(!q&&p)return false;//一个为空树,一个不为空树,返回假
    qq.push(q);
    pp.push(p);
    while(!qq.empty() && !pp.empty()){
        bool qqleft=qq.front()->left==NULL;
        bool qqright=qq.front()->right==NULL;
        bool ppleft=pp.front()->left==NULL;
        bool ppright=pp.front()->right==NULL;
        if(qqleft^ppleft)return false;//若两队首元素的左节点一个为空一个非空,返回假
        if(qqright^ppright)return false;//若两队首元素的右节点一个为空一个非空,返回假
        if(qq.front()->val!=pp.front()->val)return false;
        if(qq.front()->left)qq.push(qq.front()->left);
        if(qq.front()->right)qq.push(qq.front()->right);
        qq.pop();
        if(pp.front()->left)pp.push(pp.front()->left);
        if(pp.front()->right)pp.push(pp.front()->right);
        pp.pop();      
    }
    return true;
}

更优的方法只需要用一个队列即可,每次将两个二叉树的对应位置的节点依次入队,对比后再依次出队。

bool isSameTree(TreeNode *u, TreeNode *v) {
        queue <TreeNode*> q;
        q.push(u); 
        q.push(v);
        while (!q.empty()) {
            u = q.front(); 
            q.pop();
            v = q.front(); 
            q.pop();
            if (!u && !v) continue;//当两个二叉树的节点均为空时,跳过当前的循环
            if ((!u || !v) || (u->val != v->val)) return false;//当二叉树对应位置一个树为空节点另一个树不是空节点时,当对应位置的元素不相等时,都不相同,返回假。
            q.push(u->left); 
            q.push(v->left);
            q.push(u->right); 
            q.push(v->right);
        }
        return true;
    }

3.判断二叉树是否镜像对称

LeetCode101.对称二叉树
递归的思路和上一题的判断两棵树是否相同非常类似,判断二叉树是否镜像对称就是判断左子树和右子树是否对称,与上一题在代码上的区别只是参数是左、右子树。

bool isSymmetric_help(TreeNode* p,TreeNode* q) {
    if(p&&!q)return false;
    if(!p&&q)return false;
    if(!p&&!q)return true;
    return (p->val==q->val) && isSymmetric_help(p->left,q->right) && isSymmetric_help(p->right,q->left);
}
bool isSymmetric(TreeNode* root) {
    if(!root)return false;
    return isSymmetric_help(root->left,root->right);
}

非递归的思路依旧可以参照二叉树的层序遍历,依照判断二叉树是否相同的第三种思路,代码逻辑非常类似,交换一下节点的入栈的顺序,即可。

bool isSymmetric_help(TreeNode *u, TreeNode *v) {
        queue <TreeNode*> q;
        q.push(u); q.push(v);
        while (!q.empty()) {
            u = q.front();
            q.pop();
            v = q.front();
            q.pop();
            if (!u && !v) continue;
            if ((!u || !v) || (u->val != v->val)) return false;
            q.push(u->left); 
            q.push(v->right);
            q.push(u->right); 
            q.push(v->left);
        }
        return true;
    }
bool isSymmetric(TreeNode* root) {
    return isSymmetric_help(root, root);
}

4.翻转二叉树

LeetCode226.翻转二叉树
直接用递归即可,每次左右节点交换。

TreeNode* invertTree(TreeNode* root) {
    if(root==NULL)return NULL;
    TreeNode* left = invertTree(root->left);
    TreeNode* right = invertTree(root->right);
    root->left = right;
    root->right = left;
    return root;
}

5.合并二叉树

LeetCode617.合并二叉树
和上一题很像,利用递归的方法。

TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (!t1) return t2;
        if (!t2 ) return t1;
        TreeNode* merged = new TreeNode(t1->val + t2->val);
        merged->left = mergeTrees(t1->left, t2->left);
        merged->right = mergeTrees(t1->right, t2->right);
        return merged;
    }

6.判断二叉树是否为平衡二叉树

LeetCode110.平衡二叉树
平衡二叉树的定义是,一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
关于二叉树的高度计算,借助题目1的递归最为方便。

int depth(TreeNode* root) {
    if (root == NULL) return 0;
    return max(depth(root->left), depth(root->right)) + 1;   
 }
 bool isBalanced(TreeNode* root) {
        if (root == NULL) return true;
        return abs(depth(root->left) - depth(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }

这个方法,因此对于同一个节点,求二叉树深度的函数depth会被重复调用,导致时间复杂度较高。如果使用自底向上的做法,可以做到每次depth函数只被调用一次。
自底向上递归的做法类似于后序遍历,对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 −1-1−1。如果存在一棵子树不平衡,则整个二叉树一定不平衡。

int height(TreeNode* root) {
    if (root == NULL)return 0;
    int leftHeight = height(root->left);
    int rightHeight = height(root->right);
    if (leftHeight == -1 || rightHeight == -1 || abs(leftHeight - rightHeight) > 1) return -1;
    return max(leftHeight, rightHeight) + 1;       
}
bool isBalanced(TreeNode* root) {
    return height(root) >= 0;
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值