@author: sdubrz
@date: 2020.05.03
@e-mail: lwyz521604#163.com
題目來自《劍指offer》 電子工業出版社
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
例如,給出
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
3
/ \
9 20
/ \
15 7
限制:
0 <= 節點個數 <= 5000
我的解法
對於一顆二叉樹,其前序遍歷中第一個元素必然是根節點,並且左子樹的元素位於右子樹元素的前面。而在中序遍歷中,左子樹的元素均位於根節點元素的前面,右子樹的元素均位於根節點元素的後面。下圖表示了這一位置關係,其中紅色元素爲根節點,綠色的爲左子樹中的元素,藍色的爲右子樹中的元素。
根據這一位置關係,我們可以寫出遞歸的解決方案,下面是具體的Java代碼實現。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length==0){
return null;
}
int n = preorder.length;
TreeNode root = this.buildTree(preorder, inorder, 0, n-1, 0, n-1);
return root;
}
private TreeNode buildTree(int[] preorder, int[] inorder, int preHead, int preTail, int inHead, int inTail){
if(preHead==preTail){ // 只有一個節點
TreeNode node = new TreeNode(preorder[preHead]);
return node;
}
TreeNode root = new TreeNode(preorder[preHead]);
int rootIndex = -1;
for(int i=inHead; i<=inTail; i++){
if(inorder[i]==preorder[preHead]){
rootIndex = i;
break;
}
}
int leftSize = rootIndex - inHead; // 左子樹節點數
int rightSize = inTail - rootIndex; // 右子樹節點數
if(leftSize>0){
root.left = this.buildTree(preorder, inorder, preHead+1, preHead+leftSize, inHead, rootIndex-1);
}
if(rightSize>0){
root.right = this.buildTree(preorder, inorder, preHead+leftSize+1, preTail, rootIndex+1, inTail);
}
return root;
}
}
在 LeetCode 系統中提交的結果如下
執行結果: 通過 顯示詳情
執行用時 : 4 ms, 在所有 Java 提交中擊敗了 60.63% 的用戶
內存消耗 : 40.1 MB, 在所有 Java 提交中擊敗了 100.00% 的用戶
官方遞歸解法
LeetCode 的題解中官方給出了遞歸和迭代兩種解法。其中,遞歸解法的思路與我的一致,不過代碼的實現略有不同。下面是官方給出的遞歸解法的Java代碼。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
int length = preorder.length;
for (int i = 0; i < length; i++) {
indexMap.put(inorder[i], i);
}
TreeNode root = buildTree(preorder, 0, length - 1, inorder, 0, length - 1, indexMap);
return root;
}
public TreeNode buildTree(int[] preorder, int preorderStart, int preorderEnd, int[] inorder, int inorderStart, int inorderEnd, Map<Integer, Integer> indexMap) {
if (preorderStart > preorderEnd) {
return null;
}
int rootVal = preorder[preorderStart];
TreeNode root = new TreeNode(rootVal);
if (preorderStart == preorderEnd) {
return root;
} else {
int rootIndex = indexMap.get(rootVal);
int leftNodes = rootIndex - inorderStart, rightNodes = inorderEnd - rootIndex;
TreeNode leftSubtree = buildTree(preorder, preorderStart + 1, preorderStart + leftNodes, inorder, inorderStart, rootIndex - 1, indexMap);
TreeNode rightSubtree = buildTree(preorder, preorderEnd - rightNodes + 1, preorderEnd, inorder, rootIndex + 1, inorderEnd, indexMap);
root.left = leftSubtree;
root.right = rightSubtree;
return root;
}
}
}
由於使用了一個Map來存儲中序遍歷中每個元素與其索引的對應關係,因而在查找節點位置時,官方代碼要比我的代碼更快一些。
執行結果: 通過 顯示詳情
執行用時 : 3 ms, 在所有 Java 提交中擊敗了 81.16% 的用戶
內存消耗 : 39.8 MB, 在所有 Java 提交中擊敗了 100.00% 的用戶
官方迭代解法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preorder[0]);
int length = preorder.length;
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
int inorderIndex = 0;
for (int i = 1; i < length; i++) {
int preorderVal = preorder[i];
TreeNode node = stack.peek();
if (node.val != inorder[inorderIndex]) {
node.left = new TreeNode(preorderVal);
stack.push(node.left);
} else {
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
node = stack.pop();
inorderIndex++;
}
node.right = new TreeNode(preorderVal);
stack.push(node.right);
}
}
return root;
}
}
下面是在 LeetCode 系統中提交的結果
執行結果: 通過 顯示詳情
執行用時 : 3 ms, 在所有 Java 提交中擊敗了 81.16% 的用戶
內存消耗 : 39.6 MB, 在所有 Java 提交中擊敗了 100.00% 的用戶
由於每個元素都需要一次新建節點的過程,所以這三種方法的時間複雜度均爲 。