『1135』Is It A Red-Black Tree

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.

rbf1.jpg
rbf2.jpg
rbf3.jpg
Figure 1Figure 2Figure 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. 根节点和所有叶子节点(这里将NULL看作叶节点,因此这个特征不具有什么约束)都是黑色

  2. 如果一个节点是红色,那么它的两个子节点必须均为黑色

  3. 每个节点到它所有子代叶子节点的路径上有相同个数的黑色节点

由于是二叉搜素树,因此通过和根节点之间的比较,可以将先序遍历划分为【根节点|左子树节点|右子树节点】,然后递归划分找出所有父子关系。然后通过DFS递归验证2、3特征,2比较简单只要检查红节点的子节点 即可;验证3的话就让dfs函数返回从当前节点到叶子节点路径中的黑节点个数,如果发现左右子节点返回个数不同或者其一返回-1,则说明不符合特征3直接返回-1。


🐎

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>

using namespace std;
map<int, pair<int, int>> sons;
vector<int> pre;

void build(int s, int e)
{
    int idx = upper_bound(pre.begin() + s, pre.begin() + e, abs(pre[s]),
                          [](const int &a, const int &b) { return abs(a) < abs(b); }) - pre.begin();
    if (idx > s + 1)
    {
        sons[pre[s]].first = pre[s + 1];
        build(s + 1, idx);
    }
    if (idx < e)
    {
        sons[pre[s]].second = pre[idx];
        build(idx, e);
    }
}

int check(int n)
{
    if (n == 0) return 0;
    // feature 2
    if (n < 0 && (sons[n].first < 0 || sons[n].second < 0))
        return -1;
    int l, r;
    // feature 3
    if ((l = check(sons[n].first)) < 0) return -1;
    else if ((r = check(sons[n].second)) < 0) return -1;
    else if (l != r) return -1;
    else return l + (n > 0);
}

int main()
{
    int n, m;
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        cin >> m;
        pre.resize(m);
        for (int j = 0; j < m; ++j)
            cin >> pre[j];
        if (pre[0] < 0)
        {
            printf("No\n");
            continue;
        }
        build(0, m);
        printf(check(pre[0]) > 0 ? "Yes\n" : "No\n");
        sons.clear(), pre.clear();
    }
}

这里用了upper_bound函数找到右子树的根节点位置,该函数返回第一个>给定值的元素迭代器,它的兄弟函数lower_bound则返回第一个≥给定值的元素迭代器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值