预备知识:
- stoi() 函数的用法;
- DFS中序遍历二叉树;
- 用结构体数组存储二叉树。
题目翻译:
*写一个算法,反转二叉树(交换二叉树结点的的左右子树)
输入格式:
每个输入文件包含一个测试用例。对于每个用例,第一行给出一个正整数N(≤10),代表树中节点的总数(节点的编号从0到N−1)。接下来N行,每一行对应于从0到N−1的一个节点,并给出该节点的左、右子节点的编号。如果子节点不存在,则用 “-” 表示。每一个结点编号用空格隔开。
输出格式:
对于每个测试用例,在第一行打印级树的层序遍历序列,在第二行打印树的中序遍历序列。相邻数字用空格隔开,行尾不能有多余的空格。
输入样例:
8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6
输出样例:
3 7 2 6 4 0 5 1
6 5 7 4 3 2 0 1
题目解析:
考察二叉树的层序遍历和中序遍历
- 处理输入并建树(要注意这里输入的值可能是数字可能是字符 “-” )
a. 二叉树的反转可以在此处进行,将输入的左右子结点调换位置储存即可,之后的层序和中序遍历按常规进行即可。
b. 也可在后面输出的时候再进行反转。 - 寻找根结点(题目根结点未给出)
a. 因为编号是从0到N-1,所以只要将所有出现过的编号都记录一遍,最后没有出现的数字即为树的根结点的编号。 - 中序遍历
a. 若在建树时候已经进行反转操作,此处正常进行遍历(左根右)。
b. 若建树时没有进行反转操作,此处遍历时需要调换访问左右子树位置(右根左)。 - 层序遍历(根据数组下标和层次排序)
a. 若在建树时候已经进行反转操作,此处排序规则:层数小的排前面,相同层数时,数组下标小的排前面,排序后的结果即为反转后二叉树的层序遍历。
b. 若建树时没有进行反转操作,此处排序规则:层数小的排前面,相同层数时,数组下标大的排前面,排序后的结果即为反转后二叉树的层序遍历。 - 输出结果
逻辑梳理:
-
采用基本方法(结构体-数组)建树。结点所在数组下标和结点层级需要在层序遍历的时候用到:
// 二叉树结构体 struct node { // 参数:结点编号 左孩子编号 右孩子编号 结点所在数组下标 结点层级 int id, l, r, index, level; } a[100];
-
处理输入并建树,先存右孩子,后存左孩子,反转建立二叉树:
// n结点总数 hava记录结点是否出现 root根结点下标 int n, have[100] = {0}, root = 0; cin >> n; for (int i = 0; i < n; i++) { a[i].id = i; // 当前结点的编号 string l, r; cin >> l >> r; if (l != "-") { // 若左孩子存在 a[i].l = stoi(l); // 将l转换为整形并赋值给a的左孩子 have[stoi(l)] = 1; // 记录结点已出现 } else { a[i].l = -1; // 左孩子不存在置为-1 } if (r != "-") { // 若右孩子存在 a[i].r = stoi(r); // // 将r转换为整形并赋值给a的右孩子 have[stoi(r)] = 1; // 记录结点已出现 } else { a[i].r = -1; // 右孩子不存在置为-1 } }
-
寻找根结点:0到N-1的下标中,未出现过的下标即位根结点下标。
while (have[root] == 1) root++; //找到根节点
-
dfs中序遍历:将结果存到v1数组中,此处还需要进行二叉树的反转操作,交换左右的访问次序即可(右-根-左)。
vector<node> v1; void dfs(int root, int index, int level) { if (a[root].r != -1) dfs(a[root].r, index * 2 + 2, level + 1); v1.push_back({root, 0, 0, index, level}); if (a[root].l != -1) dfs(a[root].l, index * 2 + 1, level + 1); }
-
层序遍历:此处用了一个巧妙的方法,直接对结点所在数组中的下标进行排序:层数小的排前面,相同层数时,index(结点在数组中的下标)大的排前面。 排序后的结果即为反转后二叉树的层序遍历。
// 层数小的排前面,相同层数时,index大的排前面 // 排序后的结果即为反转后二叉树的层序遍历 bool cmp(node a, node b) { if (a.level != b.level) return a.level < b.level; return a.index > b.index; }
-
最后遍历输出即可,注意行首和行尾不要有空格。
参考代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 二叉树结构体
struct node {
// 参数:结点编号 左孩子编号 右孩子编号 结点所在数组下标 结点层级
int id, l, r, index, level;
} a[100];
vector<node> v1; // 深度遍历,将结果存到v1数组中
//dfs参数:当前结点编号 当前结点下标 当前结点所在层级
void dfs(int root, int index, int level) {
// 右子递归
if (a[root].r != -1) dfs(a[root].r, index * 2 + 2, level + 1);
// 访问当前结点
v1.push_back({root, 0, 0, index, level});
// 左子递归
if (a[root].l != -1) dfs(a[root].l, index * 2 + 1, level + 1);
}
// 层数小的排前面,相同层数时,index大的排前面
// 排序后的结果即为反转后二叉树的层序遍历
bool cmp(node a, node b) {
if (a.level != b.level) return a.level < b.level;
return a.index > b.index;
}
int main() {
// n结点总数 hava记录结点是否出现 root根结点下标
int n, have[100] = {0}, root = 0;
cin >> n;
for (int i = 0; i < n; i++) {
a[i].id = i; // 当前结点的编号
string l, r;
cin >> l >> r;
if (l != "-") { // 若左孩子存在
a[i].l = stoi(l); // 将l转换为整形并赋值给a的左孩子
have[stoi(l)] = 1; // 记录结点已出现
} else {
a[i].l = -1; // 左孩子不存在置为-1
}
if (r != "-") { // 若右孩子存在
a[i].r = stoi(r); // // 将r转换为整形并赋值给a的右孩子
have[stoi(r)] = 1; // 记录结点已出现
} else {
a[i].r = -1; // 右孩子不存在置为-1
}
}
while (have[root] == 1) root++; //找到根节点
dfs(root, 0, 0);
vector<node> v2(v1); // 复制v1到v2
sort(v2.begin(), v2.end(), cmp); // 按数组下标排序,排序后的结果既为层序遍历
for (int i = 0; i < v2.size(); i++) {
if (i != 0) cout << " ";
cout << v2[i].id;
}
cout << endl;
for (int i = 0; i < v1.size(); i++) {
if (i != 0) cout << " ";
cout << v1[i].id;
}
return 0;
}
全部通过