树的基本概念
概念讲解
什么是树?
树是一种非线性数据结构,由节点和边组成,具有以下特点:
- 有且仅有一个根节点
- 除根节点外,每个节点有且仅有一个父节点
- 没有环
基本术语
| 术语 | 说明 |
|---|---|
| 节点 | 树中的元素 |
| 根节点 | 没有父节点的节点 |
| 叶节点 | 没有子节点的节点 |
| 度 | 节点的子节点个数 |
| 深度 | 从根节点到该节点的边数 |
| 高度 | 从该节点到最远叶节点的边数 |
| 层 | 深度 + 1 |
二叉树
定义
二叉树是每个节点最多有两个子节点的树,分别称为左子节点和右子节点。
存储方式
链式存储
1234567struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};数组存储(完全二叉树)
1234int tree[100];
// 节点 i 的子节点:2*i, 2*i+1
// 节点 i 的父节点:i/2二叉树遍历
前序遍历(根-左-右)
1234567void preorder(TreeNode* root) {
if (root == nullptr) return;
cout << root->val << " "; // 访问根节点
preorder(root->left); // 遍历左子树
preorder(root->right); // 遍历右子树
}中序遍历(左-根-右)
1234567void inorder(TreeNode* root) {
if (root == nullptr) return;
inorder(root->left); // 遍历左子树
cout << root->val << " "; // 访问根节点
inorder(root->right); // 遍历右子树
}后序遍历(左-右-根)
1234567void postorder(TreeNode* root) {
if (root == nullptr) return;
postorder(root->left); // 遍历左子树
postorder(root->right); // 遍历右子树
cout << root->val << " "; // 访问根节点
}层序遍历(BFS)
12345678910111213141516void levelorder(TreeNode* root) {
if (root == nullptr) return;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
cout << node->val << " ";
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}特殊二叉树
满二叉树
每个节点都有 0 或 2 个子节点。
完全二叉树
除最后一层外,其他层都是满的,且最后一层的节点都靠左。
性质
- 第 i 层最多有 2^(i-1) 个节点
- 深度为 k 的树最多有 2^k - 1 个节点
- 叶节点数 = 度为 2 的节点数 + 1
考点分析
初赛考查方式
- 选择题: 树的基本概念
- 程序阅读: 理解遍历过程
- 填空题: 根据遍历序列还原树
常见题型
- 遍历序列还原
- 树的性质
- 层序遍历
例题精讲
例题 1:遍历序列
题目: 已知前序遍历 [1,2,4,5,3],中序遍历 [4,2,5,1,3],求后序遍历。
解答:
前序:1 2 4 5 3
中序:4 2 5 1 3
根节点:1(前序第一个)
左子树:前序 [2,4,5],中序 [4,2,5]
右子树:前序 [3],中序 [3]
后序:4 5 2 3 1例题 2:层序遍历
题目: 二叉树的层序遍历。
解答:
123456789101112131415161718192021222324252627282930313233#include <iostream>
#include <queue>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
void levelorder(TreeNode* root) {
if (root == nullptr) return;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode* node = q.front();
q.pop();
cout << node->val << " ";
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
cout << endl;
}
}练习题
- 洛谷 P1305 新二叉树
查看答案
思路:用数组存储树,然后前序遍历。
12345678910111213141516171819202122232425262728293031#include <iostream>
using namespace std;
char leftChild[256], rightChild[256];
void preorder(char root) {
if (root == '*') return;
cout << root;
preorder(leftChild[root]);
preorder(rightChild[root]);
}
int main() {
int n;
cin >> n;
char root;
cin >> root;
for (int i = 1; i < n; i++) {
char node, l, r;
cin >> node >> l >> r;
leftChild[node] = l;
rightChild[node] = r;
}
preorder(root);
cout << endl;
return 0;
}- 洛谷 P1827 美国血统
查看答案
思路:已知中序和前序,求后序。
123456789101112131415161718192021222324252627#include <iostream>
#include <string>
using namespace std;
string inorder, preorder;
void postorder(int inStart, int inEnd, int preStart, int preEnd) {
if (inStart > inEnd) return;
char root = preorder[preStart];
int rootIdx = inorder.find(root, inStart);
int leftSize = rootIdx - inStart;
postorder(inStart, rootIdx - 1, preStart + 1, preStart + leftSize);
postorder(rootIdx + 1, inEnd, preStart + leftSize + 1, preEnd);
cout << root;
}
int main() {
cin >> inorder >> preorder;
int n = inorder.size();
postorder(0, n - 1, 0, n - 1);
cout << endl;
return 0;
}- 洛谷 P1229 遍历问题
查看答案
思路:已知前序和后序,求中序遍历的可能数量。
公式:
12345678910111213141516171819202122232425#include <iostream>
#include <string>
using namespace std;
int main() {
string pre, post;
cin >> pre >> post;
int count = 0;
for (int i = 0; i < pre.size() - 1; i++) {
for (int j = 1; j < post.size(); j++) {
if (pre[i] == post[j] && pre[i + 1] == post[j - 1]) {
count++;
}
}
}
int ans = 1;
for (int i = 0; i < count; i++) {
ans *= 2;
}
cout << ans << endl;
return 0;
}易错点
⚠️ 注意:
- 前序+中序可以唯一确定一棵二叉树
- 后序+中序可以唯一确定一棵二叉树
- 前序+后序不能唯一确定一棵二叉树
- 完全二叉树可以用数组存储