题目
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
数据范围:
3<=节点总数<=10000
0<=节点值<=10000
如果给定以下搜索二叉树: {7,1,12,0,4,11,14,#,#,3,5},如下图:
示例1
输入:{7,1,12,0,4,11,14,#,#,3,5},1,12
复制返回值:7
说明:节点1和节点12的最近公共祖先是7
示例2
输入:{7,1,12,0,4,11,14,#,#,3,5},12,11
返回值:12
说明:因为一个节点也可以是它自己的祖先.所以输出12
思路1:搜素路径比较
二叉搜索树是一种特殊的二叉树,它的每个节点值大于它的左子节点,且大于全部左子树的节点值,小于它右子节点,且小于全部右子树的节点值。因此二叉搜索树一定程度上算是一种排序结构。
二叉搜索树没有相同值的节点,因此分别从根节点往下利用二叉搜索树较大的数在右子树,较小的数在左子树,可以轻松找到p、q:
//节点值都不同,可以直接用值比较
while(node.val != target) {
path.add(node.val);
//小的在左子树
if(target < node.val)
node = node.left;
//大的在右子树
else
node = node.right;
}
直接得到从根节点到两个目标节点的路径,这样利用路径比较就可以找到最近公共祖先。
具体做法:
代码
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
//求得根节点到目标节点的路径
public ArrayList getPath(TreeNode root, int target) {
ArrayList path = new ArrayList();
TreeNode node = root;
//节点值都不同,可以直接用值比较
while (node.val != target) {
path.add(node.val);
//小的在左子树
if (target < node.val) {
node = node.left;
} else { //大的在右子树
node = node.right;
}
}
path.add(node.val);
return path;
}
public int lowestCommonAncestor(TreeNode root, int p, int q) {
//求根节点到两个节点的路径
ArrayList path_p = getPath(root, p);
ArrayList path_q = getPath(root, q);
int res = 0;
//比较两个路径,找到第一个不同的点
for (int i = 0; i < path_p.size() && i < path_q.size(); i++) {
int x = path_p.get(i);
int y = path_q.get(i);
//最后一个相同的节点就是最近公共祖先
if (x == y) {
res = path_p.get(i);
} else {
break;
}
}
return res;
}
}
思路2:一次遍历
递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能将原本的问题分解为更小的子问题,这是使用递归的关键。
而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。
可以利用二叉搜索树的性质:对于某一个节点,
若是p与q都小于等于这个节点值,说明p、q都在这个节点的左子树,而最近的公共祖先也一定在这个节点的左子树;
若是p与q都大于等于这个节点值,说明p、q都在这个节点的右子树,而最近的公共祖先也一定在这个节点的右子树;
若是对于某个节点,p与q的值一个大于等于该节点值,一个小于等于该节点值,说明p、q分布在该节点的两边,而这个节点就是最近的公共祖先。
因此从上到下的其他祖先都将这个两个节点放到同一子树,只有最近公共祖先会将它们放入不同的子树,每次进入一个子树又回到刚刚的问题,因此可以使用递归。
具体做法:
代码
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param root TreeNode类
* @param p int整型
* @param q int整型
* @return int整型
*/
public int lowestCommonAncestor(TreeNode root, int p, int q) {
//空树找不到公共祖先
if (root == null) {
return -1;
}
//p、q在该节点两边说明这就是最近公共祖先
if ((p >= root.val && q <= root.val) || (p <= root.val && q >= root.val)) {
return root.val;
} else if (p <= root.val && q <= root.val) { //p、q都在该节点的左边
//进入左子树
return lowestCommonAncestor(root.left, p, q);
} else { //p、q都在该节点的右边
//进入右子树
return lowestCommonAncestor(root.right, p, q);
}
}
}