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;
}
相關文章
相關標籤/搜索