Java实现:最近公共祖先节点

24 篇文章 0 订阅
23 篇文章 0 订阅

标题: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());
	}
	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中二叉树的最近公共祖先可以通过递归来解决。首先,我们需要定义一个TreeNode类表示二叉树的节点,其中包含值和左右子节点的引用。下面是一个示例代码: ```java class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int val) { this.val = val; } } ``` 接下来,我们可以实现一个递归函数来找到最近公共祖先: ```java public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { // 判断当前节点是否为空或者等于p或q if (root == null || root == p || root == q) { return root; } // 在左子树中寻找最近公共祖先 TreeNode left = lowestCommonAncestor(root.left, p, q); // 在右子树中寻找最近公共祖先 TreeNode right = lowestCommonAncestor(root.right, p, q); // 如果左子树和右子树都找到了最近公共祖先,则当前节点就是最近公共祖先 if (left != null && right != null) { return root; } // 如果只有左子树找到了最近公共祖先,则返回左子树的结果 if (left != null) { return left; } // 如果只有右子树找到了最近公共祖先,则返回右子树的结果 if (right != null) { return right; } // 如果左右子树都没有找到最近公共祖先,则返回null return null; } ``` 这个递归函数的基本思路是: - 当前节点为空或者等于p或q时,直接返回当前节点; - 在左子树中寻找最近公共祖先; - 在右子树中寻找最近公共祖先; - 如果左子树和右子树都找到了最近公共祖先,则当前节点就是最近公共祖先; - 如果只有左子树找到了最近公共祖先,则返回左子树的结果; - 如果只有右子树找到了最近公共祖先,则返回右子树的结果; - 如果左右子树都没有找到最近公共祖先,则返回null。 你可以调用这个函数来找到二叉树中任意两个节点最近公共祖先
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值