题目
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。
代码与思路
package lowest_common_ancestor_of_a_binary_tree;
import java.util.ArrayList;
import java.util.List;
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
/**
* 1.求出较短路径的长房n.
* 2.同时遍历p节点的路径与q节点的路径,遍历n个节点,最后一个发现的相同节点,即最近公共祖先.
*/
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> path = new ArrayList<TreeNode>();//声明遍历用的临时栈
List<TreeNode> node_p_path = new ArrayList<TreeNode>();//存储p节点路径
List<TreeNode> node_q_path = new ArrayList<TreeNode>();//存储q节点路径
int finish = 0;//记录是否完成搜索的变量finish
preorder(root, p, path, node_p_path, finish);
path = new ArrayList<TreeNode>();
finish = 0;
preorder(root, q, path, node_q_path, finish);
int path_len = 0;//较短路径的长度
if(node_p_path.size()<node_q_path.size()) {
path_len = node_p_path.size();
}else {
path_len = node_q_path.size();
}
TreeNode result = new TreeNode(0);
for (int i = 0; i < path_len; i++) {//同时遍历根到p,q两个节点的路径上的节点
if(node_p_path.get(i)==node_q_path.get(i)) {
result = node_p_path.get(i);
}
}
return result;
}
//求至某节点路径
private void preorder(TreeNode node,//正在遍历的节点
TreeNode search, //待搜索节点
List<TreeNode> path, //遍历时的节点路径栈
List<TreeNode> result, //最终搜索到节点search的路径结果
int finish) {//记录是否找到节点search的变量,未找到时是0,找到为1
if (node == null || finish == 1) {//当node为空或已找到search节点直接返回,结束搜索
return;
}
path.add(node);//先序遍历,将节点压入path栈
if (node.val == search.val) {//当找到search节点后,标记finish变量
finish = 1;
result.addAll(path);//将当前的path存储到result中
}
preorder(node.left, search, path, result, finish);//深度遍历node左孩子
preorder(node.right, search, path, result, finish);//深度遍历node右孩子
path.remove(path.size() - 1);//结束遍历node时,将node节点弹出path栈
}
public static void main(String[] args) {
TreeNode a = new TreeNode(3);
TreeNode b = new TreeNode(5);
TreeNode c = new TreeNode(1);
TreeNode d = new TreeNode(6);
TreeNode e = new TreeNode(2);
TreeNode f = new TreeNode(0);
TreeNode x = new TreeNode(8);
TreeNode y = new TreeNode(7);
TreeNode z = new TreeNode(4);
a.left = b;
a.right = c;
b.left = d;
b.right = e;
c.left = f;
c.right = x;
e.left = y;
e.right = z;
Solution solve = new Solution();
TreeNode result = solve.lowestCommonAncestor(a, b, f);
System.out.printf("lowestCommonAncestor = %d\n", result.val);
result = solve.lowestCommonAncestor(a, d, z);
System.out.printf("lowestCommonAncestor = %d\n", result.val);
result = solve.lowestCommonAncestor(a, b, y);
System.out.printf("lowestCommonAncestor = %d\n", result.val);
}
}
执行用时 :13 ms, 在所有 java 提交中击败了38.93%的用户
内存消耗 :36.3 MB, 在所有 java 提交中击败了75.34%的用户
前序遍历,中序遍历,后序遍历
前序遍历,中序遍历,后序遍历,指的不是一次,两次遍历。它指的是,我在深度优先搜索的时候,前序的时候,就是还没有访问左子树,右子树的时候,我看到这个节点做什么事。中序代表的是这个,在访问完它的左子树之后,做了什么事。后序代表的是,所以都完成了,在回归,回退到上一个节点的时候,我们做了什么,这个叫后序。