团体天梯练习 L2-004 这是二叉搜索树吗?

L2-004 这是二叉搜索树吗?

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,

• 其左子树中所有结点的键值小于该结点的键值;
• 其右子树中所有结点的键值大于等于该结点的键值;
• 其左右子树都是二叉搜索树。
所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。

给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。

输入格式:

输入的第一行给出正整数 N(≤1000)。随后一行给出 N 个整数键值,其间以空格分隔。

输出格式:

如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出 YES ,然后在下一行输出该树后序遍历的结果。数字间有 1 个空格,一行的首尾不得有多余空格。若答案是否,则输出 NO。

输入样例 1:

7
8 6 5 7 10 8 11

输出样例 1:

YES
5 7 6 8 11 10 8

输入样例 2:

7
8 10 11 8 6 7 5

输出样例 2:

YES
11 8 10 7 5 6 8

输入样例 3:

7
8 6 8 5 10 9 11

输出样例 3:

NO


解题思路

这是一道判断是否是二叉搜索树(BST)的数据结构题(当然这是废话)。二叉搜索树的性质是,对于每个节点,其左子树的节点值都比它的值小,其右子树的节点值都大于等于它的值(当然这也是废话,学过数据结构的都知道),不过做出这道题的关键之处就是利用这一性质。对于镜像二叉树,也是类似的性质,只不过反一下而已。

题中所给的是一颗二叉树的前序遍历序列,判断是否是一颗二叉搜索树或者是镜像的二叉搜索树,由BST的性质可知,二叉搜索树前序遍历序列的第一个值就是根节点的值,然后是左子树的前序遍历序列且所有值都小于根节点值,最后是右子树的前序遍历序列且所有值大于等于根节点的值。

$ [Root,[LeftPreOrder], [RightPreOrder]] $

我们只需要递归建立这个二叉树,然后递归的每一层判断一下当前是否出现矛盾即可,并且在矛盾出现的时候,之后的节点都可以停止建立。

对于是判断二叉搜索树还是镜像的二叉搜索树,只需要一开始判断一下前序遍历序列的第一个值和第二个值的大小关系即可。用一个 flag 标记一下就行,flag 记为1,就是判断BST;如果 flag 为2,就是判断镜像BST。

如果递归结束后,flag 变为0了,说明出现了矛盾,即不可能是二叉搜索树或镜像二叉搜索树。

/*   一切都是命运石之门的选择  El Psy Kongroo  */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<cmath>
#include<functional>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
typedef pair<double, double> pdd;
typedef pair<string, pii> psi;
typedef __int128 int128;
#define PI acos(-1.0)
#define x first
#define y second
//int dx[4] = {1, -1, 0, 0};
//int dy[4] = {0, 0, 1, -1};
const int inf = 0x3f3f3f3f, mod = 1e9 + 7;


struct node{
    int val;
    struct node *left, *right;
};
const int N = 1010;
int a[N], n, flag;
vector<int> res;

//获取后序遍历序列
void post(node *root){
    if(!root) return;
    post(root->left);
    post(root->right);
    res.push_back(root->val);
}

node* build(int l, int r){
    if(l > r) return NULL;

    int root_val = a[l]; //最左边即根节点的值
    int i = l + 1;
    
    //如果是BST 找到第一个大于等于的值即右子树的根
    //如果是镜像BST 找到第一个小于的值即右子树的根
    while(i <= r && ((flag == 1 && a[i] < root_val) || (flag == 2 && a[i] >= root_val))) 
        i ++ ;    
    
    bool has_op = false;   //标记是否存在矛盾
    
    //如果是BST 右子树中存在一个值小于根节点的值则出现矛盾
    //如果是镜像BST 右子树中存在大于等于根节点的值则出现矛盾
    for(int j = i; j <= r; j ++ ) 
        if((flag == 1 && a[j] < root_val) || (flag == 2 && a[j] >= root_val)) 
            has_op = true;  
    
    //如果存在矛盾 还需标记flag为0 之后全部置NULL即可
    if(has_op || !flag){
        flag = 0;        
        return NULL;
    }

    node *root = new node;
    root->val = root_val;
    root->left = build(l + 1, i - 1);   //递归建立左子树
    root->right = build(i, r);          //递归建立右子树

    return root;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n;
    for(int i = 1; i <= n; i ++ ) cin >> a[i];
    if(a[2] < a[1]) flag = 1;   //说明要判断是否为BST
    else flag = 2;  //说明要判断是否为镜像BST

    node *t = build(1, n);
    
    if(flag){
        cout << "YES" << endl;
        post(t);  //获取BST后序遍历序列
        for(int i = 0; i < n - 1; i ++ ) cout << res[i] << ' ';
        cout << res[n - 1];
    }else cout << "NO" << endl;  //flag为0 说明是矛盾的

    return 0;
}
posted @ 2023-04-17 00:43  MarisaMagic  阅读(28)  评论(0编辑  收藏  举报