题目:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
示例 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 。
因为根据定义最近公共祖先节点可以为节点本身。
示例 3:
输入:root = [1,2], p = 1, q = 2 输出:1
提示:
- 树中节点数目在范围
[2, 105]
内。 -109 <= Node.val <= 109
- 所有
Node.val
互不相同
。 p != q
p
和q
均存在于给定的二叉树中。
对示例的理解
各种情况
解题代码
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return null;//根节点为空,不存在公共祖先,也就不存在最近公共祖先x
}
if(root == p || root == q){
return root;//根节点为p,q中的一个,由题意可知,此时的root节点就为最近公共祖先x 即x为p,q的一个节点,为图中最右边的情况
}
/*接下来root不等于p和q,但是root的左树找到一个,root的右树找到一个,p和q分布在root的左右,
此时的root节点就为最近公共祖先x, 即x为当前的根节点 为图中最左边的情况*/
TreeNode leftRet = lowestCommonAncestor(root.left,p,q);//在root的左树找
TreeNode rightRet= lowestCommonAncestor(root.right,p,q); //在root的右树找
if(leftRet != null && rightRet != null){
return root;//为图中最左边的情况
}else if(leftRet != null){//p 和 q 均存在于给定的二叉树中。
return leftRet;//为图中中间的情况
}else if(rightRet != null){
return rightRet;//为图中中间的情况
}
//代码走到这步,说明左右都是null,左树没有找到p和q,右树也没有找到p和q,返回null
return null;//不过根据题中的提示 p 和 q 均存在于给定的二叉树中这句话,说明代码不可能走到这步,写这句代码更多是从java的语法规范出发,不写会出现汇编异常
}
}
运行结果
但是我们可以用到栈(stack)这个数据结构来实现这个思想,把x到p的节点放到stack1,把x到q的节点放到stack2.
按照求相交链表的节点来做,栈的长度长的先弹出长度差个节点,再两个栈依次同时弹出一个节点,
stack1和stack2弹出的节点相同时,即该节点就是本题所求的最近公共祖先节点X
难点:怎样把从根节点到指定节点路径上所有的节点找到并放到栈里面了
换句话说是把其他无关节点在递归回退的时候弹出去
思路:采用前序遍历对每个节点进行判断,在找到节点p和节点q的过程中,先将节点压入栈中(压栈),如果该节点的左右节点都为null,都返回false,说明该节点不是x到节点p或者节点q路径上的节点,为其他无关节点,将该节点弹出栈,如果该节点就是节点p或者节点q,递归开始回退.
注:找节点p的时候用stack1存储,找节点q的时候用stack2存储,需要用到两个栈面
代码很好的用到了链表的特性,并与之前的知识进行了结合,编程思想很厉害,很牛叉!!!
代码
class Solution{
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//1.创建两个栈,用来存储数据
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
//2.stack1和stack2开始存储x到节点p和x到节点q路径上的节点
getPath(root, p, stack1);
getPath(root, q, stack2);
//3.判断栈的大小,并进行相关的出栈处理
int lenStack1 = stack1.size();
int lenStack2 = stack2.size();
if (lenStack1 > lenStack2) {
int len = lenStack1 - lenStack2;
while (len != 0) {
stack1.pop();
len--;
}
} else {
int len = lenStack2 - lenStack1;
while (len != 0) {
stack2.pop();
len--;
}
}
//此时stack1和stack2的数据个数,也就是节点个数相同(本质上存放的是节点的地址,所以可以用==或者!=进行判断)
//开始查找最近的公共祖先节点x
while (!stack1.isEmpty() && !stack2.isEmpty()) {
if (stack1.peek() != stack2.peek()) {
stack1.pop();
stack2.pop();
} else {
return stack1.peek();//找到了最近的公共祖先节点x
}
}
//此时,栈都出栈完了,为空了,还没找到,返回null
return null;
}
public static boolean getPath(TreeNode root, TreeNode findNode, Stack<TreeNode> stack) {
if (root == null || findNode == null) {
return false;
}
stack.push(root);//放入根节点
//放完之后,检查,判断当前节点是不是要查找的节点p或者q
if (root == findNode) {
return true;//找到了,返回true,递归开始回退
}
boolean retLeft = getPath(root.left, findNode, stack);//root的左边查找findNode节点
if (retLeft == true) {
return true;
}
boolean retRight = getPath(root.right, findNode, stack);//root的左边没找到,去root的右边查找findNode节点
if (retRight == true) {
return true;
}
//如果代码执行到这,说明该节点不是findNode,左边和右边也没有,即该节点不是x到节点p或者节点q路径上的节点,为其他无关节点,将该节点弹出栈
stack.pop();//该节点被弹出栈
return false;
}
}
运行结果
完结撒花✿✿ヽ(°▽°)ノ✿✿
题目链接
https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/submissions/