【问题标题】:How to write iterative DFS which counts descendants of a node in a tree如何编写迭代 DFS 来计算树中节点的后代
【发布时间】:2012-02-19 20:02:16
【问题描述】:

我无法打开此代码:

void dfs(int i = 1) {
  static int preorder = 0;
  d[i].first = ++preorder;
  d[i].second = 1;
  for (list<int>::iterator it = tree[i].begin(); it != tree[i].end(); ++it) {
    dfs(*it);
    d[i].second += d[*it].second;
  }
}

进入迭代。如您所见,它找到每个节点的预购编号以及它有多少后代。 我必须这样做,因为内存限制(数据大小最多为 10^6)。

提前致谢。

【问题讨论】:

  • 这不是一个很好的使用静态变量...
  • 为什么?这个函数只被调用一次。
  • 你递归调用它,所以它实际上被调用了很多次。
  • 嗯,也许我误解了一些东西,但我认为静态变量只初始化一次,它的行为就像全局变量一样(除了它不在全局命名空间中)那么你到底觉得什么不好?我能看到的唯一问题是下一个非递归调用将不会重置变量预购的值。请澄清:)
  • @rAum:没错。下次您非递归调用该函数时,它将不起作用。如果您在多线程情况下使用该函数,它也将无法正常工作。

标签: c++ tree depth-first-search iteration descendant


【解决方案1】:

DFS 是一种递归算法。

如果您试图避免用完堆栈空间,您可以使用显式堆栈(将隐式调用堆栈转换为显式节点堆栈)。但我认为你无法摆脱它是一种递归算法这一事实。

即使您使用显式堆栈,您仍然可能会耗尽内存。这取决于系统中的内存量以及树的形状。但在大多数情况下,它至少可以避免严重的崩溃(您可以检测到堆内存何时用完)。

【讨论】:

  • 如果节点有父指针,那么你不需要堆栈。
  • 我知道这一点。我可以使用堆栈编写简单的 dfs,但我的版本有问题,因为我使用回溯来计算后代并且不知道如何使其迭代。我有 32 MB 的可用内存,在最坏的情况下,我将有 10^6 个节点,而树只是一个列表,因此不可能使用重复...
  • @OliCharlesworth:嗯,节点没有父指针,但也许接下来尝试是正确的。谢谢;)
  • @OliCharlesworth 不会影响渐近运行时复杂度吗?您必须继续寻找下一个尚未访问过的孩子(而使用堆栈您可以免费获得)。
  • @OmriBarel:我不知道渐近复杂度,但它肯定会影响运行时。
【解决方案2】:

我终于弄明白了。它可能不是那么快,但它足够快,可以通过测试而不会消耗太多内存。我需要孩子指向他父亲的指针(只有 8 MB 数组,称为 ojciec),并检测节点是否是第一次访问(下降)或没有(上升)。 这是我的代码:

void dfs()
{
  int preorder = 0;
  int i;
  stack<int, list<int> > stos;

  stos.push(1);
  while(!stos.empty()) {
    i = stos.top();

    if (order[i] == 0) { // node is first time visited
      order[i] = ++preorder; // set default values
      size[i]  = 1;
    }

    if (dynastia[i] != NULL) // can go left...
      stos.push( pop( &dynastia[i] ) ); // so take first child, remove it and release memory
    else {
      stos.pop();
      size[ojciec[i]] += size[i]; // adding total number of descendants to father
    }
  }
}

【讨论】:

    猜你喜欢
    • 2013-05-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-01
    • 2015-12-22
    相关资源
    最近更新 更多