当前位置: 首页 > 知识库问答 >
问题:

在DFS遍历期间调用每个节点上的自定义函数

祝叶五
2023-03-14

我想知道什么是编写DFS遍历的最优雅的方式,它可以适应于解决不同的问题(在C中)。

我想把一个函数指针和一个void*传递给我的函数,让用户传递一个回调函数,这个回调函数将在每个节点上使用。

这就是我所拥有的:

typedef std::shared_ptr<struct Node> NodePtr;
typedef std::vector<NodePtr> NodeVector;

struct Node {
    int id{0};
    NodeVector children;
};

bool NodeIsInVector(NodePtr node, NodeVector node_vector);

void DFS(NodePtr node, void (*callback)(NodePtr, void * userdata)=nullptr, void * userdata=nullptr);
bool NodeIsInVector(NodePtr node, NodeVector node_vector){
    auto result = std::find(node_vector.begin(), node_vector.end(), node);
    return result != node_vector.end();
}

void DFS(NodePtr node, void (*callback)(NodePtr, void * userdata), void * userdata){
    static NodeVector visited;

    if (!NodeIsInVector(node, visited)) {
        visited.push_back(node);
        if (callback){
            callback(node, userdata);
        }
    }

    for (auto&& n : node->children){
        if (!NodeIsInVector(n, visited)){
            DFS(n, callback, userdata);
        }
    }
}

在这里,回调只计算节点、增量和整数。假设root是一个节点指针,并且定义了一个树。树有7个节点,因此在遍历之后,count的值应该是7。

int count = 0;
DFS(root, [](NodePtr node, void * count){ ++*(int*)count; }, (void*) &count);
std::cout << "DFS count: there are " << count << " nodes.\n";

但是输出是:DFS计数:有0个节点。调用回调(通过输出到stdout进行验证)。问题是userdata变量不会更新。

我的问题是:

  • 这是实现我想要的有效方法吗?有更好的方法吗?
  • 我犯的错误是什么?

共有2个答案

花阳辉
2023-03-14

如果我错了,请纠正我的错误,但我怀疑问题是由于DFS中定义的static变量造成的。

在我的问题中,我没有提到DFS是使用不同的回调调用的,在使用回调调用之前,回调不会增加计数变量。我认为这与我的问题无关。

在函数中声明静态变量是否会导致在第二次调用期间不替换回调函数?

以下版本按预期工作。我还用一个反映注释中提出的一些建议的std::function替换了函数指针,但更重要的是,我创建了一个包装器,声明访问了NodeVector,这让我没有任何静态 声明。

void DFS(NodePtr &node, std::function<void(NodePtr)> callback){
    NodeVector visited;
    _DFS(node, callback, visited);
}

void _DFS(NodePtr &node, std::function<void(NodePtr)> callback, NodeVector& visited){
    if (!NodeIsInVector(node, visited)) {
        visited.push_back(node);
        callback(node);
    } else {
        for(auto item : visited){
            std::cout<<item->id << " ";
        }
        std::cout << "\n";
    }
    for (auto&& n : node->children){
        if (!NodeIsInVector(n, visited)){
            _DFS(n, callback, visited);
        }
    }
}

和驱动程序代码:

int count = 0;
DFS(root, [&count](NodePtr node) mutable { ++count; });
std::cout << "DFS count: there are " << count << " nodes.\n";

生产:

DFS计数:共有7个节点

注意,相同的函数,但访问了一个静态NodeVector 而不是包装器策略无法按预期工作。我仍然不是100%清楚为什么。

辛弘壮
2023-03-14

我认为代码的问题在于,如果在调用回调之前节点没有子节点,则返回。这将导致只有父节点调用回调。

我会完全取消孩子们的空头支票。如果节点没有子节点,则不会执行for循环。

void DFS(NodePtr node, void (*callback)(NodePtr, void * userdata), void * userdata){
    static NodeVector visited;

    if (!NodeIsInVector(node, visited)) {
        visited.push_back(node);
        if (callback){
            callback(node, userdata);
        }
    }

    for (auto&& n : node->children){
        if (!NodeIsInVector(n, visited)){
            DFS(n, callback, userdata);
        }
    }
}
 类似资料:
  • 问题内容: 我是使用迭代器的新手,并且想知道如何迭代线段上的每个点(准确地说,是Line2D.Double)-我需要检查该线上的每个点是否满足某些要求。 另外,给定路径对象(如GeneralPath),您将如何做同样的事情(遍历形状轮廓上的每个点)? 理想情况下,我想要这样的东西(用直线或路径): 问题答案: Java API中似乎没有任何东西可以使布雷森汉姆的算法对用户可见。所以我写了一个遍历一

  • 问题内容: 我有一个如下所示的XML结构,但规模更大: 为此,我使用了以下代码: 但是,打印出来的作者文本为“无”。我尝试使用下面的变体来摆弄,但这会导致程序中断。 正确的输出应为: 但是我得到的是: 关于如何解决这个问题有什么建议吗? 问题答案: 您的类型为1(),通常您需要获取一个字符串。这会工作

  • 问题内容: 尝试概括我的问题…我想为SELECT语句返回的每个结果执行一个存储过程。 精神上,我想尝试类似EXEC myStoredProc(从某个表的SELECT ID中选择cond = @param) 与我的特定案例有关的更多详细信息…我有一个SaaS应用程序。我想从系统中删除一个租户。在删除租户之前,我必须删除与该租户关联的数据库中的所有记录。 租户拥有诸如包含许多不同类型字段的表单之类的项

  • $this->db->call_function(); 这个方法用于执行一些 CodeIgniter 中没有定义的 PHP 数据库函数,而且 使用了一种平台独立的方式。举个例子,假设你要调用 mysql_get_client_info() 函数,这个函数 CodeIgniter 并不是原生支持的,你可以这样做: $this->db->call_function('get_client_info')

  • 本文向大家介绍如何在JSP中遍历XML的节点?,包括了如何在JSP中遍历XML的节点?的使用技巧和注意事项,需要的朋友参考一下 <X:的forEach>标记用于遍历XML文档中的节点。 属性 <X:的forEach>标签具有以下属性- 属性 描述 需要 默认 选择 要评估的XPath表达式 是 没有 变种 用于存储每个循环的当前项目的变量名称 没有 没有 开始 迭代的开始索引 没有 没有 结束 迭

  • 我一直在尝试在工作中实现Autosys作业计划的树表示。由于每个作业(流程)可以有一个或多个依赖作业,因此我决定使用n元树实现,以便映射流程。我使用java集合来实现同样的目标。 Q1(已解决):目前的问题是显示功能被挂在一个无限循环中。我尝试寻找现有的线程,但无法遇到使用迭代器进行遍历的示例。 Q2:(打开)显示功能以后序方式遍历树。我希望以一种级别顺序的方式遍历它,其中节点以级别为基础进行打印