第一次尝试:没留意红黑树是BST,直接用回溯+非递归建树列举了所有可能情况:
#include<cstdio>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> preorder;
struct node {
int val, depth;
node *left, *right;
node(int v) {
val = v;
left = NULL;
right = NULL;
depth = 1;
}
};
node *dummy;
int weight = -1;
bool checkWeight(node *root, int curWeight) {
if (root == NULL) {
curWeight++;
if (weight == -1) {
weight = curWeight;
return true;
}
else return curWeight == weight;
}
if (root->val > 0) {
curWeight++;
}
if (checkWeight(root->left, curWeight) == false) {
return false;
}
return checkWeight(root->right, curWeight);
}
bool buildTree(node *cur, int index, stack<node*> st) {
if (cur == dummy->right) {
return false;
}
if (index == preorder.size()) {
weight = -1;
bool b = checkWeight(dummy->left, 0);
return b;
}
if (preorder[index] > 0) { //下一结点为黑结点
cur->left = new node(preorder[index]);
st.push(cur->left);
if (buildTree(cur->left, index + 1, st))
return true;
st.pop();
cur->left = NULL;
}
else { //下一结点为红结点
if (cur->val > 0) { //当前是黑结点
cur->left = new node(preorder[index]);
st.push(cur->left);
if (buildTree(cur->left, index + 1, st))
return true;
st.pop();
cur->left = NULL;
}
}
while (!st.empty()) {
cur = st.top();
st.pop();
if (preorder[index] > 0) { //下一结点为黑结点
cur->right = new node(preorder[index]);
st.push(cur->right);
if (buildTree(cur->right, index + 1, st))
return true;
st.pop();
cur->right = NULL;
}
else { //下一结点为红结点
if (cur->val > 0) {
cur->right = new node(preorder[index]);
st.push(cur->right);
if (buildTree(cur->right, index + 1, st))
return true;
st.pop();
cur->right = NULL;
}
}
}
return false;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0;i < n;i++) {
int size;
scanf("%d", &size);
preorder.resize(size);
for (int j = 0;j < size;j++) {
scanf("%d", &preorder[j]);
}
dummy = new node(-1);
stack<node*> temp;
bool isRBT = buildTree(dummy, 0, temp);
if (isRBT) {
printf("Yes\n");
}
else printf("No\n");
}
}
第二次尝试:尝试借助栈根据BST有序这一特性和先序序列非递归建树:
struct node {
int val;
node *left, *right, *parent;
node(int v) {
val = v;
left = NULL;
right = NULL;
parent = NULL;
}
};
//建树
int index = 0;
stack<node*> st;
node *root = new node(preorder[index++]), *cur = root;
st.push(root);
while (index < preorder.size()) {
while (index < preorder.size() && abs(preorder[index]) < abs(cur->val)) {
cur->left = new node(preorder[index++]);
cur->left->parent = cur;
cur = cur->left;
st.push(cur);
}
if (index < preorder.size()) {
while (!st.empty() && cur->parent != NULL && cur->parent->left == cur && abs(cur->parent->val) < abs(preorder[index])) { //不能插入正确位置,应该改写插入条件
cur = st.top();
st.pop();
}
cur->right = new node(preorder[index++]);
cur->right->parent = cur;
cur = cur->right;
st.push(cur);
}
}
以上方法并不能正确地建树,当结点要插入右子树时并不能插入到正确位置。
改正后的建树方法及完整代码:
#include<cstdio>
#include<cmath>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
struct node {
int val;
node *left, *right, *parent;
node(int v) {
val = v;
left = NULL;
right = NULL;
parent = NULL;
}
};
vector<int> preorder;
bool checkRedNode(node *cur) { //条件(4),满足则返回true
if (cur == NULL)
return true;
if (cur->val < 0) { //当前结点为红结点
if (cur->left != NULL && cur->left->val < 0 || cur->right != NULL && cur->right->val < 0) {
return false;
}
}
if (checkRedNode(cur->left) == false) {
return false;
}
return checkRedNode(cur->right);
}
int blackCount = -1;
bool checkBlack(node *cur, int count) { //条件(5),满足则返回true
if (cur == NULL) {
count++;
if (blackCount == -1) {
blackCount = count;
return true;
}
else return blackCount == count;
}
if (cur->val > 0) //黑结点
count++;
return checkBlack(cur->left, count) && checkBlack(cur->right, count);
}
bool traverse(node *cur) { //遍历判定每个结点
if (cur == NULL)
return true;
blackCount = -1;
if (checkBlack(cur, 0) == false)
return false;
return traverse(cur->left) && traverse(cur->right);
}
bool isInsertPos(node *pos, int val) {
while (pos->parent != NULL) {
if (pos->parent->left == pos) { //该结点是左子树
if (abs(pos->parent->val) < abs(val))
return false;
}
else {
if (abs(pos->parent->val) > abs(val))
return false;
}
pos = pos->parent;
}
return true;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0;i < n;i++) {
int size;
scanf("%d", &size);
preorder.resize(size);
for (int j = 0;j < size;j++) {
scanf("%d", &preorder[j]);
}
//建树
int index = 0;
stack<node*> st;
node *root = new node(preorder[index++]), *cur = root;
st.push(root);
while (index < preorder.size()) {
while (index < preorder.size() && abs(preorder[index]) < abs(cur->val)) {
cur->left = new node(preorder[index++]);
cur->left->parent = cur;
cur = cur->left;
st.push(cur);
}
if (index < preorder.size()) {
while (!isInsertPos(cur, preorder[index])) {
cur = st.top();
st.pop();
}
cur->right = new node(preorder[index++]);
cur->right->parent = cur;
cur = cur->right;
st.push(cur);
}
}
bool isRBTree = true;
if (root->val < 0 || !checkRedNode(root) || !traverse(root)) {
isRBTree = false;
}
if (isRBTree) {
printf("Yes\n");
}
else printf("No\n");
}
}
改进:
给出BST的先序序列,可以直接根据先序序列依次将结点插入树中,就能直接得到所求二叉树。
注意:
- 红黑树的结点两子树的高度差可以大于2,所以不用判断树的平衡性。
- 要判断每个结点到叶节点遍历的黑结点数。
- 判断红结点是否有子结点是红结点。
AC代码:
#include<cstdio>
#include<cmath>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
struct node {
int val;
node *left, *right;
node(int v) {
val = v;
left = NULL;
right = NULL;
}
};
vector<int> preorder;
bool checkRedNode(node *cur) { //条件(4),满足则返回true
if (cur == NULL)
return true;
if (cur->val < 0) { //当前结点为红结点
if (cur->left != NULL && cur->left->val < 0 || cur->right != NULL && cur->right->val < 0) {
return false;
}
}
if (checkRedNode(cur->left) == false) {
return false;
}
return checkRedNode(cur->right);
}
int blackCount = -1;
bool checkBlack(node *cur, int count) { //条件(5),满足则返回true
if (cur == NULL) {
count++;
if (blackCount == -1) {
blackCount = count;
return true;
}
else return blackCount == count;
}
if (cur->val > 0) //黑结点
count++;
return checkBlack(cur->left, count) && checkBlack(cur->right, count);
}
bool traverse(node *cur) {
if (cur == NULL)
return true;
blackCount = -1;
if (checkBlack(cur, 0) == false)
return false;
return traverse(cur->left) && traverse(cur->right);
}
void insert(node *&cur, int val) {
if (cur == NULL) {
cur = new node(val);
return;
}
if (abs(val) < abs(cur->val)) {
insert(cur->left, val);
}
else insert(cur->right, val);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0;i < n;i++) {
int size;
scanf("%d", &size);
preorder.resize(size);
for (int j = 0;j < size;j++) {
scanf("%d", &preorder[j]);
}
//建树
node *root = NULL;
for (int i = 0;i < preorder.size();i++) {
insert(root, preorder[i]);
}
bool isRBTree = true;
if (root->val < 0 || !checkRedNode(root) || !traverse(root)) {
isRBTree = false;
}
if (isRBTree) {
printf("Yes\n");
}
else printf("No\n");
}
}
二刷:
每个结点到叶结点的黑结点数都要相同,仅仅数一下从根到叶结点的黑结点数是不行的。
#include<iostream>
#include<string>
#include<unordered_set>
#include<cmath>
#include<algorithm>
using namespace std;
struct node {
int val;
node *left, *right;
node(int v) {
val = v;
left = NULL;
right = NULL;
}
}*root = NULL;
bool isRedBlackTree = true;
//将结点插入BST中
void insert(node *&cur, int val) {
if (cur == NULL) {
cur = new node(val);
return;
}
if (abs(cur->val) > abs(val)) {
insert(cur->left, val);
}
else insert(cur->right, val);
}
int blackCount;
//数从当前结点到叶结点的路径的黑结点数
void countBlack(node *cur, int count) {
if (cur == NULL) {
if (blackCount == -1) {
blackCount = count;
}
else if (count != blackCount) {
isRedBlackTree = false;
}
return;
}
if (cur->val > 0) {
count++;
}
countBlack(cur->left, count);
if (isRedBlackTree == false) {
return;
}
countBlack(cur->right, count);
}
void preorderTraverse(node *cur) {
blackCount = -1;
countBlack(cur, 0);
if (isRedBlackTree == false) {
return;
}
if (cur->left != NULL) {
if (cur->val < 0 && cur->left->val < 0) {
isRedBlackTree = false;
return;
}
preorderTraverse(cur->left);
}
if (cur->right != NULL) {
if (cur->val < 0 && cur->right->val < 0) {
isRedBlackTree = false;
return;
}
preorderTraverse(cur->right);
}
}
int main() {
int numCase;
cin >> numCase;
for (int i = 0; i < numCase; i++) {
root = NULL;
isRedBlackTree = true;
int numNode;
cin >> numNode;
for (int j = 0; j < numNode; j++) {
int val;
cin >> val;
insert(root, val);
}
if (root->val < 0) {
isRedBlackTree = false;
}
preorderTraverse(root);
if (isRedBlackTree) {
cout << "Yes" << endl;
}
else cout << "No" << endl;
}
}