二分木の最も近い共通の祖先
問題の説明
バイナリ ツリーが与えられた場合、ツリー内の指定された 2 つのノードに最も近い共通の祖先を見つけます。
Baidu Encyclopedia における最も近い共通祖先の定義は次のとおりです。
ルート付きツリー T 内の 2 つのノード p、q について、最も近い共通の祖先はノード x として表され、x が p、q の祖先であり、x の深さができるだけ大きいことが満たされます (ノードはそれ自身の祖先です)。
例
原題 OJリンク
https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/submissions/
応答コード
方法 1
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
//如果是一颗空树,那么也就没有什么祖先结点的说法了
return null;
}
if(root == p || root == q){
//p,q其中一个是根节点,那么祖先就是根节点
//这一行代码也是用来递归寻找p,q结点的 3
return root;
}
//往左子树去找p,q结点
TreeNode leftRet = lowestCommonAncestor(root.left,p,q);
//往右子树去找p,q结点
TreeNode rightRet = lowestCommonAncestor(root.right,p,q);
if(leftRet != null && rightRet != null){
return root;//说明pq分布分布在root的左右两边,那么他们的公共结点一定是root结点 1
}
else if(leftRet != null){
return leftRet;//说明pq都分布在根节点左边,那么一直往下找,无论pq谁先被找到谁就是祖先结点 2
}
else if(rightRet != null){
return rightRet;//说明pq都分布在根节点右边边,那么一直往下找,无论pq谁先被找到谁就是祖先结点
}
//最后一种情况,就是qp根本没有公共祖先
return null;
}
}
以下の図は上記コード1の状況です。
以下の図は上記コード2の状況です。
以下の図は上記コード3の状況です。
方法 2
さらに、ルート ノードから q ノードと p ノードへのパスを最初に見つけてリンク リストに保存し、次に 2 つのリンク リストの共通ノードを見つける問題になります。
しかし、このアイデアにはパスのルートノードからあるノードまでのパスをどうやって見つけるかという解決すべき問題があり、その方法としては、オンかどうかわからないノードをスタックを使ってプッシュするという方法がとられています。パスを探索し、そのサブツリーをたどります。ターゲット ノードが見つからない場合は、ノードがパス上にないことを確認してから、再度ポップアップします。実際には深さ優先トラバーサルであり、
具体的なコードは次のとおりです。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
import java.util.Deque;
import java.util.LinkedList;
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
Deque<TreeNode> stack1 = new LinkedList<>();
getPath(root,p,stack1);
Deque<TreeNode> stack2 = new LinkedList<>();
getPath(root,q,stack2);
//判断栈的大小
int length1 = stack1.size();
int length2 = stack2.size();
int length = 0;
if(length1>length2){
length = length1 - length2;
while(length != 0){
stack1.pop();
length--;
}
}
else{
length = length2 - length1;
while(length != 0){
stack2.pop();
length--;
}
}
//栈元素个数都一样了
while(!stack1.isEmpty() && !stack2.isEmpty()){
if(stack1.peek() != stack2.peek()){
stack1.pop();
stack2.pop();
}
else{
return stack1.peek();
}
}
return null;
}
public Boolean getPath(TreeNode root,TreeNode node,Deque<TreeNode> stack){
if(root == null || node == null){
return false;
}
stack.push(root);//根节点肯定在路径里面,第一个压入栈中,
if(root == node){
return true;
}
boolean ret1 = getPath(root.left,node,stack);//之后先把寻找过程中的结点都先压入栈中
if(ret1 == true){
return true;//往左边找找到了
}
boolean ret2 = getPath(root.right,node,stack);
if(ret2 == true){
return true;//往右边找找到了
}
//往左和往右都没找到,说明这个结点不在路径上,所以把这个点给弹出
stack.pop();
return false;
}
}