105. 從前序與中序遍歷序列構造二叉樹
棧+迭代
規律
- 前序遍歷中相鄰節點u和v,v節點一定是u節點的左節點或者是其自身某個祖先的右節點
- 一個沒有右節點的鏈,中序遍歷是從葉子到根,前序遍歷是從根到葉子
解題思路
- 用一個棧維護前序遍歷的節點
- 用一個指標
p
指向中序遍歷的第一個葉子節點 - 遍歷前序,一直遍歷到
p
指向的葉子節點,都持續是其父節點的左兒子,入棧即可。 - 若遍歷到的節點的上一個節點為
p
指向的葉子節點,那麼該節點就是樹鏈(棧)上某個節點的右節點。如何找到其父親? - 使指標
p
在中序遍歷上右移,同時在樹鏈(棧)上回溯(彈棧),找到最後一個相同的節點,則為其父親。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
TreeNode root = new TreeNode(preorder[0]);
Stack<TreeNode> s = new Stack<>();
s.push(root);
int iIn = 0;
TreeNode now = root;
for(int i = 1; i < preorder.length; ++ i) {
if(preorder[i - 1] != inorder[iIn]) {
now.left = new TreeNode(preorder[i]);
s.push(now.left);
now = now.left;
} else {
while(!s.empty() && s.peek().val == inorder[iIn]) {
now = s.pop();
iIn ++ ;
}
now.right = new TreeNode(preorder[i]);
s.push(now.right);
now = now.right;
}
}
return root;
}
}
遞迴
- 根節點在先序遍歷中左右子樹區間長度和在中序遍歷中區間長度一致
- 先序遍歷的子樹區間第一個節點一定是根
- 定位中序遍歷中根的位置,根的左子樹區間在其左邊,右子樹區間在其右邊,由此得到左右子樹區間長度
- 由得到的左右子樹區間長度來定位在先序遍歷中的子樹區間
- 再構建兩棵子樹,層層遞迴即可
class Solution {
Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
map = new HashMap<>();
for(int i = 0; i < inorder.length; ++ i ) {
map.put(inorder[i], i);
}
return build(preorder, 0, preorder.length-1, 0, preorder.length-1);
}
public TreeNode build(int[] preorder, int preL, int preR, int inL, int inR) { // [preL, preR] [inL, inR]
int val = preorder[preL];
TreeNode root = new TreeNode(val);
if(preL == preR) return root;
int inId = map.get(val);
int numL = inId - inL; // 左子樹大小
int numR = inR - inId; // 右子樹大小
if(numL > 0) root.left = build(preorder, preL + 1, preL + numL, inId - numL, inId - 1);
if(numR > 0) root.right = build(preorder, preL + numL + 1, preR, inId + 1, inId + numR);
return root;
}
}