【问题标题】:C++ Placeholder value for an integer整数的 C++ 占位符值
【发布时间】:2021-03-02 22:33:25
【问题描述】:

我目前正在用 C++ 实现红黑树。我遇到的一个问题是,最初,我的 Node 类的构造函数会像这样初始化每个 Node 对象:

(Node类属性)

    Tree_node *left;
    Tree_node *right;
    Tree_node *parent;
    static Tree_node *node_to_delete;
    static Tree_node *successor;
    static bool is_balanced_tree;
    static int black_depth_count;
    int val;
    int color;
    int visited;
    int visited_deletion;
};

(节点类构造函数)

Tree_node::Tree_node(int v){
    val=v;
    left=NULL;
    right=NULL;
    parent=NULL;
    color=1;
    visited=0;
    visited_deletion=0;
}

当然,我最终意识到,默认情况下将左/右子节点实例化为 NULL 会导致我的树没有真正的黑色外部节点,而是简单的 null 值。这会导致删除情况下的问题,其中替换我们原始节点的节点的右子节点是外部节点(因此在我的代码中被认为是简单的 NULL),因此我无法访问任何类方法来返回我们的父节点当前节点或兄弟节点,因为节点本身甚至不是节点,它只是 NULL。

所以我现在要做的是构建一个子类 Nil_Black_Node,它将是树的外部节点,而不仅仅是一个 NULL 值。不同之处在于,此构造函数默认将节点的颜色初始化为 0(黑色)而不是像 Tree_node 非外部节点的构造函数那样的 1(红色)。我的 Nil Black Node 是这样声明的(它是 Tree_node 类的子类)

class Nil_Black_Node:public Tree_node{
public:
    Nil_Black_Node();
};

它的构造函数只是将它的颜色值设置为0:

Nil_Black_Node::Nil_Black_Node():Tree_node(0){
    color=0;
}

但现在的问题是我的 Nil Black 节点被初始化为值为 0。我不希望我的 Nil Black 节点具有任何值——在这些外部节点的上下文中真正重要的是它们具有指向它们是子节点的 Tree_node 的父指针,颜色为 0。允许它们具有值会弄乱我的插入方法,因为我的方法将遍历树并看到 nil 黑色节点的值为 0 并尝试插入节点作为它的子节点,就像它是任何其他节点一样。有没有办法绕过它们有任何价值?像某种空占位符?

【问题讨论】:

标签: c++ algorithm class inheritance


【解决方案1】:

当然,我最终意识到,默认情况下将左/右子节点实例化为 NULL 会导致我的树没有真正的黑色外部节点,而是简单的 null 值。

叶子不是“真正的节点”不是问题。我们可以简单地确定空指针是零黑叶并相应地编写我们的算法。

另一种表示是使用哨兵节点,它允许您在实际对象上设置黑度,方式与使用分支节点相同。选择使用 sentinel 还是 null 会影响您编写算法的方式,但这都不是妨碍编写这些算法的问题。

所以我现在要做的是构建一个子类,

您不一定需要派生类来表示哨兵节点。您可以简单地使用Tree_node 的实例。

允许它们有值会弄乱我的插入方法,因为我的方法会遍历树并看到 nil 黑色节点的值是 0

没有任何价值怎么能解决这个问题?我不明白它怎么可能。当您的插入方法尝试获取值以查看没有值时会发生什么?实际的解决方案是修复插入方法以检查节点是否为叶节点,并做出相应的行为。

这样,哨兵是否有值无关紧要,如果有,那么该值恰好是什么也无关紧要。你根本就没有读过它。然而,当树被模板化以支持非平凡类型时,拥有多余的值可能会成为问题,在这种情况下,我们可能需要避免创建实例。


有没有什么方法可以绕过它们有任何价值?

记住我上面写的我们不一定需要关心这个,并且考虑到继承也不一定需要,这是一种基于继承的方法来避免在哨兵中存储值:

struct Base_Node {
    // no value
    ...

    virtual ~Base_Node() = default;
};

struct Branch_Node : Base_Node {
    int value; // has value
};

struct Nil_Black_Node : Base_Node {
    // no value
} sentinel;

【讨论】:

  • 我明白了。将两个类作为基节点的派生节点的继承方式将是一种聪明的方法,并且解决了我试图避免以某种方式从 Tree_node 类继承值的 Nil Black Node 类的情况。我还在考虑向 Node 类添加额外的整数/布尔标志,默认情况下设置为 false,但派生的 Nil Black Node 的构造函数将其设置为 true。这样,当使用插入和遍历树时,我可以设置一个额外的约束来忽略具有真正 nil 黑色节点布尔标志的节点。
  • @ElliottGoldstein 不需要额外的字段。区分哨兵的常规方法是只有哨兵节点的子节点指向自身。或者,在使用 null 的情况下,只需比较一个指向 null 的指针,就可以识别出它是一个 nil 黑色节点。
  • 使用 NULL 的唯一问题是我的删除案例是需要调用者对象实例访问其父值的类方法。如果外部节点设置为 NULL,并且如果中序后继节点的右子节点是 NULL 外部节点,我不能使用我的 get_parent() 类方法,因为“this”对象只是一个 NULL 值,它没有像 a 这样的数据其中的父指针。我不知道以这种方式定义的哨兵值。我很容易想象我可以编写一个布尔方法来检查节点的子节点是否指向自身,从而解决问题!
  • @ElliottGoldstein:听起来您正在重新设计数据结构以避开删除代码中的错误,而不仅仅是修复删除案例。
猜你喜欢
  • 2011-03-30
  • 2010-11-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-13
  • 2013-10-19
  • 2023-03-31
  • 1970-01-01
  • 2023-01-13
相关资源
最近更新 更多