先序遍历:对任一子树,先访问根,然后遍历其左子树,最后遍历其右子树。
中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树。
后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。
若知道先序遍历结果,那么第一个元素即是根节点
若知道后序遍历结果,那么最后一个元素即是根节点
中序遍历序列可以根据根节点位置可以得到左子树的中序遍历序列和右子树的中序遍历序列
前序遍历序列可以根据根节点和左子树的中序遍历序列元素数量得到左子树的前序遍历序列和右子树的前序遍历序列
比如二叉树:
3
/ \
9 20
/ \ / \
6 8 15 7
前序遍历序列:[3, 9, 6, 8, 20, 15, 7]
中序遍历序列:[6, 9, 8, 3, 15, 20, 7]
那么根节点就是前序遍历的第一个元素:3
左子树的中序遍历序列就是中序遍历序列根节点元素左侧部分:[6, 9, 8]
右子树的中序遍历序列就是中序遍历序列根节点元素右侧部分:[15, 20, 7]
左子树的前序遍历序列就是前序遍历序列根节点元素右侧3个元素:[9, 6, 8]
右子树的前序遍历序列就是前序遍历序列中左子树的前序遍历序列后边部分:[20, 15, 7]
即
前序遍历序列:[根节点,[左子树的前序遍历序列],[右子树的前序遍历序列]]
中序遍历序列:[[左子树的中序遍历序列],根节点,[右子树的中序遍历序列]]
算法框架
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 找到根节点
int rootValue = ?;
// 找到根节点在中序遍历序列中的索引位置
int rootIndex = ?;
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 得到构建左子树的前序遍历序列
int[] leftPreorder = ?;
// 得到构建左子树的中序遍历序列
int[] leftInorder = ?;
// 构建左子树
root.left = buildTree(leftPreorder, leftInorder);
// 得到构建右子树的前序遍历序列
int[] rightPreorder = ?;
// 得到构建右子树的中序遍历序列
int[] rightInorder = ?;
// 构建右子树
root.right = buildTree(rightPreorder, rightInorder);
return root;
}
根节点就是前序遍历序列第一个元素:
int rootValue = preorder[0];
找到根节点在中序遍历序列中的索引位置:
int rootIndex = -1;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == rootValue) {
rootIndex = i;
}
}
若要构建左子树,则需要先得到左子树的前序遍历序列和中序遍历序列
若要构建右子树,则需要先得到右子树的前序遍历序列和中序遍历序列
初始化四个数组,分别代表左子树的前序遍历序列和中序遍历序列以及右子树的前序遍历序列和中序遍历序列
int[] leftProrder = new int[];
int[] leftInorder = new int[];
int[] rightProrder = new int[];
int[] rightInorder = new int[];
可是数组初始化需要确定数组长度,即元素个数
左子树的元素个数即中序遍历序列根节点左侧的元素个数,值等于 根节点的索引值
右子树的元素个数即中序遍历序列根节点右侧的元素个数,值等于 中序遍历序列的元素数量 - 左子树元素数量 - 根节点 ,也等于 中序遍历序列最大索引值 - 根节点索引值
// 左子树节点数量
int leftSize = rootIndex;
// 右子树节点数量
int rightSize = inorder.length - 1 - rootIndex;
左子树的中序遍历序列就是中序遍历序列根节点元素左侧部分:
for (int i = 0, j = 0; i < leftSize; i++, j++) {
// 从 inorder 第 0 个元素开始,取 leftSize 个元素
leftInorder[j] = inorder[i];
}
右子树的中序遍历序列就是中序遍历序列根节点元素右侧部分:
for (int i = rootIndex + 1, j = 0; i < inorder.length; i++, j++) {
// 从 inorder 第 rootIndex + 1 元素开始,直至结束
rightInorder[j] = inorder[i];
}
左子树的前序遍历序列就是前序遍历序列根节点元素右侧左子树元素总数数量的N个元素:
for (int i = 1, j = 0; i <= leftSize; i++, j++) {
// 从 preorder 第 2 个元素开始,取 leftSize 个元素
leftPreorder[j] = preorder[i];
}
右子树的前序遍历序列就是前序遍历序列中左子树的前序遍历序列后边部分:
for (int i = rootIndex + 1, j = 0; i < preorder.length; i++, j++) {
// 从 preorder 第 rootIndex + 1 元素开始,直至结束
rightPreorder[j] = preorder[i];
}
算法框架
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 找到根节点
int rootValue = preorder[0];;
// 找到根节点在中序遍历序列中的索引位置
int rootIndex = -1;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == rootValue) {
rootIndex = i;
}
}
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 计算左子树节点数量
int leftSize = rootIndex;
// 计算右子树节点数量
int rightSize = inorder.length - 1 - rootIndex;
// 得到构建左子树的前序遍历序列
int[] leftPreorder = new int[leftSize];
for (int i = 1, j = 0; i <= leftSize; i++, j++) {
// 从 preorder 第 2 个元素开始,取 leftSize 个元素
leftPreorder[j] = preorder[i];
}
// 得到构建左子树的中序遍历序列
int[] leftInorder = new int[leftSize];
for (int i = 0, j = 0; i < leftSize; i++, j++) {
// 从 inorder 第 0 个元素开始,取 leftSize 个元素
leftInorder[j] = inorder[i];
}
// 构建左子树
root.left = buildTree(leftPreorder, leftInorder);
// 得到构建右子树的前序遍历序列
int[] rightPreorder = new int[rightSize];
for (int i = rootIndex + 1, j = 0; i < preorder.length; i++, j++) {
// 从 preorder 第 rootIndex + 1 元素开始,直至结束
rightPreorder[j] = preorder[i];
}
// 得到构建右子树的中序遍历序列
int[] rightInorder = new int[rightSize];
for (int i = rootIndex + 1, j = 0; i < inorder.length; i++, j++) {
// 从 inorder 第 rootIndex + 1 元素开始,直至结束
rightInorder[j] = inorder[i];
}
// 构建右子树
root.right = buildTree(rightPreorder, rightInorder);
return root;
}
考虑边界条件:
1)preorder 或者 inorder 为空,则直接返回 null
2)preorder 和 inorder 都只有一个节点,则直接返回根节点即可
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || inorder == null) {
return null;
}
if (preorder.length == 1 && inorder.length == 1) {
return new TreeNode(preorder[0]);
}
// 找到根节点
int rootValue = preorder[0];;
// 找到根节点在中序遍历序列中的索引位置
int rootIndex = -1;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == rootValue) {
rootIndex = i;
}
}
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 计算左子树节点数量
int leftSize = rootIndex;
// 计算右子树节点数量
int rightSize = inorder.length - 1 - rootIndex;
// 得到构建左子树的前序遍历序列
int[] leftPreorder = new int[leftSize];
for (int i = 1, j = 0; i <= leftSize; i++, j++) {
// 从 preorder 第 2 个元素开始,取 leftSize 个元素
leftPreorder[j] = preorder[i];
}
// 得到构建左子树的中序遍历序列
int[] leftInorder = new int[leftSize];
for (int i = 0, j = 0; i < leftSize; i++, j++) {
// 从 inorder 第 0 个元素开始,取 leftSize 个元素
leftInorder[j] = inorder[i];
}
// 构建左子树
root.left = buildTree(leftPreorder, leftInorder);
// 得到构建右子树的前序遍历序列
int[] rightPreorder = new int[rightSize];
for (int i = rootIndex + 1, j = 0; i < preorder.length; i++, j++) {
// 从 preorder 第 rootIndex + 1 元素开始,直至结束
rightPreorder[j] = preorder[i];
}
// 得到构建右子树的中序遍历序列
int[] rightInorder = new int[rightSize];
for (int i = rootIndex + 1, j = 0; i < inorder.length; i++, j++) {
// 从 inorder 第 rootIndex + 1 元素开始,直至结束
rightInorder[j] = inorder[i];
}
// 构建右子树
root.right = buildTree(rightPreorder, rightInorder);
return root;
}
提交算法时发现未通过,有测试用例:preorder = [1, 2] inorder = [2, 1],只有根节点和左子树
1
/
2
同理可能还存在只有根节点和右子树的情况:
1
\
2
需要对左子树节点数量和右子树节点数量判空一下
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || inorder == null) {
return null;
}
if (preorder.length == 1 && inorder.length == 1) {
return new TreeNode(preorder[0]);
}
// 找到根节点
int rootValue = preorder[0];;
// 找到根节点在中序遍历序列中的索引位置
int rootIndex = -1;
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == rootValue) {
rootIndex = i;
}
}
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 计算左子树节点数量
int leftSize = rootIndex;
// 计算右子树节点数量
int rightSize = inorder.length - 1 - rootIndex;
if (leftSize > 0) {
// 得到构建左子树的前序遍历序列
int[] leftPreorder = new int[leftSize];
for (int i = 1, j = 0; i <= leftSize; i++, j++) {
// 从 preorder 第 2 个元素开始,取 leftSize 个元素
leftPreorder[j] = preorder[i];
}
// 得到构建左子树的中序遍历序列
int[] leftInorder = new int[leftSize];
for (int i = 0, j = 0; i < leftSize; i++, j++) {
// 从 inorder 第 0 个元素开始,取 leftSize 个元素
leftInorder[j] = inorder[i];
}
// 构建左子树
root.left = buildTree(leftPreorder, leftInorder);
}
if(rightSize > 0) {
// 得到构建右子树的前序遍历序列
int[] rightPreorder = new int[rightSize];
for (int i = rootIndex + 1, j = 0; i < preorder.length; i++, j++) {
// 从 preorder 第 rootIndex + 1 元素开始,直至结束
rightPreorder[j] = preorder[i];
}
// 得到构建右子树的中序遍历序列
int[] rightInorder = new int[rightSize];
for (int i = rootIndex + 1, j = 0; i < inorder.length; i++, j++) {
// 从 inorder 第 rootIndex + 1 元素开始,直至结束
rightInorder[j] = inorder[i];
}
// 构建右子树
root.right = buildTree(rightPreorder, rightInorder);
}
return root;
}
再次提交算法,测试通过
实际上要构建一棵二叉树,只要知道如何构建根节点,如何构建左子树,如何构建右子树即可构建一棵二叉树
而构建左子树和构建右子树和构建树是一样的,只需要递归调用构建树方法即可
public TreeNode buildTree() {
// 找到根节点的值
int rootValue = ?;
// 找到根节点的索引
int rootIndex = ?;
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 构建左子树
root.left = buildTree();
// 构建右子树
root.right = buildTree();
return root;
}
根据题意只要能确定前序遍历序列和中序遍历序列,即可最终构建出一棵二叉树
只要知道前序遍历序列左侧起始索引和前序遍历序列右侧起始索引即可确定前序遍历序列
只要知道中序遍历序列左侧起始索引和中序遍历序列右侧起始索引即可确定中序遍历序列
public TreeNode buildTree(int[] preorder, int[] inorder,
int preLeftIndex, int preRightIndex,
int inLeftIndex, int inRightIndex) {
// 找到根节点的值
int rootValue = ?;
// 找到根节点的索引
int rootIndex = ?;
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 构建左子树
root.left = buildTree(preorder, inorder, ?, ?, ?, ?);
// 构建右子树
root.right = buildTree(preorder, inorder, ?, ?, ?, ?);
return root;
}
根节点的值即是初始前序遍历序列中指定的子前序遍历序列起始索引位置的元素值:int rootValue = preorder[preLeftIndex]
左子树的前序遍历序列左侧起始索引位置即 preLeftIndex + 1
左子树的前序遍历序列右侧结束索引位置即 preLeftIndex + 左子树节点数量 = preLeftIndex + rootIndex - inLeftIndex
左子树的中序遍历序列左侧起始索引位置即 inLeftIndex
左子树的中序遍历序列结束索引位置等于 inLeftIndex + 左子树节点数量 - 1 = inLeftIndex + rootIndex - inLeftIndex = rootIndex - 1
右子树的前序遍历序列左侧起始索引位置等于 preLeftIndex + 左子树节点数量 + 1 = preLeftIndex + rootIndex - inLeftIndex + 1
右子树的前序遍历序列右侧结束索引位置等于 preRightIndex
右子树的中序遍历序列左侧起始索引位置等于 rootIndex + 1
右子树的中序遍历序列右侧结束索引位置等于 inRightIndex
public TreeNode buildTree(int[] preorder, int[] inorder,
int preLeftIndex, int preRightIndex,
int inLeftIndex, int inRightIndex,
HashMap<Integer, Integer> valueIndexMap) {
if (preLeftIndex > preRightIndex) {
return null;
}
// 找到根节点的值
int rootValue = preorder[preLeftIndex];
// 找到根节点的索引
int rootIndex = valueIndexMap.get(rootValue);
// 左子树节点数量
int leftSize = rootIndex - inLeftIndex;
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 左子树前序遍历序列起始索引
int leftNextPreLeftIndex = preLeftIndex + 1;
// 左子树前序遍历序列结束索引
int leftNextPreRightIndex = preLeftIndex + leftSize;
// 左子树中序遍历序列起始索引
int leftNextInLeftIndex = inLeftIndex;
// 左子树中序遍历序列结束索引
int leftNextInRightIndex = inLeftIndex + leftSize;
// 构建左子树
root.left = buildTree(preorder, inorder,
leftNextPreLeftIndex, leftNextPreRightIndex,
leftNextInLeftIndex, leftNextInRightIndex,
valueIndexMap);
// 右子树前序遍历序列起始索引
int rightNextPreLeftIndex = preLeftIndex + leftSize + 1;
// 右子树前序遍历序列结束索引
int rightNextPreRightIndex = preRightIndex;
// 右子树中序遍历序列起始索引
int rightNextInLeftIndex = rootIndex + 1;
// 右子树中序遍历序列结束索引
int rightNextInRightIndex = inRightIndex;
// 构建右子树
root.right = buildTree(preorder, inorder,
rightNextPreLeftIndex, rightNextPreRightIndex,
rightNextInLeftIndex, rightNextInRightIndex,
valueIndexMap);
return root;
}
那怎么找到根节点的索引呢?在知道根节点的值的情况下,可以反向从原始中序遍历序列中定位到索引值,需要提前维护一下值和索引的映射关系
public TreeNode buildTree(int[] preorder, int[] inorder) {
HashMap<Integer, Integer> valueIndexMap = new HashMap();
for(int i = 0; i < inorder.length; i++) {
valueIndexMap.put(inorder[i], i);
}
return buildTree(preorder, inorder,
0, preorder.length - 1,
0, inorder.length - 1,
valueIndexMap);
}
public TreeNode buildTree(int[] preorder, int[] inorder,
int preLeftIndex, int preRightIndex,
int inLeftIndex, int inRightIndex,
HashMap<Integer, Integer> valueIndexMap) {
if (preLeftIndex > preRightIndex) {
return null;
}
// 找到根节点的值
int rootValue = preorder[preLeftIndex];
// 找到根节点的索引
int rootIndex = valueIndexMap.get(rootValue);
// 左子树节点数量
int leftSize = rootIndex - inLeftIndex;
// 构建根节点
TreeNode root = new TreeNode(rootValue);
// 左子树前序遍历序列起始索引
int leftNextPreLeftIndex = preLeftIndex + 1;
// 左子树前序遍历序列结束索引
int leftNextPreRightIndex = preLeftIndex + leftSize;
// 左子树中序遍历序列起始索引
int leftNextInLeftIndex = inLeftIndex;
// 左子树中序遍历序列结束索引
int leftNextInRightIndex = inLeftIndex + leftSize;
// 构建左子树
root.left = buildTree(preorder, inorder,
leftNextPreLeftIndex, leftNextPreRightIndex,
leftNextInLeftIndex, leftNextInRightIndex,
valueIndexMap);
// 右子树前序遍历序列起始索引
int rightNextPreLeftIndex = preLeftIndex + leftSize + 1;
// 右子树前序遍历序列结束索引
int rightNextPreRightIndex = preRightIndex;
// 右子树中序遍历序列起始索引
int rightNextInLeftIndex = rootIndex + 1;
// 右子树中序遍历序列结束索引
int rightNextInRightIndex = inRightIndex;
// 构建右子树
root.right = buildTree(preorder, inorder,
rightNextPreLeftIndex, rightNextPreRightIndex,
rightNextInLeftIndex, rightNextInRightIndex,
valueIndexMap);
return root;
}