給定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
#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; }