递归与二叉树

众所周知,二叉树可谓是递归中最典型的例子了,可谓是如影随形。凡是有关二叉树的问题,基本都可以用递归来解决,这也给了我们一个启示:看到二叉树,首先就要想到递归。

经过一番的刷题,渐渐总结出了有关二叉树的一些套路,也就是递归模板。一次只考虑一个点,没个点把自己要做的做好而不要关其他的点(我们一般首先考虑根节点)。

F(TreeNode root){
  //先序遍历(先对root操作)
  F(TreeNode root->left);
  //中序遍历(在中间对root操作)
  F(TreeNode root->right);
  //后序遍历(在最后对root操作)
}

可以说所有的二叉树递归都逃不出这个模板,只不过中间的处理过程及函数简繁不一,但框架总是对的。

先举几个简单的例子:
1.leetcode 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if(t1==null&&t2==null)return null;
        if(t1==null)return t2;
        if(t2==null)return t1;
        //下面的代码相当于二叉树的先序遍历
        //而且我们请注意返回值为TreeNode型的函数,
        //我们对其孩子遍历时,要用相对应的孩子节点去接受返回的值
        TreeNode root=new TreeNode(t1.val+t2.val);
        root.left=mergeTrees(t1.left,t2.left);
        root.right=mergeTrees(t1.right,t2.right);
        return root;
    }
}

2.leetcode二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {      
    //递归,结束判断是根节点为两者之一或者为空,
    //如果在左子树能找到右子树找不到去左子树找,
    //否则去右子树,如果都能找到那么返回结果。
    //因为我们的递归先考虑一个最小单元,所以我们总是先考虑
    //root根节点,所以此时要考虑的是p、q其中之一在root中的情况,
    //因为这种情况直接就是目前要处理的情况,不能交给子问题,
    //而p、q在其左右孩子的情况我们交给子问题(也就是通过递归)来处理
        if(!root||p==root||q==root)return root;
        TreeNode *l=lowestCommonAncestor(root->left,p,q);
        TreeNode *r=lowestCommonAncestor(root->right,p,q);
        //如果左右节点都没有,返回NULL
        if(!l&&!r)return NULL;
        //如果左节点没有,返回右节点(往右节点找)
        else if(!l)return r;
        //如果右节点没有,返回左节点(往左节点找)
        else if(!r)return l;
        //如果左右都有(也就是p、q分布在其左右孩子节点,则返回根节点)
        return root;
    }
};

3.leetcode判断二叉查找树的合法性 Validate Binary Search Tree

题目:Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:
The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than the node’s key.
Both the left and right subtrees must also be binary search trees.

先说一下思路,因为这道题很容易出错,这也是我想说的一个二叉树递归需要注意的点:按照之前说过的思路,每个节点⾃⼰要做的事不就是⽐较 ⾃⼰和左右孩⼦吗?看起来应该这样写代码:

boolean	isValidBST(TreeNode	root)	{	
if	(root	==	null)	return	true;				
if	(root.left	!=	null	&&	root.val	<=	root.left.val)	
return	false;				
if	(root.right	!=	null	&&	root.val	>=	root.right.val)	
return	fals e;
return	isValidBST(root.left)&&	isValidBST(root.right); 
}

然而得到的结果却是错误的。因为在这段代码中,我们对于每个节点只考虑了其左右孩子结点与其本身的大小关系,而没有考虑到整个左右子树上每一个节点的值得大小。

此时只凭借一个参数的函数递归已经不足以解决问题了,那么就要增加一些必要的辅助参数:(当我们要求解的问题不单单是之和一个节点的孩子节点有关联而是和整个子树有关的时候,也就是如果当前节点会对下⾯的⼦节点有整体影响,可以通过辅助函数增⻓参 数列表,借助参数传递信息,须用到多个参数)。

class Solution {
public:
    bool isValidBST(TreeNode *root) {
        return isValid(root, INT_MIN, INT_MAX);
    }
    
    bool isValid(TreeNode *root, int low, int high)
    {
        if(root == NULL)
            return true;
            //最后的返回就很能说明问题,我们不仅要求对某个节点本身要满足bst的定义,
            //其左右子树都要满足bst的定义
        return root->val > low && root->val < high 
               && isValid(root->left, low, root->val)
               && isValid(root->right, root->val, high);
    }
};


还有一种思路就是可以采用中序遍历来做(因为BST中序遍历的升序的),此时我们可以只用一个pre来保存前一个节点就可以。

```cpp

或者采用另一种比较巧妙的方法






class Solution {
public:
    int* last = NULL;
    bool isValidBST(TreeNode* root) {
        if (root){
            if(!isValidBST(root->left)) return false;
            if (last && *last>=root->val) return false;
            last = &root->val;
            if(!isValidBST(root->right)) return false;
            return true;
        }else return true;
    };
};

这里我总结在二叉树的递归操作中,必须保证一次支队一个节点进行操作,
如果涉及不同节点间的比较,则通过迭代的方法更新某参数,而不能牵扯
别的节点,比如这里就是通过last指针不断更新来和不同的节点迭代比较,
而在我们之前的错误写法中,则是一次操作了3个节点(root/root->left/root->right)
故出错,或者我们可以通过携带多个参数来满足一次性对多个参数的比较。



class Solution {
public:
    bool isValidBST(TreeNode *root) {
        int pre = INT_MIN; //最初的pre设为一个特殊值,为了不和节点值冲突
        return fun(root, pre); 
    }
    ///也可以将pre设为全局变量
    bool fun(TreeNode *root, int &pre) //只需要一个int来保存中序遍历序列的上一个数
    {
        if(root != NULL)
        {
            bool left = fun(root->left, pre);
            
            if(pre != INT_MIN  && pre >= root->val) //判断新增的值是否是满足增序
                    return false;
            pre = root->val; //更新序列最后一个数
            
            bool right = fun(root->right, pre);
            
            return left && right;  //返回左右子树的综合状态
        }
        else
            return true; //到达空树直接返回true
    }
};

leetcode110. 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root==null)return true;
        return Math.abs(depth(root.left)-depth(root.right))<2
        &&isBalanced(root.left)&&isBalanced(root.right)?true:false;
        //这里注意必须添加其左右子树叶都是高度平衡树的条件,
        //因为这是自顶向下求解,所以先检查的是最顶层的树,
        也就是根节点,如果根节点满足条件,
        //那么该函数就会直接返回,而不会往下检查。
    }
    public int depth(TreeNode root){
        if(root==null)return 0;
        return Math.max(depth(root.left),depth(root.right))+1; 
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值