标题:Java实现:最近公共祖先节点
建议可以直接看这个:
Java得到二叉树的最近公共祖先节点
一、方法一 ,很容易想到,但不是最优解
思想:找到两个满足条件的节点,得到他们的祖先节点
1).使用非递归的后序遍历实现【因为先序遍历,直接删节点就行了,不需要后序遍历那么的麻烦,中序也同样】
2)eg:栈中为:1-> 2 -> 3 那么说此节点数组中存放的为3,2,1因为 这样才是最近的公共祖先节点【注意:要包含自身】
3)最后通过两个for循环,得到TreeNode
/*
* 使用后序遍历,得到两个链表
*
* 执行用时:44 ms, 在所有 Java 提交中击败了5.00% 的用户
内存消耗:40.7 MB, 在所有 Java 提交中击败了77.18% 的用户
*/
public TreeNode common02(TreeNode head, TreeNode p1, TreeNode p2) {
System.out.println("\n迭代后序遍历");
if(head == null) {
return null;
}
Deque<TreeNode> s = new LinkedList<>();
s.push(head);
//创建两个ArrayList
ArrayList<TreeNode> list1 = new ArrayList<>();
ArrayList<TreeNode> list2 = new ArrayList<>();
if(head.val == p1.val) {
list1.add(head);
}else if(head.val == p2.val) {
list2.add(head);
}
while(!s.isEmpty()) {
TreeNode p = s.peek();
if(p.left != null) {//遍历节点的左边
s.push(p.left); //细心,不要写成node.left
//对list进行赋值,若都赋值了,则说明得到所有的父节点
if(p.left.val == p1.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list1.add(it.next());
}
}else if(p.left.val == p2.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list2.add(it.next());
}
}
if(list1.size() != 0 && list2.size() != 0) {
break;
}
}else {
while(p.right == null && !s.isEmpty()) {//为了给里面的while中break,使得可以跳出外围的break
TreeNode last = s.pop();
System.out.print(last.val + " ");
if(s.isEmpty()) {
break;
}
p = s.peek();
while(p.right == last) {
last = s.pop();
System.out.print(last.val + " ");
if(s.isEmpty()) {
break;
}
p = s.peek();
}
}
if(s.isEmpty()) {//栈为null 和之前的不同,因为,后序是判断右边是否为null,再输出node,
break;
}else {//p.right != null
s.push(p.right);
//对list进行赋值,若都赋值了,则说明得到所有的父节点
if(p.right.val == p1.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list1.add(it.next());
}
}else if(p.right.val == p2.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list2.add(it.next());
}
}
if(list1.size() != 0 && list2.size() != 0) {
break;
}
}
}
}
System.out.println(list1.toString());
System.out.println(list2.toString());
for(int i = 0; i < list1.size(); i++) {
for(int j = 0; j < list2.size(); j++) {
if(list1.get(i).val == list2.get(j).val) {
return list1.get(i); //注意不是生成新的节点,否则这个节点没有意义,只是做题 important
}
}
}
return null;
}
二、方法二 挺节省空间的
思想:使用前序遍历实现,满足条件的解可分为两种
1)找到第一个满足条件的节点,剩下一个满足条件的在此节点的左子树中,return 第一个满足条件的节点
2)找到第一个满足条件的节点,剩下一个满足条件的在此节点的右子树,或者此节点之上 使用prev,代表返回的节点
【此节点,向前退一层,再次查找右子树,直至找到为止】
/**
* 方法二 迭代 使用前序遍历,
* 执行用时:10 ms, 在所有 Java 提交中击败了23.52% 的用户
内存消耗:39.5 MB, 在所有 Java 提交中击败了98.92% 的用户
* @param head
* @param p1
* @param p2
* @return
*/
public TreeNode common(TreeNode head, TreeNode p1, TreeNode p2) {
if(head == null) {
return null;
}
Deque<TreeNode> s = new LinkedList<>();
s.push(head);
// System.out.print(head.val + " ");
TreeNode node = null;
TreeNode prev = null;
if(head.val == p1.val || head.val == p2.val) {
node = head;
prev = node;
}
//开始前序遍历
while(!s.isEmpty()) {
TreeNode p = s.peek();
if(p.left != null) {
s.push(p.left);
// System.out.print(p.left.val + " ");
if(prev == null) {
if(p.left.val == p1.val || p.left.val == p2.val) {
node = p.left;
prev = node;
}
}else {
if(p.left.val == p1.val || p.left.val == p2.val) {
return prev;
}
}
}else {
p = s.pop();
if(p == node) {
prev = node;
node = s.peek();
}
while(p.right == null && !s.isEmpty()) {
p = s.pop();
if(p == node) {
prev = node;
node = s.peek();
}
}
if(p.right != null) {
s.push(p.right);
// System.out.print(p.right.val + " ");
if(prev == null) {
if(p.right.val == p1.val || p.right.val == p2.val) {
node = p.right;
prev = node;
}
}else {
if(p.right.val == p1.val || p.right.val == p2.val) {
return prev;
}
}
}else {
break;
}
}
}
return null;
}
三、方法三:使用递归,这个题目用递归,有点想法
思想:从二叉树的后面向前面找,
分为四情况:
1)节点node的左右子节点都不等于给定值【!= p1.val && != p2.val】 ,此时返回null
2)节点node满足条件【=p1.val || =p2.val】 返回node
3)节点node的左节点满足 && 节点node的右节点也满足 【一个等于p1.val 一个=p2.val】 返回node
4)节点node的左节点 || 右节点满足,返回左节点 || 右节点
/**
*后序,递归,huishu
*执行结果:
通过
显示详情
执行用时:7 ms, 在所有 Java 提交中击败了99.86% 的用户
内存消耗:40.7 MB, 在所有 Java 提交中击败了76.95% 的用户
* @param head
* @param p1
* @param p2
* @param b1
* @param b2
* @return
*/
public TreeNode commonTN(TreeNode head, TreeNode p1, TreeNode p2) {
if(head == null) {
return null;
}else {
TreeNode t1 = this.commonTN(head.left, p1, p2);
TreeNode t2 = this.commonTN(head.right, p1, p2);
if(head.val == p1.val || head.val == p2.val) {
return head;
}
if(t1 != null && t2 != null) {
return head;
}
return t1 == null? t2 : t1;
}
}
完整实例代码
public class TestCommonTreeNode {
/**
*后序,递归,huishu
*执行结果:
通过
显示详情
执行用时:7 ms, 在所有 Java 提交中击败了99.86% 的用户
内存消耗:40.7 MB, 在所有 Java 提交中击败了76.95% 的用户
* @param head
* @param p1
* @param p2
* @param b1
* @param b2
* @return
*/
public TreeNode commonTN(TreeNode head, TreeNode p1, TreeNode p2) {
if(head == null) {
return null;
}else {
TreeNode t1 = this.commonTN(head.left, p1, p2);
TreeNode t2 = this.commonTN(head.right, p1, p2);
if(head.val == p1.val || head.val == p2.val) {
return head;
}
if(t1 != null && t2 != null) {
return head;
}
return t1 == null? t2 : t1;
}
}
/**
* 方法二 迭代 使用前序遍历,
* 执行用时:10 ms, 在所有 Java 提交中击败了23.52% 的用户
内存消耗:39.5 MB, 在所有 Java 提交中击败了98.92% 的用户
* @param head
* @param p1
* @param p2
* @return
*/
public TreeNode common(TreeNode head, TreeNode p1, TreeNode p2) {
if(head == null) {
return null;
}
Deque<TreeNode> s = new LinkedList<>();
s.push(head);
// System.out.print(head.val + " ");
TreeNode node = null;
TreeNode prev = null;
if(head.val == p1.val || head.val == p2.val) {
node = head;
prev = node;
}
//开始前序遍历
while(!s.isEmpty()) {
TreeNode p = s.peek();
if(p.left != null) {
s.push(p.left);
// System.out.print(p.left.val + " ");
if(prev == null) {
if(p.left.val == p1.val || p.left.val == p2.val) {
node = p.left;
prev = node;
}
}else {
if(p.left.val == p1.val || p.left.val == p2.val) {
return prev;
}
}
}else {
p = s.pop();
if(p == node) {
prev = node;
node = s.peek();
}
while(p.right == null && !s.isEmpty()) {
p = s.pop();
if(p == node) {
prev = node;
node = s.peek();
}
}
if(p.right != null) {
s.push(p.right);
// System.out.print(p.right.val + " ");
if(prev == null) {
if(p.right.val == p1.val || p.right.val == p2.val) {
node = p.right;
prev = node;
}
}else {
if(p.right.val == p1.val || p.right.val == p2.val) {
return prev;
}
}
}else {
break;
}
}
}
return null;
}
/*
* 使用后序遍历,得到两个链表
*
* 执行用时:44 ms, 在所有 Java 提交中击败了5.00% 的用户
内存消耗:40.7 MB, 在所有 Java 提交中击败了77.18% 的用户
*/
public TreeNode common02(TreeNode head, TreeNode p1, TreeNode p2) {
System.out.println("\n迭代后序遍历");
if(head == null) {
return null;
}
Deque<TreeNode> s = new LinkedList<>();
s.push(head);
//创建两个ArrayList
ArrayList<TreeNode> list1 = new ArrayList<>();
ArrayList<TreeNode> list2 = new ArrayList<>();
if(head.val == p1.val) {
list1.add(head);
}else if(head.val == p2.val) {
list2.add(head);
}
while(!s.isEmpty()) {
TreeNode p = s.peek();
if(p.left != null) {//遍历节点的左边
s.push(p.left); //细心,不要写成node.left
//对list进行赋值,若都赋值了,则说明得到所有的父节点
if(p.left.val == p1.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list1.add(it.next());
}
}else if(p.left.val == p2.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list2.add(it.next());
}
}
if(list1.size() != 0 && list2.size() != 0) {
break;
}
}else {
while(p.right == null && !s.isEmpty()) {//为了给里面的while中break,使得可以跳出外围的break
TreeNode last = s.pop();
System.out.print(last.val + " ");
if(s.isEmpty()) {
break;
}
p = s.peek();
while(p.right == last) {
last = s.pop();
System.out.print(last.val + " ");
if(s.isEmpty()) {
break;
}
p = s.peek();
}
}
if(s.isEmpty()) {//栈为null 和之前的不同,因为,后序是判断右边是否为null,再输出node,
break;
}else {//p.right != null
s.push(p.right);
//对list进行赋值,若都赋值了,则说明得到所有的父节点
if(p.right.val == p1.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list1.add(it.next());
}
}else if(p.right.val == p2.val) {
Iterator<TreeNode> it = s.iterator();
while(it.hasNext()) {
list2.add(it.next());
}
}
if(list1.size() != 0 && list2.size() != 0) {
break;
}
}
}
}
System.out.println(list1.toString());
System.out.println(list2.toString());
for(int i = 0; i < list1.size(); i++) {
for(int j = 0; j < list2.size(); j++) {
if(list1.get(i).val == list2.get(j).val) {
return list1.get(i); //注意不是生成新的节点,否则这个节点没有意义,只是做题 important
}
}
}
return null;
}
/**
* 前序遍历
* @param head
*/
public void preOrder(TreeNode head) {
if(head == null) {
return ;
}else {
System.out.print(head.val + " ");
this.preOrder(head.left);
this.preOrder(head.right);
}
}
/**
* 初始化一个tree
* 类广度遍历
* @param a
* @return
*/
public TreeNode initTree(Integer[] a) {
if(a == null || a.length == 0) {
return null;
}
int t = 0;
TreeNode p = new TreeNode(a[t]); //至少有一个元素
Queue<TreeNode> q = new LinkedList<>();
q.offer(p);
while(!q.isEmpty()) {
TreeNode node = q.poll();
if(t + 1 == a.length) { //先判断数组中是否还有下一个元素
return p;
}else {
t++;
if(a[t] == null) { //若下一个元素为null,则不需要创建新的节点
node.left = null;
}else {
node.left = new TreeNode(a[t]);
q.offer(node.left);
}
}
if(t + 1 == a.length) {
return p;
}else {
t++;
if(a[t] != null){ //上面的简写,a[t] == null,不需要再赋值
node.right = new TreeNode(a[t]);
q.offer(node.right);
}
}
}
return p;
}
@Test
public void test() {
Integer[] a = new Integer[] {1, 2, 3, 4, 5, 9, 10, null, 6, 7, 8, null, null, null, 11};
TreeNode head = this.initTree(a);
TreeNode p1 = new TreeNode (2);
TreeNode p2 = new TreeNode (7);
System.out.println("方法一:使用递归");
System.out.println(this.commonTN(head, p1, p2).val);
System.out.println("方法二:迭代");
System.out.println(this.common(head, p1, p2).val);
System.out.println("方法三:迭代 得到两个list");
System.out.println(this.common02(head, p1, p2).val);
// System.out.println("前序遍历");
// this.preOrder(head);
}
@Test
public void test02() {
// Deque<Integer> s = new LinkedList<>();
// s.push(1);
// s.push(2);
// s.push(3);
//
// Iterator<Integer> it = s.iterator();
// while(it.hasNext()) {
// System.out.print(it.next() + " ");
//
// }
//
// System.out.println(s.toString());
}
}