一、给定前序和中序遍历,重构二叉树
leetcode 105号题
题目: 根据一棵二叉树的前序和中序遍历,重构出这棵树。树中没有重复元素
例如,给出:
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下二叉树:
3
/ \
9 20
/ \
15 7
题解:
- 前序遍历是根左右的顺序,中序遍历是左根右的顺序;
- 前序遍历的第一个元素必然是整棵树的根节点,知道根节点之后我们就可以依据中序遍历切分出左右子树;
- 我们在中序序列中分割好了左右子树之后,我们再找到左右子树在前序遍历和中序遍历中的起始位置,这样就可以把我们找到的左右子树再作为一棵单独的树来进行处理;
- 重复2,3两个步骤
整体的思想就是这样的,现在来看具体怎么做:
首先定义如下几个变量:
- iStart(中序的起始位置下标)
- iEnd(中序的结束位置下标)
- pStart(前序的起始位置下标)
- pEnd(前序的结束位置下标)
- rIndex(根节点在中序中的下标)
先看一下这几个变量是怎么变化的:
红色:根节点
绿色:左子树
蓝色:右子树
根据图中的标注,可以得到下面这些信息:
1. 左子树的长度:rIndex-iStart
2. 右子树的长度:iEnd-rIndex +1
左子树在前序遍历中的开始位置:pStart + 1
左子树在前序遍历中的结束位置:pStart + rIndex - iStart
左子树在中序遍历中的开始位置:iStart
左子树在中序遍历中的结束位置:rIndex- 1
右子树在前序遍历中的开始位置:pStart + 1 + rIndex - iStart
右子树在前序遍历中的结束位置:pEnd
右子树在中序遍历中的开始位置:rIndex + 1
右子树在中序遍历中的结束位置:iEnd
代码实现:
class Solution {
// map存储中序中节点值与下标的映射关系,方便获取根节点在中序中的下标
public HashMap<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int [] pre,int [] in) {
for (int i = 0; i <= in.length - 1; i++) {
map.put(in[i],i);
}
return dfs(pre, 0, pre.length - 1, 0, in.length - 1);
}
public TreeNode dfs(int[] preOrder, int pStart, int pEnd, int iStart, int iEnd) {
if (pStart > pEnd) {
return null;
}
TreeNode node = new TreeNode(preOrder[pStart]);
int rIndex = map.get(node.val);
TreeNode left = dfs(preOrder, pStart + 1, pStart + rIndex - iStart, iStart, rIndex - 1);
TreeNode right = dfs(preOrder, pStart + 1 + rIndex - iStart, pEnd, rIndex + 1, iStart);
node.left = left;
node.right = right;
return node;
}
}
二、给定中序和后续遍历,重构二叉树
leetcode 106号题
题目: 根据一棵二叉树的中序和后序遍历,重构出这棵树。树中没有重复元素
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
题解:
这个题和上一题其实是一样的思路:
- 后序遍历是根左右的顺序,中序遍历是左根右的顺序;
- 后序遍历的最后一个元素必然是整棵树的根节点,知道根节点之后我们就可以依据中序遍历切分出左右子树;
- 我们在中序序列中分割好了左右子树之后,我们再找到左右子树在后序遍历和中序遍历中的起始位置,这样就可以把我们找到的左右子树再作为一棵单独的树来进行处理;
- 重复2,3两个步骤
还是定义几个变量:
- iStart(中序的起始位置下标)
- iEnd(中序的结束位置下标)
- pStart(后序的起始位置下标)
- pEnd(后序的结束位置下标)
- rIndex(根节点在中序中的下标)
还是和上面一样,我们先看一下这几个值的变化:
根据图中的标注,我们也可以得出以下信息:
1. 左子树的长度:rIndex-iStart
2. 右子树的长度:iEnd-rIndex +1
左子树在后序遍历中的开始位置:pStart
左子树在后序遍历中的结束位置:pStart + rIndex-iStart - 1
左子树在中序遍历中的开始位置:iStart
左子树在中序遍历中的结束位置:rIndex- 1
右子树在后序遍历中的开始位置:pStart+ rIndex - iStart
右子树在后序遍历中的结束位置:pEnd - 1
右子树在中序遍历中的开始位置:rIndex + 1
右子树在中序遍历中的结束位置:iEnd
代码实现:
class Solution {
Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return help(postorder, 0, inorder.length - 1, 0, postorder.length - 1);
}
public TreeNode help(int[] post, int iStart, int iEnd, int pStart, int pEnd) {
if (pStart > pEnd) return null;
int rootVal = post[pEnd];
int rIndex = map.get(rootVal);
TreeNode node = new TreeNode(rootVal);
node.left = help(post, iStart, rIndex - 1, pStart, pStart + rIndex - iStart - 1);
node.right = help(post, rIndex + 1, iEnd, pStart + rIndex - iStart , pEnd - 1);
return node;
}
}
这两道题的思路几乎是一致的,主要是要想明白前序和中序、中序和后序有什么关联。找到它们之间的关系,这两个题就解决了。
期待大家批评指正