题意理解:
二叉树的最近公共祖先: 简单理解,就是p和q值的那两个节点,不断向上返回,然后会在一个点汇合,那么他们第一次汇合的这个点就是他们的最近公共祖先。
解题的思路就是:
如果这一层找到了p或q的值,就向上一层返回。
如果父节点的左右分别找了p、q节点,则返回父节点
如果父节点占p、q中的一个,然后子树中找到一个,则返回父节点
否则返回null
解题方法:
其实就是树的话,一般都可以用递归和迭代两种方法的,就是这道题,用迭代比较难理解一点。
1.递归
先判断子树是否找到p、q值,再判断中间节点是否需要返回,所以处理顺序为: 左子树、右子树、中间节点——后续遍历
对于这道题来说,递归的思路看起来更为明确。
/*** 什么是最近公共祖先?* 两个值节点合并到一棵树的那个祖先节点* 题目限制:树中没有重复值,p,q一定存在,这使得题目简单了不少* 思路:找到一个p|q,则向上返回,当遇到某个节点,左子树,右子树返回都不为null时,* 说明该节点是最近祖先,向上返回,* 最终返回最近祖先* @param root* @param p* @param q* @return*/public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {//由于题目限制,root一定不为null//由于需要对左右子树进行判断,然后对中间节点进行处理,故使用后序遍历:左右中//左处理TreeNode leftResult=root.left!=null?lowestCommonAncestor(root.left,p,q):null;//右处理TreeNode rightResult=root.right!=null?lowestCommonAncestor(root.right,p,q):null;//中间处理if(root.val==p.val||root.val==q.val) return root;//左右结果均不为空,则说明左右子树各找到一个目标值// 因为pq一定存在,p!=q,且树中没有重复值,故上述结论一定成立if(Objects.nonNull(leftResult)&&Objects.nonNull(rightResult)) return root;//左右找到一个,即该子树仅存在p|q中一个,继续向上返回else if(Objects.nonNull(leftResult)&&Objects.isNull(rightResult)) return leftResult;else if(Objects.isNull(leftResult)&&Objects.nonNull(rightResult)) return rightResult;//左右都没有找到,则说明该子树没有目标值,返回nullelse return null;/*** //中间处理逻辑精简* if(root.val==p.val||root.val==q.val||(Objects.nonNull(leftResult)&&Objects.nonNull(rightResult))) return root;* //左右找到一个,即该子树仅存在p|q中一个,继续向上返回* else if(Objects.nonNull(leftResult)&&Objects.isNull(rightResult)) return leftResult;* else if(Objects.isNull(leftResult)&&Objects.nonNull(rightResult)) return rightResult;* //左右都没有找到,则说明该子树没有目标值,返回null* else return null;*/}
2.迭代
不建议用迭代,理解太复杂,除非有要求。
这里需要两个栈,一个栈用来维护遍历树的操作。
另一个栈用来描述找到的节点在树种的位置关系,用来向上返回找到的值,且可以用来判断找到的两个节点是否在此处汇合。
eg: null null null 3 9 null 即可表示要找的值在同一层,一个null表示即将出现一个中间节点,两值汇合后的下一个null出现时对应的中间节点,即为汇合点,即最近公共祖先。
//迭代public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {//自定义栈-模拟递归Stack<TreeNode> stack=new Stack<>();//暂存找到的值,并指示层级Stack<TreeNode> resultStack=new Stack<>();stack.push(root);while(!stack.isEmpty()){TreeNode cur=stack.pop();if (Objects.nonNull(cur)) {//节点入栈stack.push(cur);stack.push(null);resultStack.push(null);//每加一个null相当于一层,用于只是层级//保证后序遍历的顺序入栈if(Objects.nonNull(cur.right)) stack.push(cur.right);if(Objects.nonNull(cur.left)) stack.push(cur.left);}else{//碰到中间节点cur=stack.pop();//该层有找到的值加入结果暂存层if(cur.val==p.val||cur.val==q.val){resultStack.push(cur);}TreeNode node=resultStack.pop();if(node!=null){//该层有找到的值if(resultStack.peek()!=null) return cur;//该层找到两个值else {resultStack.pop();//该层找到一个值,上移resultStack.push(node);}}}}return null;}
3.分析
时间复杂度:
递归:O(n)
迭代:O(n)
空间复杂度:
递归:O(n)
迭代:O(2n)