1. 程式人生 > >判斷一棵二叉樹是否為另一棵二叉樹的子結構(JAVA版本)

判斷一棵二叉樹是否為另一棵二叉樹的子結構(JAVA版本)

分析:判斷root1是否為root2的子樹?首先,必須先找到樹1中與樹2的根節點相同的節點,然後判斷從該節點開始root1中是否root2的結構;若有,則返回true,若沒有,則返回false?答案是No! 因為二叉樹root1中可能含有值相同的節點,所以,如果沒有找到,就需要繼續遍歷root1.
Way: 先設計一個函式,isSame(root1,root2)判斷給定相同根節點的兩子樹是否一樣?再設計一個函式,遞迴遍歷二叉樹root1各個節點,當找到相同節點時,就呼叫isSame()方法判斷是否具有相同的結構,若沒有,則繼續遍歷,若有,遞迴返回。

核心實現程式碼塊:

	public static boolean isSame(TreeNode<Character> root1,TreeNode<Character> root2){
		if(root2==null)
			return true;
		
		if(root1==null)
			return false;//此時root2不為空,而root1卻為空了,所以root2不是一的子結構
		
		if(!root1.data.equals(root2.data))
			return false;		
		
		return isSame(root1.leftChildren,root2.leftChildren)&&isSame(root1.rightChildren,root2.rightChildren);
	}
遞迴遍歷:

	//判斷是否子樹結構
	public static boolean isSubTree(TreeNode<Character> root1,TreeNode<Character> root2){
		if(root2==null)
			return true;//如果root2我空節點,則是root1的子樹
		if(root1!=null){
			if(root1.data.equals(root2.data) && isSame(root1,root2)){	
				//相等成立,表示在樹1中找到了與樹2根節點值相同的節點
				//****root1中可能有多個節點與root2根相等,僅當是子結構才返回,若不是則繼續遍歷
					return true;
			}else{
				//注意:此處不能分開寫,遞迴理解:只要左子樹或右子樹中有一個包含子樹即說明是root2是root1的子樹
				//若分開寫,則左子樹若判斷除了是子樹,在當前棧中返回了一個true,由於遞迴棧深度大,無法傳遞回來
				return isSubTree(root1.leftChildren,root2) || isSubTree(root1.rightChildren,root2);	
			}
		}
		return false;
	}
例項:
root1:
	    8
	   / \
	  8   7
	 / \
	9   2
	   / \
	  4   7
root2:
	    8
	   / \
	  8   7
完整程式碼:

(包含樹的節點定義、前序建立、中序遍歷)

class TreeNode<T> {
	T data;
	TreeNode<T> leftChildren;
	TreeNode<T> rightChildren;
	public TreeNode(T data){
		this.data=data;
		this.leftChildren=null;
		this.rightChildren=null;
	}
}

public class Mao {

	public static void main(String[] args) {
		
		//建立2顆二叉樹
		String str1="889##24##7##7##";
		String str2="89##2##";
		
		StringBuilder sb1=new StringBuilder(str1);
		StringBuilder sb2=new StringBuilder(str2);
		TreeNode<Character> root1=recurseCreateTree(sb1);
		TreeNode<Character> root2=recurseCreateTree(sb2);
		System.out.print("inOrder Tree1: ");
		inOrder(root1);
		System.out.print("\ninOrder Tree2: ");
		inOrder(root2);
		
		//判斷
//		root1=root2=null;   // true
//		root1=null;     //false
//		root2=null;     //false
//		root2.leftChildren.data='3';     //false
		System.out.println("\n Tree2 is subTree of Tree1? "+isSubTree_better(root1,root2));
	}
	
	//判斷是否子樹結構
	public static boolean isSubTree(TreeNode<Character> root1,TreeNode<Character> root2){
		if(root2==null)
			return true;//如果root2我空節點,則是root1的子樹
		if(root1!=null){
			if(root1.data.equals(root2.data) && isSame(root1,root2)){	
				//相等成立,表示在樹1中找到了與樹2根節點值相同的節點
				//****root1中可能有多個節點與root2根相等,僅當是子結構才返回,若不是則繼續遍歷
					return true;
			}else{
				//注意:此處不能分開寫,遞迴理解:只要左子樹或右子樹中有一個包含子樹即說明是root2是root1的子樹
				//若分開寫,則左子樹若判斷除了是子樹,在當前棧中返回了一個true,由於遞迴棧深度大,無法傳遞回來
				return isSubTree(root1.leftChildren,root2) || isSubTree(root1.rightChildren,root2);	
			}
		}
		return false;
	}
	
	//上述方法更好的可以寫為:(尤其是在普通樹結構中)
	public static boolean isSubTree_better(TreeNode<Character> root1,TreeNode<Character> root2){
		
		if(root2==null)
			return true;
		boolean flag=false;
		if(root1!=null){
			if(root1.data.equals(root2.data)){
				flag=isSame(root1,root2);
			}
			if(!flag)
				flag=isSubTree_better(root1.leftChildren,root2);
			if(!flag)
				flag=isSubTree_better(root1.rightChildren,root2);
		}
		
		return flag;
	}
	
	//比較兩棵樹是否一樣
	public static boolean isSame(TreeNode<Character> root1,TreeNode<Character> root2){
		if(root2==null)
			return true;
		
		if(root1==null)
			return false;//此時root2不為空,而root1卻為空了,所以root2不是一的子結構
		
		if(!root1.data.equals(root2.data))
			return false;		
		
		return isSame(root1.leftChildren,root2.leftChildren)&&isSame(root1.rightChildren,root2.rightChildren);
	}

	//遞迴建立二叉樹
	public static TreeNode<Character> recurseCreateTree(StringBuilder str){
		TreeNode<Character> node=null;
		char data=str.toString().charAt(0);
		str.delete(0,1);
		if('#'!=data){
			node=new TreeNode<Character>(data);
			node.leftChildren=recurseCreateTree(str);
			node.rightChildren=recurseCreateTree(str);
		}
		return node;
	}

	//二叉樹的前序遍歷
	public static void inOrder(TreeNode<Character> root){
		if(root!=null){
			inOrder(root.leftChildren);
			System.out.print(root.data+" ");
			inOrder(root.rightChildren);
		}
	}
}
輸出結果為:

inOrder Tree1: 9 8 4 2 7 8 7 
inOrder Tree2: 9 8 2 
 Tree2 is subTree of Tree1? true
總結:本題重點在對二叉樹的操作上,需要深入的理解遞迴的使用,以自己的見解(很淺):不需要深入考慮複雜的遞迴呼叫內部,而是單純的將原問題劃分子問題,如上述方法,出去對節點的null判斷,實際就是在root1中找到與root2相等的節點,判斷是否是子結構;如果不是,再看root1的左子樹是否包含root2結構,再沒有,就看右子樹是否包含root2結構;最後加上個遞迴結束條件,root1傳入的值為null結束。(如果太糾結遞迴那個過程很容易頭暈腦脹的,所以還是簡單點思考吧!)