【BUCTOJ】Contest2781 - 软件技术基础2022-第六次课堂作业-MF

问题 A: 算法6-1~6-4:二叉链表存储的二叉树

题面

题目描述

树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用。对于每一个结点至多只有两课子树的一类树,称其为二叉树。在本题中,将会给出一个按照先序遍历得出的字符串,空格代表空的子节点,大写字母代表节点内容。请通过这个字符串建立二叉树,并按照题目描述中的一种先序遍历和两种中序遍历的算法分别输出每一个非空节点。

输入

输入只有一行,包含一个字符串S,用来建立二叉树。保证S为合法的二叉树先序遍历字符串,节点内容只有大写字母,且S的长度不超过100。

输出

共有三行,每一行包含一串字符,表示分别按先序、中序、中序得出的节点内容,每个字母后输出一个空格。请注意行尾输出换行。

样例输入 

ABC  DE G  F   

样例输出 

A B C D E G F 
C B E G D F A 
C B E G D F A 

 题解

前置知识-拓展二叉树

由于先序、中序和后序序列中的任一个都不能唯一确定一棵二叉树,所以对二叉树做如下处理,将二叉树的空结点用一符号补齐(将其标记出来,本题中使用空格)。我们把这样处理后的二叉树称为原二叉树的扩展二叉树,扩展二叉树的先序和后序序列能唯一确定其二叉树。

如何根据拓展二叉树的前序遍历结果创建二叉树

我们采用递归的方式进行,首先读入拓展二叉树前序序列。然后递归创建左子树(优先)和右子树,这里递归函数应该是返回定义的二叉树的指针类型。由于是先序,所以每创建一个二叉树节点我们就将前序序列的指针加一。

AC代码

// Problem: A: 算法6-1~6-4:二叉链表存储的二叉树
// Contest: BUCTOJ
// URL: https://buctoj.com/problem.php?cid=2781&pid=0
// Memory Limit: 128 MB
// Time Limit: 1000 ms

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
#define PY puts("Yes")
#define PN puts("No")
string s;
int p;//指向前序序列(题目给出)中正在创建的位置
struct BTTree
{
    BTTree(char x)
    {
        val = x;
        lchild = nullptr;
        rchild = nullptr;
    }
    char val;
    BTTree *lchild, *rchild;
};
BTTree *build()
{
    if (p >= s.length())
        return nullptr;
    if (s[p] == ' ')
    {
        p++;
        return nullptr;
    }
    // cout << "now build element " << s[p] << endl;
    BTTree *ts = new BTTree(s[p++]);
    ts->lchild = build(); //先创建左子树
    ts->rchild = build();
    return ts; //返回当前创建完成(已经向下找并返回)到上一级
}
void preorder(BTTree *now)
{
    if (!now)
        return;
    cout << now->val << " ";
    preorder(now->lchild);
    preorder(now->rchild);
}
void inorder(BTTree *now)
{
    if (!now)
        return;
    inorder(now->lchild);
    cout << now->val << " ";
    inorder(now->rchild);
}
void postorder(BTTree *now)
{
    if (!now)
        return;
    postorder(now->lchild);
    postorder(now->rchild);
    cout << now->val << " ";
}
int main()
{
    getline(cin, s); //有空格用getline读
    BTTree *root = build(); //调用函数创建,得到的是根节点
    // cout << "build complete" << endl;
    /*输出部分,调用函数即可*/
    preorder(root);
    cout << endl;
    inorder(root);
    cout << endl;
    inorder(root);
    return 0;
}

问题 B: 静态链表存储的二叉树查找根节点

题面

题目描述

用数组模拟实现链表的功能,即静态链表,也可以用来存储二叉树。
请编写程序,输出静态链表存储的二叉树的根节点

输入

输入给出2棵二叉树树的信息。
对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N−1编号);
随后N行,第i行对应编号第i个结点,给出该结点中存储的1个英文大写字母、其左孩子结点的编号、右孩子结点的编号。
如果孩子结点为空,则在相应位置上给出“-”。
给出的数据间用一个空格分隔。
注意:题目保证每个结点中存储的字母是不同的。

输出

针对每颗二叉树,输出根节点。
如果二叉树为空,则输出空行。

样例输入 

8
A 1 2
B 3 4
C 5 -
D - -
E 6 -
G 7 -
F - -
H - -
8
G - 4
B 7 6
F - -
A 5 1
H - -
C 0 -
D - -
E 2 -

样例输出

A
A

题解 

我的评价:这是一道毒瘤题,把输入卡得死死的,还限制下标,造成了一系列坑点,完全偏离了这道题的意图

第一个要解决的就是读入,它居然将空节点用'-'来表示,所以我们没法简单的使用设定好的数据类型读入,只有自己写读入函数(你给个用-1表示行不行啊)

第二个就是这个下标,居然是从0开始的,所以又给读入增加了难度,为了判断空节点,我将其转换成-1,又与自己写的读入函数发生矛盾,没法判断0是读入的还是空节点没有读入产生的,因此又需要重写一遍。

看一下具体解决过程(思路):

感觉这道题更偏向图论的知识,我们需要找到根节点,它的特征就是入度为零。我们可以将每个节点深搜,如果这个点只被搜到一次,那么这个节点就是根节点。

然后就是毒瘤的卡输入过程,可以看我的代码解决

AC代码

// Problem: B: 静态链表存储的二叉树查找根节点
// Contest: BUCTOJ
// URL: https://buctoj.com/problem.php?cid=2781&pid=1
// Memory Limit: 128 MB
// Time Limit: 1000 ms

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
#define PY puts("Yes")
#define PN puts("No")
int cnt[N];
struct Bttree
{
    int lchild, rchild;
    char x;
} bttree[N];
void dfs(int now)
{
    if (now == -1)
        return;
    // cout << "vis " << now << endl;
    cnt[now]++; //统计访问次数
    dfs(bttree[now].lchild);
    dfs(bttree[now].rchild);
}
void solve()
{
    int n;
    cin >> n;
    memset(cnt, 0, sizeof(cnt));//每次记得清零
    cin.ignore();//用getline前先清理缓冲区
    for (int i = 0; i < n; i++)
    {
        string s;
        getline(cin, s);
        char c;
        int a = 0, b = 0;
        int p = 2;
        c = s[0]; //读入字母这个是毋庸置疑的
        bool spa = false, spb = false;//判断第一个和第二个是否为'-'

        while (p < s.length() && s[p] != ' ')
        {
            if (!isdigit(s[p]))
                spa = true;
            else
                a = a * 10 + s[p] - '0';
            p++;
        }
        p++;
        while (p < s.length() && s[p] != ' ')
        {
            if (!isdigit(s[p]))
                spb = true;
            else
                b = b * 10 + s[p] - '0';
            p++;
        }

        if (spa)
            a = -1;
        if (spb)
            b = -1;
        // cout << c << " " << a << " " << b << endl;
        bttree[i].lchild = a, bttree[i].rchild = b;
        bttree[i].x = c;
    }
    // cout << "read done!" << endl;
    for (int i = 0; i < n; i++) //对每个节点深搜
    {
        dfs(i);
    }
    for (int i = 0; i < n; i++)
    {
        if (cnt[i] == 1)  //输出
        {
            cout << bttree[i].x << endl;
        }
    }
    // cout << endl;
}

int main()
{
    for (int i = 0; i < 2; i++)
        solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值