1. 程式人生 > >演算法學習之重構二叉樹

演算法學習之重構二叉樹

背景

根據提供的前序和中序遍歷的結果,重構二叉樹

 

思路

根據前序和中序的特點,來構造二叉樹。

前序遍歷,根左右,頭一個一定是根結點。

中序遍歷,左根右,根據由前序遍歷確定的根結點,可以確定出左右子樹的中序遍歷結果,以及左右子樹的元素數量。

而後,根據中序遍歷確定的左右子樹的元素數量,可以分出前序遍歷中左右子樹的範圍。

最後,進行遞迴,後序遍歷驗之。

 

實現

程式碼如下

package practice;

import oo.MyTree;

public class RebuildBinaryTree {

	public static void reBuild(MyTree t, int[] pre, int[] in) {
		int leftLength = -1; // 左子樹長度
		int rightLength = -1; // 右子樹長度
		int rootData = pre[0]; // 先序遍歷,第一個一定是根結點

		t.setData(rootData);

		for (int i = 0; i < in.length; i++) {
			if (in[i] == rootData) {
				leftLength = i;// leftLength = 3 1
				break;
			}
		} // 中序遍歷,根節點前面的都是左子樹,據此求出左子樹長度

		if (leftLength <= 0) {
			t.setLeft(null);
			// 如果左子樹長度小於1,說明中序遍歷的頭一個就是根結點,也就是沒有左子樹
		} else {
			// 否則,說明有左子樹,把左子樹的先序遍歷和中序遍歷的結果拷貝,進行遞迴構造
			int[] leftInOrder = new int[leftLength];
			int[] leftPreOrder = new int[leftLength];
			for (int i = 0; i < leftLength; i++) {
				leftInOrder[i] = in[i];
				leftPreOrder[i] = pre[i + 1]; // i+1是因為先序遍歷的第一個是根結點
			}
			t.setLeft(new MyTree());
			reBuild(t.getLeft(), leftPreOrder, leftInOrder); // 遞迴
		}

		// 相似的方法構造右子樹
		if (leftLength == in.length - 1) {
			t.setRight(null);
			// 如果左子樹的長度是中序遍歷的結果-1,那左子樹配上根結點,就是中序遍歷的全部結果,也就是沒有右子樹了
		} else {
			rightLength = in.length - leftLength - 1;
			// 否則,就是有右子樹,其長度是中序長度-左子樹長度-根結點(也就是-1)
			// 這個右子樹長度,也可以看成右子樹元素在中序遍歷結果中的偏移量
			int[] rightInOrder = new int[rightLength];
			int[] rightPreOrder = new int[rightLength];
			for (int i = 0; i < rightLength; i++) {
				rightInOrder[i] = in[i + rightLength]; // 拷貝時要加上偏移量
				rightPreOrder[i] = pre[i + rightLength];
			}

			t.setRight(new MyTree());
			reBuild(t.getRight(), rightPreOrder, rightInOrder); // 遞迴
		}

	}

	public static void main(String[] args) {
		// 前序遍歷{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6}
		int[] pre = { 1, 2, 4, 7, 3, 5, 6, 8 };
		int[] in = { 4, 7, 2, 1, 5, 3, 8, 6 };

		MyTree t = new MyTree();

		reBuild(t, pre, in);

		t.travelsInLastOrder(); // 後序遍歷驗證結果 
	}

}

執行結果

由前序和中序可知樹的拓撲圖為

後序遍歷的列印結果為

 

可見執行沒問題。