Cracking the coding interview--Q4.6

原文:

Design an algorithm and write code to find the first common ancestor of two nodes in a binary tree. Avoid storing additional nodes in a data structure. NOTE: This is not necessarily a binary search tree.

译文:

写程序在一棵二叉树中找到两个结点的第一个共同祖先。不允许存储额外的结点。注意: 这里不特指二叉查找树。


若使用额外的存储空间,最简单的方式就是对于一个结点向父节点进行遍历,直至根结点。可以考虑存储为hash,然后对另一个结点向父节点进行遍历,同时和第一个结点的父节点,所组成的hash集合进行比较,第一个相同的结点即为二者的共同祖先;

若不能使用额外的存储空间,则只能是不断的取一个节点的父节点,然后同另外一个节点的所有父节点进行比较,直到根节点;

另外如果节点没有父指针,那么则需要从根节点开始遍历,依次判断每个节点,是否可以到达问题中的两个节点。找到最后一个这样的节点即可

代码如下:


1、如果结点可以记录父亲结点信息,并可以借助外部存储空间

	/**
	 * @param p
	 * @param q
	 * @return
	 * 
	 * 如果结点可以记录父亲结点信息,并可以借助外部存储空间,
	 * 则可以使p的所有父亲结点记录在hashset中,然后一次遍历q的父亲结点,每个父节点一次与hashset中的结点比较,
	 * 相同即为最近的相同父亲结点
	 * 
	 */
	public static Node_4_6 findCommonParent(Node_4_6 p, Node_4_6 q) {
		if(p ==null || q ==null) {
			return null;
		}
		HashSet<Node_4_6> set = new HashSet<Node_4_6>();
		while(p.parent != null) {
			set.add(p.parent);
			p = p.parent;
		}
		while(q.parent != null) {
			q = q.parent;
			if(set.contains(q)) {
				break;
			} 
		}
		if(q != null) {
			return q;
		} else {
			return null;
		}
	}



2、如果结点可以记录父亲结点信息,不可以借助外部存储空间

	/**
	 * @param p
	 * @param q
	 * @return
	 * 
	 * 如果结点可以记录父亲结点信息,不可以借助外部存储空间,
	 * 则可以遍历p的所有父亲结点,然后每次遍历一个p的父亲结点则依次遍历q的父亲结点,直到找到相同结点存在,
	 * 相同即为最近的相同父亲结点
	 * 
	 */
	public static Node_4_6 findCommonParent2(Node_4_6 p, Node_4_6 q) { 
		if(p ==null || q ==null) {
			return null;
		}
		while(p.parent != null) {
			p = p.parent;
			while(q.parent != null) {
				q = q.parent;
				if( p == q) {
					return p;
				}
			}
		}
		return null;
	}


3、如果结点没有引用父亲结点,并且同时不能使用额外的存储空间

	/**
	 * @param head
	 * @param p
	 * @param q
	 * @return
	 * 
	 * 如果结点没有引用父亲结点,并且同时不能使用额外的存储空间,
	 * 则只能从head结点一次遍历结点信息,并且每次遍历结点判断当前结点是否同时为p,q的父结点
	 * 并且还需要判断是否最近
	 * 
	 */
	public static void findCommonParent3(Node_4_6 head, Node_4_6 p, Node_4_6 q) { 
		if(p ==null || q ==null || head == null) {
			return;
		}
		if(head != null && head!=p && head!=q && father(head, p) && father(head, q)) {
			ancestor = head;
			findCommonParent3(head.lchild, p, q);
			findCommonParent3(head.rchild, p, q);
		}
	}	
	
	/**
	 * @param head
	 * @param node
	 * @return
	 * 
	 * 判断当前结点node是否是head结点的子节点
	 * 即head是否为node的祖先结点
	 * 
	 */
	public static boolean father(Node_4_6 head, Node_4_6 node) {
		if(head == null) {
			return false;
		} else if(node == head) {
			return true;
		} else {
			return father(head.lchild, node) || father(head.rchild, node);
		}
	}


总代码如下:

package chapter_4_TreesandGraphs;

import java.util.HashSet;
import java.util.Scanner;
import java.util.Stack;

class Node_4_6 {
	public int data;
	public Node_4_6 lchild;
	public Node_4_6 rchild;
	public Node_4_6 parent;
}

public class Question_4_6 {
	private static Node_4_6 p, q;
	private static Node_4_6 ancestor;
	
	public static void insertTreeNode(Node_4_6 curNode, Node_4_6 node) {
		if(node.data < curNode.data) {
			if(curNode.lchild != null) {
				insertTreeNode(curNode.lchild, node);
			} else{
				curNode.lchild = node;
				node.parent = curNode;
			}
		} else {
			if(curNode.rchild != null) {
				insertTreeNode(curNode.rchild, node);
			} else {
				curNode.rchild = node;
				node.parent = curNode;
			}
		}
	}
	
	public static void createTree(Node_4_6 head, int array[]) {
		int len = array.length;
		int curIndex = 1;
		while(curIndex < len) {
			Node_4_6 node = new Node_4_6();
			node.data = array[curIndex];
			if(node.data == 1) {
				p = node;
				System.out.println("set p.data = " + node.data);
			} 
			if(node.data == 5) {
				q = node;
				System.out.println("set q.data = " + node.data);
			}
			node.lchild = null;
			node.rchild = null;
			insertTreeNode(head, node);
			curIndex ++;
		}
	}
	
	public static void postOrderTraverce(Node_4_6 head) {
		Stack<Node_4_6> stack = new Stack<Node_4_6>();
		stack.push(head);
		Node_4_6 node;
		Node_4_6 preNode = null;
		while(!stack.empty()) {
			node = stack.peek();
			if(node.lchild==null && node.rchild==null ||
					(preNode!=null && (node.lchild == preNode || node.rchild == preNode))) {
				System.out.print(" " + node.data + " ");
				stack.pop();
				preNode = node;
			} else {
				if(node.rchild != null) {
					stack.push(node.rchild);
				} 
				if(node.lchild != null) {
					stack.push(node.lchild);
				}
			}
		}
	}
	

	/**
	 * @param p
	 * @param q
	 * @return
	 * 
	 * 如果结点可以记录父亲结点信息,并可以借助外部存储空间,
	 * 则可以使p的所有父亲结点记录在hashset中,然后一次遍历q的父亲结点,每个父节点一次与hashset中的结点比较,
	 * 相同即为最近的相同父亲结点
	 * 
	 */
	public static Node_4_6 findCommonParent(Node_4_6 p, Node_4_6 q) {
		if(p ==null || q ==null) {
			return null;
		}
		HashSet<Node_4_6> set = new HashSet<Node_4_6>();
		while(p.parent != null) {
			set.add(p.parent);
			p = p.parent;
		}
		while(q.parent != null) {
			q = q.parent;
			if(set.contains(q)) {
				break;
			} 
		}
		if(q != null) {
			return q;
		} else {
			return null;
		}
	}
	
	/**
	 * @param p
	 * @param q
	 * @return
	 * 
	 * 如果结点可以记录父亲结点信息,不可以借助外部存储空间,
	 * 则可以遍历p的所有父亲结点,然后每次遍历一个p的父亲结点则依次遍历q的父亲结点,直到找到相同结点存在,
	 * 相同即为最近的相同父亲结点
	 * 
	 */
	public static Node_4_6 findCommonParent2(Node_4_6 p, Node_4_6 q) { 
		if(p ==null || q ==null) {
			return null;
		}
		while(p.parent != null) {
			p = p.parent;
			while(q.parent != null) {
				q = q.parent;
				if( p == q) {
					return p;
				}
			}
		}
		return null;
	}
	
	/**
	 * @param head
	 * @param p
	 * @param q
	 * @return
	 * 
	 * 如果结点没有引用父亲结点,并且同时不能使用额外的存储空间,
	 * 则只能从head结点一次遍历结点信息,并且每次遍历结点判断当前结点是否同时为p,q的父结点
	 * 并且还需要判断是否最近
	 * 
	 */
	public static void findCommonParent3(Node_4_6 head, Node_4_6 p, Node_4_6 q) { 
		if(p ==null || q ==null || head == null) {
			return;
		}
		if(head != null && head!=p && head!=q && father(head, p) && father(head, q)) {
			ancestor = head;
			findCommonParent3(head.lchild, p, q);
			findCommonParent3(head.rchild, p, q);
		}
	}	
	
	/**
	 * @param head
	 * @param node
	 * @return
	 * 
	 * 判断当前结点node是否是head结点的子节点
	 * 即head是否为node的祖先结点
	 * 
	 */
	public static boolean father(Node_4_6 head, Node_4_6 node) {
		if(head == null) {
			return false;
		} else if(node == head) {
			return true;
		} else {
			return father(head.lchild, node) || father(head.rchild, node);
		}
	}
	
	public static void main(String args[]) {
		Scanner scanner = new Scanner(System.in);
		String string = scanner.nextLine();
		// 7 10 1 3 6 5 4 2 8
		String strs[] = string.split(" ");
		int array[] = new int[strs.length];
		for(int i=0; i<array.length; i++) {
			array[i] = Integer.parseInt(strs[i]);
		}
		
		Node_4_6 head = new Node_4_6();
		head.data = array[0];
		head.lchild = null;
		head.rchild = null;
		
		createTree(head, array);
		
		postOrderTraverce(head);
		System.out.println("");
		
		Node_4_6 commonParent = findCommonParent(p, q);
		if(commonParent == null) {
			System.out.println("error!不存在");
		} else {
			System.out.println("common least parent is : " + commonParent.data);
		}
		
		Node_4_6 commonParent2 = findCommonParent2(p, q);
		if(commonParent2 == null) {
			System.out.println("error!不存在");
		} else {
			System.out.println("common least parent is : " + commonParent.data);
		}
		
		findCommonParent3(head, p, q);
		if(ancestor == null) {
			System.out.println("error!不存在");
		} else {
			System.out.println("common least parent is : " + ancestor.data);
		}
		
	}	
}	


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值