PAT_甲级_1135 Is It A Red-Black Tree

题目大意:

给定K给测试用例,每个测试用例给定一个N个结点的二叉搜索树的前序序列,要求判断该二叉树是不是红黑树。算法

算法思路:

首先使用isRed记录全部的红色结点,这样在建树的时候就能够使用正数来建树。而后再根据先序序列创建二叉搜索树(不须要中序),而后再使用先序遍历判断该数是不是红黑树便可。数组

建树过程:

每一次递归访问首先创建根节点并初始化数据,而后找到第一个大于根节点的位置k,这样[preL+1,k-1]为左子树 ,[k,preR]为右子树,分别递归建树就好。测试

Node* createTree(int preL,int preR){
    if(preL>preR) return nullptr;
    Node *root = new Node;
    root->data = pre[preL];
    // 找到第一个大于根节点的位置k
    int k;
    for(k=preL+1;k<=preR;++k){
        if(pre[k]>root->data) break;
    } 
    // [preL+1,k-1]为左子树
    root->left =  createTree(preL+1,k-1);
    // [k,preR]为右子树
    root->right = createTree(k,preR);
    return root;
}
先序遍历判断过程:

若是root==nullptr,说明到达叶子结点,将当前路径黑色结点数目countBlack添加进set cntBlack中,而后判断集合大小是否为1,若是不是说明不是红黑树。若是不是叶子结点,就访问该结点,若是是黑色结点,就自增countBlack,若是是红色结点,就判断其存在的孩子是否也是红色结点,若是是就说明不是红黑树,并返回,不然就递归访问左子树和右子树。spa

unordered_set<int> cntBlack;// 记录每一条路径的黑色结点个数
void DFS(Node *root,int countBlack,bool &flag){
    if(root==nullptr){
        // 到达叶子结点 
        cntBlack.insert(countBlack);
        if(cntBlack.size()!=1){
            // 出现不一样路径黑色结点不一样的状况 
            flag = false;
        }
        return;
    }
    // 当前结点为黑结点 
    if(!isRed[root->data]){
        ++countBlack;
    }else {
        // 当前结点为红结点
        if((root->left!=nullptr&&isRed[root->left->data])||(root->right!=nullptr&&isRed[root->right->data])) {
            // 孩子结点也为红色 
            flag = false;
            return;
        }
    }
    DFS(root->left,countBlack,flag);
    DFS(root->right,countBlack,flag);
}

在建树以前,先判断根节点是不是黑色结点,若是是再建树,能够减小时间成本。code

注意点:

  • 一、这里须要对于第三条须要特意说明一下,叶子结点为空是否是黑的不重要,重要的是必定得搜索到空再判断当前路径的黑色结点数目是否一致,不然会出现判断不彻底,由于从根节点到NULL和到非空叶子结点的路径不彻底同样(前者包含后者),本身动手将NULL补全,画出来就知道了。
  • 二、对于测试点3出现的段错误,通常是2个缘由,第一个是开辟的数组过小,最小为3225。第二是建树的时候出现内存泄漏,好比没有写return;,也有多是采用了不恰当的建树方式(我直接采用前序和中序建树就出现段错误,改成前序建树就行了)。
  • 三、每一次查询都得初始化全局变量。

提交结果:

image.png

AC代码:

#include<cstdio>
#include<vector>
#include<cstring>
#include<unordered_set>

using namespace std;

struct Node{
    int data;
    Node *left;
    Node *right;
};

const int maxn = 3225;// 通过测试,最小为3225,不然测试点3段错误 
bool isRed[maxn];// 记录结点是不是红色的 
int pre[maxn];// 前驱序列 
unordered_set<int> cntBlack;// 记录每一条路径的黑色结点个数 

Node* createTree(int preL,int preR){
    if(preL>preR) return nullptr;
    Node *root = new Node;
    root->data = pre[preL];
    // 找到第一个大于根节点的位置k
    int k;
    for(k=preL+1;k<=preR;++k){
        if(pre[k]>root->data) break;
    } 
    // [preL+1,k-1]为左子树
    root->left =  createTree(preL+1,k-1);
    // [k,preR]为右子树
    root->right = createTree(k,preR);
    return root;
}

void DFS(Node *root,int countBlack,bool &flag){
    if(root==nullptr){
        // 到达叶子结点 
        cntBlack.insert(countBlack);
        if(cntBlack.size()!=1){
            // 出现不一样路径黑色结点不一样的状况 
            flag = false;
        }
        return;
    }
    // 当前结点为黑结点 
    if(!isRed[root->data]){
        ++countBlack;
    }else {
        // 当前结点为红结点
        if((root->left!=nullptr&&isRed[root->left->data])||(root->right!=nullptr&&isRed[root->right->data])) {
            // 孩子结点也为红色 
            flag = false;
            return;
        }
    }
    DFS(root->left,countBlack,flag);
    DFS(root->right,countBlack,flag);
}

int main(){
    int K,N;
    scanf("%d",&K);
    for(int i=0;i<K;++i){
        memset(isRed,0,sizeof(isRed));
        cntBlack.clear();
        scanf("%d",&N);
        bool isRedBlackTree = true;
        for(int j=0;j<N;++j){
            scanf("%d",&pre[j]);
            if(pre[j]<0){
                pre[j] = -pre[j];
                isRed[pre[j]] = true;
            }
        }
        // 根节点是红色结点必定不是红黑树 
        if(isRed[pre[0]]) {
            isRedBlackTree = false;
            printf("No\n");
        }else{
            // 根据前序遍历构建二叉搜索树 
            Node *root = createTree(0,N-1); 
            // 先序遍历判断当前树是不是红黑树 
            DFS(root,0,isRedBlackTree);
            if(isRedBlackTree){
                printf("Yes\n");
            }else{
                printf("No\n");
            }
        }
    }
    return 0;
}
相关文章
相关标签/搜索