1102 Invert a Binary Tree (25 分) 全网最细 题目详解 翻译 完整代码 PAT甲级真题解析

预备知识:

  1. stoi() 函数的用法;
  2. DFS中序遍历二叉树;
  3. 用结构体数组存储二叉树。

题目翻译:

*写一个算法,反转二叉树(交换二叉树结点的的左右子树)

输入格式:

每个输入文件包含一个测试用例。对于每个用例,第一行给出一个正整数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

题目解析:

考察二叉树的层序遍历中序遍历

  1. 处理输入并建树(要注意这里输入的值可能是数字可能是字符 “-” )
    a. 二叉树的反转可以在此处进行,将输入的左右子结点调换位置储存即可,之后的层序和中序遍历按常规进行即可。
    b. 也可在后面输出的时候再进行反转。
  2. 寻找根结点(题目根结点未给出)
    a. 因为编号是从0到N-1,所以只要将所有出现过的编号都记录一遍,最后没有出现的数字即为树的根结点的编号。
  3. 中序遍历
    a. 若在建树时候已经进行反转操作,此处正常进行遍历(左根右)。
    b. 若建树时没有进行反转操作,此处遍历时需要调换访问左右子树位置(右根左)。
  4. 层序遍历(根据数组下标和层次排序)
    a. 若在建树时候已经进行反转操作,此处排序规则:层数小的排前面,相同层数时,数组下标小的排前面,排序后的结果即为反转后二叉树的层序遍历。
    b. 若建树时没有进行反转操作,此处排序规则:层数小的排前面,相同层数时,数组下标大的排前面,排序后的结果即为反转后二叉树的层序遍历。
  5. 输出结果

逻辑梳理:

  1. 采用基本方法(结构体-数组)建树。结点所在数组下标和结点层级需要在层序遍历的时候用到:

    // 二叉树结构体
    struct node {
        // 参数:结点编号 左孩子编号 右孩子编号 结点所在数组下标 结点层级
        int id, l, r, index, level;
    } a[100];
    
  2. 处理输入并建树,先存右孩子,后存左孩子,反转建立二叉树:

    // 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
        }
    }
    
  3. 寻找根结点:0到N-1的下标中,未出现过的下标即位根结点下标。

    while (have[root] == 1) root++; //找到根节点
    
  4. 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);
    }
    
  5. 层序遍历:此处用了一个巧妙的方法,直接对结点所在数组中的下标进行排序:层数小的排前面,相同层数时,index(结点在数组中的下标)大的排前面。 排序后的结果即为反转后二叉树的层序遍历。

    // 层数小的排前面,相同层数时,index大的排前面
    // 排序后的结果即为反转后二叉树的层序遍历
    bool cmp(node a, node b) {
        if (a.level != b.level) return a.level < b.level;
        return a.index > b.index;
    }
    
  6. 最后遍历输出即可,注意行首和行尾不要有空格。

参考代码:

#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;
}

全部通过
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值