关于树与递归关系的剖析

一提到树与递归,我想大家印象应该真的是很深了!从二叉树的遍历开始,我们就知道递归在树中占据的重要地位。因为树这个数据结构有很强的规律性。特别是二叉树。所以在做树相关的题型或者能通过树数据结构表示出来的数据结构联想到递归或者迭代是最基本的思想,而递归也能使代码更加的简洁。

  • 遍历

第一次感受到遍历魅力就是在学习树的遍历时,看原理的时候感觉虽然简单但泄气来应该够麻烦的,可谁想知道答案让人大跌眼镜。也就区区几行而已。如下就是前序遍历,而中序和后序遍历也就是改变一下输出语句与递归函数的位置而已。

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

//前序遍历
void PreOrderTraverse(TreeNode* root)
{
    if(root == nullptr)
        return;
    cout << root->val << endl;
    PreOrderTraverse(root->left);
    PreOrderTraverse(root->right);
}
  • 题型

下面我们来对几道题目进行解析,题目来源是leetcode。

路径总和

在最开始看到题目的时候,我想大家都一样肯定是是从前往后加嘛的确是一个很好的方法嘛,每加一个结点我们都可以,对其与sum进行判断,代码如下:

class Solution {
public:
         bool hasPathSum(TreeNode* root,  int sum)
         {
             
             return help(root, 0, sum);
         }
       bool help(TreeNode* root, int val, int sum) //用来记录从根节点开始累加的值
        {
            //static int val = 0; //用来记录从根节点开始累加的值
            if(root == NULL)
                return false;
            val = val + root->val;
            if(root->left == NULL && root->right == NULL)
                return val == sum;
            else
                return help(root->left,val, sum) || help(root->right,val, sum);
        }
};

其实这题也算是一个很好的逆向思维题,改变一下思路,既然是最后判断累加和与sum的大小对比,那么也可以从sum入手,也就是说每经过一个结点就将sum 减一嘛,最后到叶子结点在再判断sum是不是为0。

class Solution {
public:
         bool hasPathSum(TreeNode* root, int sum)
         {
             if(root == NULL)
                return false;
            sum = sum - root->val;
            if(root->left == NULL && root->right == NULL)
                return sum == 0;
            else
                return hasPathSum(root->left,sum) || hasPathSum(root->right, sum);
         }
};

对称二叉树

同样,你看这种题型,也是有递归和迭代的,虽然题目比较简单,但是值得好好体会。我们先来看看递归是如何解决问题的,和前面其实是参不多的,主要是对于对称的考虑问题的,我们来找找规律,如果两个树要对称,你什么首先根节点的值要相等;其次,要对称嘛,判断 A 的右子树与 B 的左子树是否对称,判断 A 的左子树与 B 的右子树是否对称,如果都对称那么问题也就得到解决了。

bool isSymmetric(TreeNode* root) {
        if(root == NULL)
            return true;
        return helper(root->left, root->right);
    }
    
    bool helper(TreeNode* root1, TreeNode* root2)
    {
        if(root1 == NULL && root2 == NULL)
            return true;
        if(root1 == NULL || root2 == NULL)
            return false;
        
        return root1->val == root2->val && helper(root1->left, root2->right) && helper(root1->right, root2->left);  
    }

我们来看看迭代,迭代的话,主要用到的是广度优先搜索,说到这个,我想大家应该都不会觉得很难,可是呀,应用到这里就是比较巧妙了(以下参考leetcode 题101 官方题解)。

使用队列。队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像。最初,队列中包含的是 root->left 以及 root->right。该算法的工作原理类似于 BFS,但存在一些关键差异。每次提取两个结点并比较它们的值。然后,将两个结点的左右子结点按相反的顺序插入队列中。当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续结点)时,该算法结束。

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        
        queue<TreeNode*> Q; 
        if(root == NULL)
            return true;
        if(root->left == NULL && root->right == NULL)
            return true;
        if(root->left == NULL || root->right == NULL)
            return false;
        Q.push(root->left);
        Q.push(root->right);
        
        while(!Q.empty())
        {
            TreeNode* t1 = Q.front();
            Q.pop();
            TreeNode* t2 = Q.front();
            Q.pop();
            
            if(t1 == NULL && t2 == NULL)
                continue;
            if(t1 == NULL || t2 == NULL)
                return false;
            if(t1->val != t2->val)
                return false;
            Q.push(t1->left);
            Q.push(t2->right);
            Q.push(t1->right);
            Q.push(t2->left);
        }
        return true;
    }
};

相同的树

这题思路基本和第一道题一模一样。

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(p == NULL && q == NULL) return true;
        if(p == NULL || q == NULL) return false;
        return p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
};

总结

我们看了三道题,不知道大家有没有发现规律,我觉得很有规律可循。

首先,题目的要求都是验证某个东西,true 或 false,所以再使用递归的时候明显可以看到答案具有高度相似的架构,在ruturn语句中,都存在 && 或者 || 等运算符。也就是说一般通过 递归1 || 或 && 递归2 来实现分支的递归。可以发现递归函数就可以很简单那的设计啦。

 

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值