题目大意:
给定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,如果是红色结点,就判断其存在的孩子是否也是红色结点,如果是就说明不是红黑树,并返回,否则就递归访问左子树和右子树。
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);
}
在建树之前,先判断根节点是否是黑色结点,如果是再建树,可以减少时间成本。
注意点:
- 1、这里需要对于第三条需要特地说明一下,叶子结点为空是不是黑的不重要,重要的是一定得搜索到空再判断当前路径的黑色结点数目是否一致,否则会出现判断不完全,因为从根节点到NULL和到非空叶子结点的路径不完全一样(前者包含后者),自己动手将NULL补全,画出来就知道了。
- 2、对于测试点3出现的段错误,一般是2个原因,第一个是开辟的数组太小,最小为3225。第二是建树的时候出现内存泄漏,比如没有写return;,也有可能是采用了不恰当的建树方式(我直接采用前序和中序建树就出现段错误,改为前序建树就好了)。
- 3、每一次查询都得初始化全局变量。
提交结果:
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;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。