判斷一棵二叉樹是否為另一棵二叉樹的子結構(JAVA版本)
阿新 • • 發佈:2018-12-01
分析:判斷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結束。(如果太糾結遞迴那個過程很容易頭暈腦脹的,所以還是簡單點思考吧!)