题目链接
题目描述
给定一棵二叉树的层序遍历序列和中序遍历序列,求这棵二叉树的:
-
叶子节点(按照从左到右的顺序)。
-
先序遍历序列。
-
后序遍历序列。
解题思路
这个问题的核心是根据层序遍历和中序遍历序列来重建二叉树。一旦树被成功重建,获取叶子节点、先序遍历和后序遍历就都是常规的树遍历操作了。
重建二叉树的步骤:
-
确定根节点:
在层序遍历序列中,第一个元素永远是整棵树(或当前子树)的根节点。
-
划分左右子树:
找到这个根节点在中序遍历序列中的位置。中序遍历的特性是
[左子树的所有节点, 根节点, 右子树的所有节点]
。因此,该位置左边的所有元素都属于根节点的左子树,右边的所有元素都属于根节点的右子树。 -
构建子树的遍历序列:
-
我们已经根据中序遍历划分了左右子树的节点集合。
-
现在,我们需要为左右子树分别找出它们自己的层序遍历序列。这可以通过遍历原始的层序遍历序列(跳过第一个根节点)来完成。对于序列中的每一个节点,我们检查它是否属于左子树的节点集合(可以预先将左子树的节点存入一个哈希表中以便快速查找)。如果是,就将其加入左子树的层序遍历序列;否则,加入右子树的层序遍历序列。
-
-
递归构建: 我们现在有了左子树的层序和中序序列,以及右子树的层序和中序序列。我们可以递归地调用相同的构建函数,来分别构建左子树和右子树,并将它们连接到根节点的
left
和right
指针上。
递归终止条件:当中序遍历序列为空时,说明当前子树为空,返回 nullptr
。
为了提高效率,我们可以预先将中序遍历序列的 (值 -> 索引)
关系存入一个哈希表(map
),这样在第 2 步中就可以 时间找到根节点的位置。
重建后的操作:
-
获取叶子节点:对重建后的树进行一次先序(或任何顺序)遍历。如果一个节点的左子节点和右子节点都为空,那么它就是叶子节点。
-
先序遍历:执行标准的
根 -> 左 -> 右
遍历。 -
后序遍历:执行标准的
左 -> 右 -> 根
遍历。
代码
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 存放三种遍历结果的辅助结构体
struct TraversalResults {
vector<int> leaves;
vector<int> preorder;
vector<int> postorder;
};
// 递归构建二叉树
TreeNode* buildTree(const vector<int>& levelorder, const vector<int>& inorder) {
if (levelorder.empty()) {
return nullptr;
}
int rootVal = levelorder[0];
TreeNode* root = new TreeNode(rootVal);
auto it = find(inorder.begin(), inorder.end(), rootVal);
int rootIndex = distance(inorder.begin(), it);
vector<int> leftInorder(inorder.begin(), inorder.begin() + rootIndex);
vector<int> rightInorder(inorder.begin() + rootIndex + 1, inorder.end());
unordered_set<int> leftInorderSet(leftInorder.begin(), leftInorder.end());
vector<int> leftLevelorder, rightLevelorder;
for (size_t i = 1; i < levelorder.size(); ++i) {
if (leftInorderSet.count(levelorder[i])) {
leftLevelorder.push_back(levelorder[i]);
} else {
rightLevelorder.push_back(levelorder[i]);
}
}
root->left = buildTree(leftLevelorder, leftInorder);
root->right = buildTree(rightLevelorder, rightInorder);
return root;
}
// 遍历并收集结果
void traverse(TreeNode* root, TraversalResults& results) {
if (!root) {
return;
}
// Preorder
results.preorder.push_back(root->val);
// Leaves
if (!root->left && !root->right) {
results.leaves.push_back(root->val);
}
traverse(root->left, results);
traverse(root->right, results);
// Postorder
results.postorder.push_back(root->val);
}
void printVector(const vector<int>& vec) {
for (size_t i = 0; i < vec.size(); ++i) {
cout << vec[i] << (i == vec.size() - 1 ? "" : " ");
}
cout << endl;
}
vector<int> readVector(const string& line) {
vector<int> vec;
stringstream ss(line);
int num;
while (ss >> num) {
vec.push_back(num);
}
return vec;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
string level_line, in_line;
getline(cin, level_line);
getline(cin, in_line);
vector<int> levelorder = readVector(level_line);
vector<int> inorder = readVector(in_line);
TreeNode* root = buildTree(levelorder, inorder);
TraversalResults results;
traverse(root, results);
printVector(results.leaves);
printVector(results.preorder);
printVector(results.postorder);
return 0;
}
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
class TraversalResults {
List<Integer> leaves = new ArrayList<>();
List<Integer> preorder = new ArrayList<>();
List<Integer> postorder = new ArrayList<>();
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String levelLine = sc.nextLine();
String inLine = sc.nextLine();
List<Integer> levelorder = parseLine(levelLine);
List<Integer> inorder = parseLine(inLine);
TreeNode root = buildTree(levelorder, inorder);
TraversalResults results = new TraversalResults();
traverse(root, results);
printList(results.leaves);
printList(results.preorder);
printList(results.postorder);
}
private static List<Integer> parseLine(String line) {
List<Integer> list = new ArrayList<>();
if (line.isEmpty()) return list;
String[] parts = line.split(" ");
for (String part : parts) {
list.add(Integer.parseInt(part));
}
return list;
}
private static void printList(List<Integer> list) {
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + (i == list.size() - 1 ? "" : " "));
}
System.out.println();
}
private static TreeNode buildTree(List<Integer> levelorder, List<Integer> inorder) {
if (levelorder.isEmpty()) {
return null;
}
int rootVal = levelorder.get(0);
TreeNode root = new TreeNode(rootVal);
int rootIndex = inorder.indexOf(rootVal);
List<Integer> leftInorder = inorder.subList(0, rootIndex);
List<Integer> rightInorder = inorder.subList(rootIndex + 1, inorder.size());
Set<Integer> leftInorderSet = new HashSet<>(leftInorder);
List<Integer> leftLevelorder = new ArrayList<>();
List<Integer> rightLevelorder = new ArrayList<>();
for (int i = 1; i < levelorder.size(); i++) {
int val = levelorder.get(i);
if (leftInorderSet.contains(val)) {
leftLevelorder.add(val);
} else {
rightLevelorder.add(val);
}
}
root.left = buildTree(leftLevelorder, leftInorder);
root.right = buildTree(rightLevelorder, rightInorder);
return root;
}
private static void traverse(TreeNode root, TraversalResults results) {
if (root == null) {
return;
}
// Preorder
results.preorder.add(root.val);
// Leaves
if (root.left == null && root.right == null) {
results.leaves.add(root.val);
}
traverse(root.left, results);
traverse(root.right, results);
// Postorder
results.postorder.add(root.val);
}
}
import sys
from collections import deque
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def build_tree(levelorder, inorder):
if not levelorder:
return None
root_val = levelorder[0]
root = TreeNode(root_val)
try:
root_index = inorder.index(root_val)
except ValueError:
return None
left_inorder = inorder[:root_index]
right_inorder = inorder[root_index+1:]
left_inorder_set = set(left_inorder)
left_levelorder = []
right_levelorder = []
for val in levelorder[1:]:
if val in left_inorder_set:
left_levelorder.append(val)
else:
right_levelorder.append(val)
root.left = build_tree(left_levelorder, left_inorder)
root.right = build_tree(right_levelorder, right_inorder)
return root
def get_traversals(root):
leaves = []
preorder = []
postorder = []
def traverse(node):
if not node:
return
# Preorder
preorder.append(node.val)
# Leaves
if not node.left and not node.right:
leaves.append(node.val)
traverse(node.left)
traverse(node.right)
# Postorder
postorder.append(node.val)
traverse(root)
return leaves, preorder, postorder
def main():
try:
level_line = sys.stdin.readline().strip()
in_line = sys.stdin.readline().strip()
if not level_line or not in_line:
return
levelorder = list(map(int, level_line.split()))
inorder = list(map(int, in_line.split()))
root = build_tree(levelorder, inorder)
leaves, preorder, postorder = get_traversals(root)
print(" ".join(map(str, leaves)))
print(" ".join(map(str, preorder)))
print(" ".join(map(str, postorder)))
except (IOError, ValueError):
return
if __name__ == "__main__":
main()
算法及复杂度
-
算法:二叉树、递归、分治
-
时间复杂度:
,其中
是二叉树的节点数。在
buildTree
函数的每一次递归调用中,我们都需要遍历层序序列来划分左右子树的层序序列,这个操作是,其中
是当前子树的节点数。对于一个不平衡的树(链状),递归深度为
,总复杂度会达到
。对于平衡树,复杂度会好一些,约为
。重建树后,三次遍历的时间复杂度都是
。
-
空间复杂度:
。在最坏情况下(链状树),递归的每一层都需要创建新的
levelorder
和inorder
列表/向量,总的空间消耗会达到。同时,递归栈的深度也可能达到
。可以通过传递索引而非创建新数组来将空间复杂度优化到
。