C++ 中的 DFS 实现

Posted

技术标签:

【中文标题】C++ 中的 DFS 实现【英文标题】:DFS implementation in C++ 【发布时间】:2020-06-10 11:09:31 【问题描述】:

我写了下面的代码。但是我不确定我是否正确插入了我的树。代码编译成功,但我没有在输出中得到 DFS 遍历数组。谁能告诉我哪里出错了?

输入树在下面,但如果我在主函数中进行了正确的调用,请告诉我?随着我的输出即将到来 - [a b c d e f g h i j k]。而正确的输出应该是 [a b e f i j c d g k h]。

    A
  / \ \
 B   C D
/ \   / \
E  F  G  H
 / \  \
I  J  K
#include <bits/stdc++.h>
using namespace std;

class Node

    public:
      string name;
      vector <Node *> children;

      Node(string name)
      
          this->name = name;
      

      //O(v+e) time | O(v) space
      vector <string> depthFirstSearch(vector<string> *array)
      
          array->push_back(this->name);
          for(size_t i = 0; i < this->children.size(); i++)
               children[i]->depthFirstSearch(array);


          return *array;
      

      Node *addChild(string name)
      
          Node *child = new Node(name);
          children.push_back(child);
          return this;
      
;

int main()

    Node n1("a");



    n1.addChild("b");
    n1.addChild("c");
    n1.addChild("d");
    n1.children[0]->addChild("e");
    n1.children[0]->addChild("f");
    n1.children[0]->children[1]->addChild("i");
    n1.children[0]->children[1]->addChild("j");
    n1.children[2]->addChild("g");
    n1.children[2]->addChild("h");
    n1.children[2]->children[0]->addChild("k");
    vector <string> array;
    n1.depthFirstSearch(&array);




    for (size_t i = 0; i< array.size(); i++)
        cout<<array[i]<<' ';






注意 - 感谢您的解释,但我在主函数中构建树的方式进行了一些编辑,并继续使用 return 此语句并且它起作用了。

【问题讨论】:

您的树表示令人困惑。 A有三个孩子吗? B、C 和 D? @Scheff 好点。我承认有点以二叉树为中心的狭隘思想。 ;-) 尽管如此,我还是对此评论表示“同意”。因此,明确说明可能会有所帮助。而且树的表示在其他地方也有点奇怪。 addChild 返回this。它不应该返回child 吗? @Damien 实际上是一个简单的错误,但只要你专注于depthFirstSearch()... :-) 【参考方案1】:

错误实际上很简单,但不是预期的。

Node::addChild() 返回this

      Node *addChild(string name)
      
          Node *child = new Node(name);
          children.push_back(child);
          return this; // <-- OUCH!
      

所以,当用于main():

    Node n1("a");
    Node *n2 = n1.addChild("b"); // => n2 = &n1;
    n1.addChild("c");
    Node *n4 = n1.addChild("d"); // => n4 = &n1;
    n2->addChild("e");
    Node *n3 = n2->addChild("f"); // => n3 = n2 = &n1;
    n3->addChild("i");
    n3->addChild("j");
    Node *n5 = n4->addChild("g"); // => n5 = n4 = &n1;
    n4->addChild("h");
    n5->addChild("k");

所以,树实际上并没有得到预期的形状,而是类似于:

   A________________
  / \ \ \ \ \ \ \ \ \
 B   C D E F G H I J K

对于 OP 的当前输出是完全正确的。

修复很简单:

      Node *addChild(string name)
      
          Node *child = new Node(name);
          children.push_back(child);
          return child;
      

输出:

a b e f i j c d g k h 

Live Demo on coliru


OP 坚持 Node::addChild() 必须 return this; 并要求另一种方法来规避这个问题。我试图让 OP 相信 return child;return this; 更有意义。

实际上,我直观地期望Node::addChild() 会返回创建的孩子,这使我花费了一些额外的调试才能找到实际的错误。 :-)

关于

我在主函数中构建树的方式 - 是正确的方式还是可以有更有效的方式来做到这一点

我个人认为main() 中的代码并没有那么糟糕。

但是,前往罗马的方式通常不止一种。

所以,这只是另一个想法:

显式构造子节点的第二个构造函数:

      Node(Node &parent, string name): Node(name)
      
        parent.children.push_back(this);
      

允许编写树初始化。在main() 像这样:

    Node nA("a");
    Node nB(nA, "b");
    Node nC(nA, "c");
    Node nD(nA, "d");
    Node nE(nB, "e");
    Node nF(nB, "f");
    Node nI(nF, "i");
    Node nJ(nF, "j");
    Node nG(nD, "g");
    Node nH(nD, "h");
    Node nK(nG, "k");

(在这种情况下,甚至不需要/使用Node::addChild()。)

输出:

a b e f i j c d g k h 

Live Demo on coliru

【讨论】:

感谢您的澄清。它现在工作正常。但我的困惑是 - 我在主函数中构建树的方式 - 是正确的方式还是可以有更有效的方式来做到这一点。我想要的是不要改变“return this to return child”,我想知道有没有一种方法我们在 main 函数中编写我们的树结构,代码也可以与“return this”一起使用。 忘记标记你了。 @islia 如果您不使用return child;,那么您如何获得指向在Node::addChild() 中创建的节点的指针?另一方面,为什么return this;this 是您事先肯定知道的。 (否则你不能为某个节点调用addChild(),因为return this;只是返回调用addChild()的节点。) @islia 在某些情况下,return this;(或return *this;)允许链接运算符或成员函数调用是有意义的。 (operator=() 的惯用重载是众所周知的例子。)我不会将您的 Node::addChild() 视为这种情况。 我编辑了我的代码并改变了我构建树的方式,还包括返回这个。代码运行正常。

以上是关于C++ 中的 DFS 实现的主要内容,如果未能解决你的问题,请参考以下文章

递归函数中的for循环在递归结束后继续

STL中的全排列实现

C++中的atoi实现

防止 C++ 中的虚拟方法实现

如何删除 dfs 表中的重复数据?

Protobuf ParseDelimitedFrom C++ 中的实现