【算法22天:Day22】第六章二叉树 LeetCode 二叉搜索树的最近公共祖先(235)

55 阅读2分钟

题目一:

image.png

解法一:(递归)

解题思路:只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。理解这一点,本题就很好解了。

二叉树:公共祖先问题 (opens new window)不同,普通二叉树求最近公共祖先需要使用回溯,从底向上来查找,二叉搜索树就不用了,因为搜索树有序(相当于自带方向),那么只要从上向下遍历就可以了。

那么我们可以采用前序遍历(其实这里没有中节点的处理逻辑,遍历顺序无所谓了)。

递归三部曲

  • 确定递归函数返回值以及参数

参数就是当前节点,以及两个结点 p、q。返回值是要返回最近公共祖先。

var lowestCommonAncestor = function(root, p, q) {}
  • 确定终止条件

遇到空返回就可以了,代码如下:

if (cur == NULL) return cur;

其实都不需要这个终止条件,因为题目中说了p、q 为不同节点且均存在于给定的二叉搜索树中。也就是说一定会找到公共祖先的,所以并不存在遇到空的情况。

  • 确定单层递归的逻辑

在遍历二叉搜索树的时候就是寻找区间[p.val, q.val](注意这里是左闭又闭)

那么如果 cur.val 大于 p.val,同时 cur.val 大于q.val,那么就应该向左遍历(说明目标区间在左子树上)。

if(root.val>p.val&&root.val>q.val) {
    // 向左子树查询
    let left = lowestCommonAncestor(root.left,p,q);
    return left !== null&&left;
}

需要注意的是此时不知道p和q谁大,所以两个都要判断

在这里调用递归函数的地方,把递归函数的返回值left,直接return

二叉树:公共祖先问题 (opens new window)中,如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树。

搜索一条边的写法:

if (递归函数(root.left)) return ;
if (递归函数(root.right)) return ;

搜索整个树写法:

left = 递归函数(root.left);
right = 递归函数(root.right);
left与right的逻辑处理;

本题就是标准的搜索一条边的写法,遇到递归函数的返回值,如果不为空,立刻返回。

如果 cur.val 小于 p.val,同时 cur.val 小于 q.val,那么就应该向右遍历(目标区间在右子树)。

if(root.val<p.val&&root.val<q.val) {
    // 向右子树查询
    let right = lowestCommonAncestor(root.right,p,q);
    return right !== null&&right;
}

剩下的情况,就是cur节点在区间(p.val <= cur.val && cur.val <= q.val)或者 (q.val <= cur.val && cur.val <= p.val)中,那么cur就是最近公共祖先了,直接返回cur。

return root;

整体代码:

var lowestCommonAncestor = function(root, p, q) {
    // 使用递归的方法
    // 1. 使用给定的递归函数lowestCommonAncestor
    // 2. 确定递归终止条件
    if(root === null) {
        return root;
    }
    if(root.val>p.val&&root.val>q.val) {
        // 向左子树查询
        let left = lowestCommonAncestor(root.left,p,q);
        return left !== null&&left;
    }
    if(root.val<p.val&&root.val<q.val) {
        // 向右子树查询
        let right = lowestCommonAncestor(root.right,p,q);
        return right !== null&&right;
    }
    return root;
};

解法二:(迭代法)

var lowestCommonAncestor = function(root, p, q) {
    // 使用迭代的方法
    while(root) {
        if(root.val>p.val&&root.val>q.val) {
            root = root.left;
        }else if(root.val<p.val&&root.val<q.val) {
            root = root.right;
        }else {
            return root;
        }
        
    }
    return null;
};