题目一:
解法一:(递归)
解题思路:只要从上到下遍历的时候,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;
};