JOU1015

调这道题慢慢调了有一周吧(写成了屎山),其实这题不难,但是初次接触数据结构并不会注意到一些有趣的特殊情况,导致被卡,也是到最后问朱哥要了两个测试点才恍然大悟 hhh

根据先序和中序顺序建树

给定的遍历顺序是二叉树的先序顺序,由于二叉搜索树的特性,他的中序遍历就是按照从小到大来的,所以排序后就能得到中序遍历顺序。
这个算法使用递归实现,核心目的是划分出每次递归结束后以当前节点作为根节点后的左右子树,每次划分,都是下图的一个状态:

鉴于本题需要输出最左边和最右边的数,有必要记录每个节点的层数,方便找到每层的最右和最左边的节点。

node *build(vector<int> &pre,int pl,int pr,vector<int> in,int il,int ir,int deep,int width,int type){//0:left,1:right
    if(pl > pr) return nullptr;
    int root_val = pre[pl];
    int p = pos[root_val];
    node *pnode = new node();
    int sz = p - il;
    pnode->deep = deep;
    pnode->val = root_val;
    pnode->left = build(pre,pl+1,pl+sz,in,il,p-1,deep+1,pnode->width-1,0);
    pnode->right = build(pre,pl+sz+1,pr,in,p+1,ir,deep+1,pnode->width+1,1);
    return pnode;
}

关于回溯条件为什么是那个,我在 Tree Traversals Again 递归过程详解-优快云博客做了一个简单的证明。

将数据映射到遍历顺序下标

这应该是本题最抽象的一步吧,比赛的时候没看数据范围直接就写了,不管测试点有没有拿这个卡测试点,这一步还是需要考虑的。

什么数据需要映射

答案是只要处理中序遍历顺序就行了。众所周知,给定两种遍历顺序才能建树,对于先序和中序来说,中序遍历顺序的存在是为了找到先序的根节点在中序中的位置进而确定左右子树的长度,以进行下一轮的左右子树划分。
所以映射的目的是把大范围的数据映射到 vector 容器的下标上去,以便在中序中找到根节点的位置。

map<int,int> pos;
//---
for(int i = 0;i<n;i++){
	pos[in[i]] = i;
}

如何搜出所有最左或最右的节点

这里面存在一些特殊情况,题面那个图有很强的误导性

看上去会让你觉得建个树找到那个根节点然后一路搜下去就行了,如果这样做的话,40 分。

一条链较短另一条链较长的情况


如果这时候后面有一条更长的链,那么左视图的节点就要增加两个,右视图情况同理。

没有判断好每层节点左右关系

补题的时候记录了每层节点的宽度,采取很暴力的左节点就在父节点的基础上 -1,右节点 +1 的方法记录左右关系,但只能 70 分,因为会出现这种情况:

二叉树并不会右边别到左边来,但是如果这样判断左右的话最下面就反了,这就是 7、8、9 测试点的坑。

如何搜索?

小心上面三个坑点,思路其实很简单了,先搜到根节点,然后从根节点下面开始深搜,将搜到的节点放到对应层数的二维 vector 中去。
至于左右判断其实很简单,二叉树左边的节点肯定比右边小嘛,小的就是左边,大的就是右边,根据这个对容器排下序,然后按层数输出首元素就行啦。


最后放下我的 130 行大屎山
抽时间研究下朱哥的巨精简代码

#include <bits/stdc++.h>
using namespace std;
int n;
int level;
struct node{
    int val;
    int deep;
    int width;
    node *left;
    node *right;
    node() : val(0), right(nullptr), left(nullptr), deep(0), width(0) {}
};

map<int,int> pos;

node *build(vector<int> &pre,int pl,int pr,vector<int> in,int il,int ir,int deep,int width,int type){//0:left,1:right
    if(deep > level) level = deep;
    if(pl > pr) return nullptr;
    int root_val = pre[pl];
    int p = pos[root_val];
    node *pnode = new node();
    int sz = p - il;
    if(deep == 1 && type == 0){
        pnode->width = -1000000;
    }
    else if(deep == 1 && type == 1){
        pnode->width = 1000000;
    }else{
        pnode->width = width;
    }
    pnode->deep = deep;
    pnode->val = root_val;
    pnode->left = build(pre,pl+1,pl+sz,in,il,p-1,deep+1,pnode->width-1,0);
    pnode->right = build(pre,pl+sz+1,pr,in,p+1,ir,deep+1,pnode->width+1,1);
    return pnode;
}

node* find(int val, node* root) {
    if (root == nullptr) return nullptr; // 如果当前节点为空,返回nullptr
    if (root->val == val) return root;   // 如果找到目标值,返回当前节点
    node *left = find(val, root->left);  // 在左子树中查找
    if (left != nullptr) return left;     // 如果左子树中存在目标值,返回当前节点
    return find(val, root->right);       // 在右子树中查找
}

struct s_node{
    int width;
    int data;
};


void ans_dfs_right(node *root,vector<vector<s_node>> &path,int type){//0:left,1:right
    if(root == nullptr){
        return;
    }
    path[root->deep].push_back({root->width, root->val});
    ans_dfs_right(root->left,path,0);
    ans_dfs_right(root->right,path,1);
}

void ans_dfs_left(node *root,vector<vector<s_node>> &path,int type){//0:left,1:right
    if(root == nullptr){
        return;
    }
    path[root->deep].push_back({root->width, root->val});
    ans_dfs_left(root->left,path,0);
    ans_dfs_left(root->right,path,1);
}

void debug_find(int des,node *root){
    node *ptr = find(des,root);
    cout << "---" << endl;
    cout << "(" << ptr->val << "," << ptr->deep << "," << ptr->width << ")" << endl;
}

int main(int argc, char const *argv[])
{
    cin >> n;
    vector<int> pre(n);
    vector<int> in(n);
    for(int i = 0;i<n;i++){
        cin >> pre[i];
        in[i] = pre[i];
    }
    sort(in.begin(), in.end());
    for(int i = 0;i<n;i++){
        pos[in[i]] = i;
    }
    node *root = build(pre,0,n-1,in,0,n-1,0,0,0);
    int x;
    cin >> x;
    for(int i = 0;i<x;i++){
        int des;
        string type;
        cin >> des >> type;
        vector<vector<s_node>> path(level);
        node *ptr = find(des,root);
        if(type == "right"){
            ans_dfs_right(ptr,path,1);
            for(int i = 0;i<level;i++){
                if(path[i].size() == 0) continue;
                sort(path[i].begin(), path[i].end(),[](s_node a,s_node b){
                    return a.data > b.data;
                });
            }
            int level_now = ptr->deep;
            for(;level_now<level;level_now++){
                if(path[level_now].size() != 0)
                    cout << path[level_now][0].data << " ";
            }
            cout << endl;
        }else{
            ans_dfs_left(ptr,path,0);
            for(int i = 0;i<level;i++){
                if(path[i].size() == 0) continue;
                sort(path[i].begin(), path[i].end(),[](s_node a,s_node b){
                    return a.data < b.data;
                });
            }
            int level_now = ptr->deep;
            for(;level_now<level;level_now++){
                if(path[level_now].size() != 0)
                    cout << path[level_now][0].data << " ";
            }
            cout << endl;
        }
    }
    return 0;
}

塞个夕宝,个人认为画的最有味道的一张
1700209371573.png

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yyt363045841

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值