【问题标题】:is there a way to check if two left-child right-sibling trees are equal?有没有办法检查两个左孩子右兄弟树是否相等?
【发布时间】:2020-08-04 21:33:15
【问题描述】:

我目前正在为左子右兄弟类重载相等运算符,并且该函数应该检查两棵树是否具有相同的根、相同的孩子和相同的同胞(或者总体而言,如果两棵树恰好是一样)

bool CTree::operator==(const CTree &root){
if(this->kids!=nullptr){
return ((kids)==(root.kids));
}
else{
if(this->sibs!=nullptr){
return ((sibs)==(root.sibs));
}
else{
return (data==root.data);
}
}
}

这是我当前检查两个左子右兄弟树是否相等的函数;我认为这有两个问题我找不到解决方法;首先,它返回kids==root.kids 而不检查sibs,其次,一旦kids 和sibs 指向null,这个函数就会停止检查两棵树的相等性。我试图写第二个函数

bool CTree::operator==(const CTree &root){
  if(this->kids!=nullptr){
    if (!(kids==root.kids)){
        return false;
      }
  }
  else{
    if(this->sibs!=nullptr){
      if (!(kids==root.sibs)){
          return false;
        }
    }
    else{
      if (!(data==root.data)){
          return false;
        }
    }
  }
  return true;
}

但这也不能正确检查两棵树的相等性。有人可以给我一个提示如何进行吗?我包含了部分头文件以供参考

class CTree {
  friend class CTreeTest;

  char data;     // the value stored in the tree node

  CTree * kids;  // children - pointer to first child of list, maintain order & uniqueness

  CTree * sibs;  // siblings - pointer to rest of children list, maintain order & uniqueness
                 // this should always be null if the object is the root of a tree

  CTree * prev;  // pointer to parent if this is a first child, or left sibling otherwise
                 // this should always be null if the object is the root of a tree}

【问题讨论】:

  • 如果我们能看到 CTree 标题会更容易判断
  • @Jeffrey 对不起,我忘了包括它!感谢您指出这一点!
  • 什么是 CTree?我以前从未听说过这种数据结构。根据描述,我无法理解 kidssibs 之间的区别。
  • 你的结构对我来说没有意义。一个人怎么知道有多少孩子?同胞也有同样的问题。
  • 不知道你最后比较data == root.data。我会先做这个比较,以尽早终止递归。如果节点本身不相等,则检查节点的所有子节点(成对)是没有意义的,不是吗?

标签: c++ class recursion tree operator-overloading


【解决方案1】:

我必须承认,我从未听说过(术语)left-child-right-sibling-trees(或者我已经忘记了)。但是,当我使用 libXml2 时,我已经看到了类似的情况。
(看看struct _xmlNode 了解我的意思。)

所以,我解释一下我将如何进行比较CTree::operator==()

    bool operator==(const CTree &cTree) const
    {
  1. 我会先比较data。如果根节点的数据不同,则无需遍历任何子节点或兄弟节点。
      if (data != cTree.data) return false; // unequal data -> equality failed
  1. 下一步是成对比较kids 指针。 如果其中一个节点有子节点,而另一个节点没有,则相等比较可以退出。为此,我只需将指针转换为bools。
      if (!kids != !cTree.kids) return false; // only one has kids -> equality failed
  1. 同级指针也是如此。
      if (!sibs != !cTree.sibs) return false; // only one has sibs -> equality failed 
  1. 现在,两个或没有一个节点有子节点。因此,对于前一种情况,第一次递归到孩子中已经完成。 (后一种情况,可以跳过这一步。)
      // recursion into kids (if there are kids)
      if (kids && *kids != *cTree.kids) return false; // kids exist but are unequal
  1. 在最后一步中,如果有兄弟姐妹,则必须对兄弟姐妹进行递归。否则,此时节点(子树)已被视为相等。
    我使用逻辑或短路。因此,如果sibs 具有nullptr,则跳过右侧并返回!nullptr (== true)。否则,返回值会导致兄弟姐妹的比较。
      // recursion into sibs (if there are sibs)
      return !sibs || *sibs == *cTree.sibs; // no sibs or result of sibs equality check
  1. 就是这样。
    }
  1. 为了对称(并且因为它已经被使用),还必须定义对应部分operator!=()。它只是建立在operator==() 和 AFAIK 之上,这很常见。
    bool operator!=(const CTree &cTree) const { return !(*this == cTree); }

我制作了一个 MCVE 来检查和演示这一点。因此,我将 data 的类型从 char 更改为 std::string 以使其更具说明性:

#include <iostream>
#include <string>

class CTree {
  private:
    std::string data;
    CTree *kids;
    CTree *sibs;
    CTree *prev;
  public:
    CTree(const std::string data = ""):
      data(data), kids(nullptr), sibs(nullptr), prev(nullptr)
    { }

    // adds a child.
    void add(CTree *kid)
    {
      if (kids) {
        kids->prev = kid; kid->sibs = kids; 
      }
      kids = kid; kid->prev = this;
    }

    bool operator==(const CTree &cTree) const
    {
      if (data != cTree.data) return false; // unequal data -> equality failed
      if (!kids != !cTree.kids) return false; // only one has kids -> equality failed
      // recursion into kids (if there are kids)
      if (kids && *kids != *cTree.kids) return false; // kids exist but are unequal
      if (!sibs != !cTree.sibs) return false; // only one has sibs -> equality failed 
      // recursion into sibs (if there are sibs)
      return !sibs || *sibs == *cTree.sibs; // no sibs or result of sibs equality check
    }

    bool operator!=(const CTree &cTree) const { return !(*this == cTree); }
};

CTree* install(bool complete)
{
  CTree *pRoot = new CTree("/");
  CTree *pBin = new CTree("bin/"); pRoot->add(pBin);
  CTree *pUsr = new CTree("usr/"); pRoot->add(pUsr);
  CTree *pBash = new CTree("bash"); pBin->add(pBash);
  CTree *pCsh = new CTree("csh"); pBin->add(pCsh);
  if (complete) {
    CTree *pEtc = new CTree("etc/"); pRoot->add(pEtc);
    CTree *pInitD = new CTree("init.d"); pEtc->add(pInitD);
  }
  return pRoot;
}

int main()
{
  CTree *drv1 = install(false);
  CTree *drv2 = install(false);
  CTree *drv3 = install(true);
  std::cout << "drv1 == drv2? " << (*drv1 == *drv2 ? "yes" : "no") << '\n';
  std::cout << "drv1 == drv3? " << (*drv1 == *drv3 ? "yes" : "no") << '\n';
  std::cout << "drv2 == drv3? " << (*drv2 == *drv3 ? "yes" : "no") << '\n';
}

输出:

drv1 == drv2? yes
drv1 == drv3? no
drv2 == drv3? no

Live Demo on coliru


三思而后行,我意识到等式 operator==() 可以写得更紧凑,尽管我认为它既不提高可读性,也不期望任何性能提升:

    bool operator==(const CTree &cTree) const
    {
      return data == cTree.data // unequal data -> equality failed
        && !kids == !cTree.kids // only one has kids -> equality failed
        && !sibs == !cTree.sibs // only one has sibs -> equality failed
        && (!kids || *kids == *cTree.kids) // no kids or result of kids equality check
        && (!sibs || *sibs == *cTree.sibs); // no sibs or result of sibs equality check
    }

Live Demo on coliru

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    相关资源
    最近更新 更多