题目描述
背景知识
红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。
通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树的5个性质:
(1)每个结点要么是红的要么是黑的。
(2)根结点是黑的。
(3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
(4)如果一个结点是红的,那么它的两个儿子都是黑的。
(5)对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
此处解释一下NIL指针:NIL是一个类对象的值,如果要把一个Class类型的对象设置为空的时候就用NIL。NULL是一个通用指针。所以就是空指针的意思。
本题由于对第五点性质的理解有误,使我一开始有两个测试点无法通过。
之后查阅了一些资料,对第五点性质有了更深的理解。如下图,这里的叶子结点是NIL结点,并不是我们常说的左右孩子结点都为NULL的结点。因此,计算路径上的黑结点个数时,NIL结点也应算上。
性质5确实可以转化为只判断从根节点到所有NIL结点路径,不必判断每一个结点。举个栗子说明:如下图,无需判断1-NIL、1-6-NIL、1-6-NIL这三条路径是否满足性质5,因为在判断根结点时,13-8-1-NIL、13-8-1-6-NIL、13-8-1-6-NIL已经判断过。
AC代码
#include<iostream>
#include<vector>
using namespace std;
struct node {
int val;
node *left, *right;
};
void insert(node* &root, int val) {
if (root == NULL) {
root = new node;
root->val = val;
root->left = root->right = NULL;
return;
}
if (abs(root->val) > abs(val)) insert(root->left, val); //注意取绝对值比较
else insert(root->right, val);
}
vector<int> num; //存放根节点到NIL指针的路径上黑结点的个数
bool flag=1; //表示是否满足性质4
void dfs(node* root, bool isred, int sum) { //isred==true表示root的父结点为red
if (root == NULL || root->val > 0) sum++;
if (root == NULL) {
num.push_back(sum);
return;
}
if (isred == 1 && root->val < 0) flag = 0;
isred = root->val < 0;
dfs(root->left, isred, sum);
dfs(root->right, isred, sum);
}
int main() {
int n, k; cin >> n;
while (n--) {
scanf("%d", &k);
node* root = NULL;
while (k--) {
int val; scanf("%d", &val);
insert(root, val);
}
if (root->val < 0) printf("No");
else {
num.clear(); //注意初始化
flag = 1;
dfs(root, 0, 0);
bool samenum = 1; //个数是否相同
for (int i = 1; i < num.size(); i++) {
if (num[i] != num[0]) samenum = 0;
}
printf("%s", samenum&&flag ? "Yes" : "No");
}
printf("\n");
}
return 0;
}
再探错误代码
既然每个叶结点(传统意义上的叶结点)都有NIL指针,那么黑结点个数加不加NIL指针好像不会影响判断结果吧?其实不然!我找到一个反例:
对于上图,大可以试试两种方法,结果确实不同。