1. 程式人生 > >二叉樹遍歷演算法與重構(2)

二叉樹遍歷演算法與重構(2)

上一篇(https://blog.csdn.net/To_be_to_thought/article/details/84668630)介紹了二叉樹的性質和常用的相關的遞迴、非遞迴演算法,這一篇想接著寫從二叉樹的遍歷結果重構一棵樹,主要是重構的過程思路。

1.中序遍歷和先根遍歷結果重構二叉樹演算法:

                                                

                                                 

1.1回憶原始的樹(以2為根節點的樹)先根遍歷過程和結果序列:

 

                                                

                                                          

再看以7為根節點的子樹的先根遍歷過程以及結果序列:

                                                                             

                                                                 

再看以5位根節點的右子樹的先根遍歷過程以及結果序列:

                                                                              

                                                                             

把整個遞迴過程表現出來,黑色表示遍歷訪問一棵新(子)樹,紅色表示訪問樹節點:

                                                                  

1.2另一方面回憶一下(以2為根節點的樹)中根遍歷過程和結果序列:

                                            

                                                 

以7為根節點的左子樹的中根遍歷和結果序列:

                                                              

                                                                       

以5根節點的右子樹的中根遍歷和結果序列;

                                                                    

 

                                                                                      

把整個遞迴過程表現出來,黑色表示遍歷訪問一棵新(子)樹,紅色只表示訪問樹節點,藍色表示當前子樹的根:

                                                  

                                                                          

1.3把上述過程總結一下:

在中序遍歷中找出整棵樹的根節點的索引index,然後index左邊序列為左子樹的中序遍歷,index右邊序列為右子樹的中序遍歷。

               

對於這一步產生的左子樹比遍歷序列可以採用同樣的方式繼續切分:

                       

那麼應該如何切分才是最為關鍵的問題:

首先根節點元素在子陣列中的相對位置i是可以通過在中序遍歷子陣列中搜索到的,子陣列索引範圍可以由(起點索引,終點索引)或者(起點索引,子陣列長度)的二元組唯一確定:
     先根遍歷和中根遍歷兩種次序可以唯一確定二叉樹,後根遍歷(子)陣列PostOrderArray和中根遍歷子陣列(子)InOrderArray長度為n     
    對於整棵樹,先根遍歷序列的起點prestart,先根遍歷序列PreOrderArray的PreOrderArray[prestart]為根節點;中根遍歷序列的起點為inStart,中根遍歷序列的InOrderArray中尋找值等於PreOrderArray[inStart]的節點,序號為i
     在InOrderArray中i左邊0,i-1為左子樹,i+1,n為右子樹
     左子樹的先根遍歷序列(長度為i)為PreOrderArray[preStart+1],...,PreOrderArray[preStart+i],
     左子樹的中根遍歷序列(長度為i)為InOrderArray[inStart],...InOrderArray[i+inStart-1]
     右子樹的先根遍歷序列(長度為n-i-1)為PreOrderArray[preStart+i+1],...PreOrderArray[n+preStart+1]
     右子樹的中根遍歷序列(長度為n-i-1)為InOrderArray[inStart+i+1],...,InOrderArray[n+inStart-1]

有了如上歸納,不難得出如下程式碼:

        public BinaryTree(T[] preOrderArray,T[] inOrderArray)
	{
		this.root=create(preOrderArray,inOrderArray,0,0,preOrderArray.length);
	}
	
	public BinaryNode<T> create(T[] preOrderArray,T[] inOrderArray,int preStart,int inStart,int n )
	{
		if(n<=0)
			return null;
		T e=preOrderArray[preStart];
		BinaryNode<T> p=new BinaryNode<T>(e);
		
		int i=0;
		while(i<n && !inOrderArray[i+inStart].equals(e))
			i++;
		p.left=create(preOrderArray,inOrderArray,preStart+1,inStart,i);
		p.right=create(preOrderArray,inOrderArray,preStart+i+1,inStart+i+1,n-1-i);
		return p;
	}

2.後序遍歷和先根遍歷結果重構二叉樹演算法:

由於上一個例子過於詳細,這個演算法有點類似,關鍵是找切分子陣列的位置

舉個例子:後根遍歷和中根遍歷兩種次序可以唯一確定二叉樹,後根遍歷(子)陣列PostOrderArray和中根遍歷(子)陣列InOrderArray長度為n,序號為0~n-1
     後根遍歷整個陣列中PostOrderArray的PostOrderArray[n-1]為根節點,在中根遍歷序列的InOrderArray中尋找值為PostOrderArray[n-1]的節點,序號為i
     在InOrderArray中i左邊[0:i-1]為左子樹,[i+1:n]為右子樹
     左子樹的後根遍歷序列為PostOrderArrayArray[0],...,PostOrderArray[i-1]
     左子樹的中根遍歷序列為InOrderArray[0],...InOrderArray[i-1]
     右子樹的後根遍歷序列為PostOrderArray[i+1],...PostOrderArray[n-2]
     右子樹的中根遍歷序列InOrderArray[i+1],...,InOrderArray[n-1]

下面進行歸納:

     後根遍歷和中根遍歷兩種次序可以唯一確定二叉樹,後根遍歷(子)陣列PostOrderArray和中根遍歷子陣列(子)InOrderArray長度為n ,在原陣列中的起點索引分別為inStart,postStart,終點索引分別為inStart+n-1,postStart+n-1,也就是說:PostOrderArray[postStart:postStart+n-1]和InOrderArray[inStart:inStart+n-1]對同一棵樹的後根遍歷和中根遍歷。
    後根遍歷序列PostOrderArray的PostOrderArray[postStart+n-1]為根節點;在中根遍歷子序列尋找值為PostOrderArray[postStart+n-1]的節點陣列索引i;
     左子樹的後根遍歷序列(長度為i)為PostOrderArray[postStart],...,PreOrderArray[postStart+i-1],
     左子樹的中根遍歷序列(長度為i)為InOrderArray[inStart],...InOrderArray[i+inStart-1]
     右子樹的後根遍歷序列(長度為n-i-1)為PostOrderArray[postStart+i],...PostOrderArray[postStart+n-2]
     右子樹的中根遍歷序列(長度為n-i-1)為InOrderArray[inStart+i+1],...,InOrderArray[inStart+n-1]

可以利用起點索引和子陣列長度(子樹節點總數)手動來推一推上面的歸納結論:

程式碼如下:

        public BinaryTree(T[] PostOrderArray,T[] inOrderArray)
	{
		this.root=create(PostOrderArray,inOrderArray,0,0,PostOrderArray.length);
	}
	
	public BinaryNode<T> create(T[] PostOrderArray,T[] inOrderArray,int postStart,int inStart,int n )
	{
		if(n<=0)
			return null;
		T e=PostOrderArray[postStart];
		BinaryNode<T> p=new BinaryNode<T>(e);
		
		int i=0;
		while(i<n && !inOrderArray[i+inStart].equals(e))
			i++;
		p.left=create(PostOrderArray,inOrderArray,postStart,inStart,i);
		p.right=create(PostOrderArray,inOrderArray,postStart+i,inStart+i+1,n-i-1);
		return p;
	}

這大概是我寫的最長的部落格了,圖花了好長時間才畫好的!!!