1. 程式人生 > >PAT甲級1135----紅黑樹題解

PAT甲級1135----紅黑樹題解

題目描述

  1. Is It A Red-Black Tree (30)
    時間限制
    400 ms
    記憶體限制
    65536 kB
    程式碼長度限制
    16000 B
    判題程式
    Standard
    作者
    CHEN, Yue

There is a kind of balanced binary search tree named red-black tree in the data structure. It has the following 5 properties:

(1) Every node is either red or black.
(2) The root is black.
(3) Every leaf (NULL) is black.
(4) If a node is red, then both its children are black.
(5) For each node, all simple paths from the node to descendant leaves contain the same number of black nodes.

For example, the tree in Figure 1 is a red-black tree, while the ones in Figure 2 and 3 are not.

Figure 1

Figure 2

Figure 3

For each given binary search tree, you are supposed to tell if it is a legal red-black tree.

Input Specification:

Each input file contains several test cases. The first line gives a positive integer K (<=30) which is the total number of cases. For each case, the first line gives a positive integer N (<=30), the total number of nodes in the binary tree. The second line gives the preorder traversal sequence of the tree. While all the keys in a tree are positive integers, we use negative signs to represent red nodes. All the numbers in a line are separated by a space. The sample input cases correspond to the trees shown in Figure 1, 2 and 3.

Output Specification:

For each test case, print in a line “Yes” if the given tree is a red-black tree, or “No” if not.
Sample Input:

3
9
7 -2 1 5 -4 -11 8 14 -15
9
11 -2 1 -7 5 -4 8 14 -15
8
10 -7 5 -6 8 15 -11 17

Sample Output:

Yes
No
No

紅黑樹性質

紅黑樹是每個節點都帶有顏色屬性的二叉查詢樹,顏色或紅色或黑色。在二叉查詢樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求:
性質1. 節點是紅色或黑色。
性質2. 根節點是黑色。
性質3 每個葉節點(NIL節點,空節點)是黑色的。
性質4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。

題目理解

對於這題,資料域為正的節點為黑結點,資料域為負的是紅節點。然後忽略正負,他是一個二叉搜尋樹,這就給了我們由前序遍歷建樹的條件。建完樹後判斷。
如何判斷二叉樹。根據題目描述的性質,我們要進行如下三點判斷
1、根節點是否為正數
2、節點為負數的節點的孩子是否為正數
3、從根節點到葉子節點所有路徑擁有相同的黑色節點
第三點是有第五條性質決定的
因為如果滿足我們的第三條判斷,那麼從根節點的子孩子出發到達葉子節點的所有路徑擁有的黑色節點必然相同,層層遞迴下去,就是性質五

建離二叉鏈樹

/*
**per[perL~preR]之間代表的是二叉子樹前序遍歷,初始值因為整棵樹
*/
Tree* buildTree(int preL,int preR){
  //如果左索引比右索引說明遞迴到的是葉子節點的子節點,自然為NULL
  if(preL>preR)
    return NULL;
  Tree *tree = new Tree(pre[preL]);
  //在前序陣列中,找到第一個比根節點大的節點即為右子樹的根節點
  int i = preL+1;
  for(;i<=preR;++i){
    if(abs(pre[i])>abs(pre[preL])){
      break;
    }
  }
  //遞迴建立左右子樹
  tree->left = buildTree(preL+1,i-1);
  tree->right = buildTree(i,preR);
  //返回根節點
  return tree;
}

紅黑樹判斷

/*
**@tree 根節點
**@isRedroot 根節點是否為紅色如果是紅色就要判斷當前節點是不是黑了
**@num_blacknodes 記錄當前路徑已經遍歷的黑色節點個數
**
**@flag 初始值為true,他只有改為false的機會,只要我們某一次判斷是錯的他的值變永久性改為false
**@first 狀態位,是不是第一次到達葉子節點
**@num 記錄第一次到達葉子節點時經過的黑色節點個數,並作為判斷從根節點出發到葉子節點的其他路徑是否與第一次相等的憑藉
*/
void judge(Tree *tree,bool isRedroot,int num_blacknodes){
  //如果當前節點為空,代表父節點為葉子節點
  if(tree==NULL){
    //如果第一次抵達葉子節點,那麼用num儲存,黑色節點的個數
    if(first){
      num = num_blacknodes;
      first = false;
    }else{
      //如果不是第一次,就進行紅黑樹的第五條性質判斷
      if(num!=num_blacknodes){
        flag = false;
      }
    }
  }else{
   //記錄當前節點顏色
    bool isBlack = tree->data>0;
    //如果父節點是紅色,而當前節點也是紅色,那麼就不是紅黑樹
    if(isRedroot&&!isBlack){
      flag = false;
    }
    judge(tree->left,!isBlack,num_blacknodes+isBlack);
    judge(tree->right,!isBlack,num_blacknodes+isBlack);
  }
}

完整程式碼

#include <iostream>
#include <cmath>

using namespace std;

struct Tree{
  int data;
  Tree *left;
  Tree *right;
  Tree(int d):data(d){}
};

int pre[30];

Tree* buildTree(int preL,int preR){
  if(preL>preR)
    return NULL;
  Tree *tree = new Tree(pre[preL]);
  int i = preL+1;
  for(;i<=preR;++i){
    if(abs(pre[i])>abs(pre[preL])){
      break;
    }
  }
  tree->left = buildTree(preL+1,i-1);
  tree->right = buildTree(i,preR);
  return tree;
}

void freeMemory(Tree *tree){
  if(tree!=NULL){
    freeMemory(tree->left);
    freeMemory(tree->right);
    delete tree;
    tree = NULL;
  }
}

bool flag,first;
int num;

void judge(Tree *tree,bool isRedroot,int num_blacknodes){
  if(tree==NULL){
    if(first){
      num = num_blacknodes;
      first = false;
    }else{
      if(num!=num_blacknodes){
        flag = false;
      }
    }
  }else{
    bool isBlack = tree->data>0;
    if(isRedroot&&!isBlack){
      flag = false;
    }
    judge(tree->left,!isBlack,num_blacknodes+isBlack);
    judge(tree->right,!isBlack,num_blacknodes+isBlack);
  }
}

int main(int argc, char const *argv[])
{
  int K;
  cin >> K;
  for (int i = 0; i < K; ++i)
  {
    int N;
    cin >> N;
    flag = first = true;
    num = 0;
    for (int j = 0; j < N; ++j)
    {
      cin >> pre[j];
    }
    Tree *tree = buildTree(0,N-1);
    if(tree->data<0){
      flag = false;
    }else{
      judge(tree,false,0);
    }
    cout << (flag?"Yes":"No") << endl;
    freeMemory(tree);
  }
  return 0;
}