Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”
_______3______ / \ ___5__ ___1__ / \ / \ 6 _2 0 8 / \ 7 4
For example, the lowest common ancestor (LCA) of nodes 5
and 1
is 3
. Another example is LCA of nodes 5
and 4
is 5
, since a node can be a descendant of itself according to the LCA definition.
Subscribe to see which companies asked this question
Step1:首先用先序遍历的方法分别找到根结点到两个给定结点的路径;
Step2:那么两条路径中最后一个相同的结点就是两个给定结点的最低公共祖先。
方法一:一次遍历二叉树得到两条路径。
优点:只需一次遍历二叉树;
缺点:不好控制遍历结束的条件,容易混淆。
ArrayList<TreeNode> pathToP = new ArrayList<TreeNode>();// 根结点到p结点的路径 ArrayList<TreeNode> pathToQ = new ArrayList<TreeNode>();// 根结点到Q结点的路径 int num = 0;// 表示目前找到的路径数。 /** * 求一颗二叉树的最低公共祖先。 * Step1:首先分别找到根结点到两个给定结点的路径; * Step2:那么两条路径中最后一个相同的结点就是两个给定结点的最低公共祖先。 * 提交通过。 */ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root == null || p == null || q == null) { return null; } ArrayList<TreeNode> path = new ArrayList<TreeNode>();// 辅助存储 /* 调用方法找根结点到两个结点的路径 */ findPath(root, p, q, path); /* 找两个路径链表中最后一个相同的结点 */ int len = Math.min(pathToP.size(), pathToQ.size()); int i = 0; while (i < len && pathToP.get(i) == pathToQ.get(i)) { i++; } return pathToP.get(i - 1); } /** * 先序遍历找到根结点到两个目标结点的路径。 * 形参ArrayList<TreeNode> path 提交之后总说这个是堆栈溢出, * 但是编程List<TreeNode> path 之后就提交过了。 */ boolean findPath(TreeNode root, TreeNode p, TreeNode q, List<TreeNode> path) { path.add(root); /* 判断是否找到了根结点到p结点的路径 */ if (root == p) { pathToP.addAll(path); num++; } /* 判断是否找到了根结点到Q结点的路径 */ if (root == q) { pathToQ.addAll(path); num++; } if (num == 2) {//说明根结点到两个目标结点的路径都找到了。 return true; } if (root.left != null) { if(findPath(root.left, p, q, path)){ return true; } } if (root.right != null) { if(findPath(root.right, p, q, path)){ return true; } } /* 返回其调用函数之前,先从当前路径中删除当前结点 */ path.remove(path.size() - 1); return false; }
优点:思路清晰,不易混淆;
缺点:需要两次遍历二叉树。
ArrayList<TreeNode> pathToP = new ArrayList<TreeNode>();// 根结点到p结点的路径 ArrayList<TreeNode> pathToQ = new ArrayList<TreeNode>();// 根结点到Q结点的路径 int num = 0;// 表示目前找到的路径数。 /** * Step1:首先分别找到根结点到两个给定结点的路径; * Step2:那么两条路径中最后一个相同的结点就是两个给定结点的最低公共祖先。 * @param root * @param p * @param q * @return */ public TreeNode lowestCommonAncestor3(TreeNode root, TreeNode p, TreeNode q) { if (root == null || p == null || q == null) { return null; } ArrayList<TreeNode> pathp = new ArrayList<TreeNode>();// 辅助存储 ArrayList<TreeNode> pathq = new ArrayList<TreeNode>();// 辅助存储 /* 调用方法找根结点到两个结点的路径 */ getPath(root, p, pathp); getPath(root, q, pathq); /* 找两个路径链表中最后一个相同的结点 */ /* 找两个路径链表中最后一个相同的结点 */ int len = Math.min(pathp.size(), pathq.size()); int i = 0; while (i < len && pathp.get(i) == pathq.get(i)) { i++; } return pathp.get(i - 1); } /** * 找从根结点到某个结点的路径,找到这个根结点则返回。 * 注意方法的形参是List则可以通过,换成ArrayList则不通过、 */ private boolean getPath(TreeNode root, TreeNode n, List<TreeNode> path) { path.add(root); if(root==n) { //11 return true; } if(root.left!=null) { if(getPath(root.left, n, path)) return true; } if(root.right!=null) { if(getPath(root.right, n, path)) return true; } path.remove(path.size()-1); return false; }
需要注意的是在递归找路径的时候,用List不溢出,为什么用ArrayList会溢出呢????