在具有重复子树的树上遍历

时间:2015-07-23 08:37:28

标签: c++ algorithm tree traversal

我在树上有一个简单的递归后序遍历,可以打印所有可能的路径。问题是需要花费很多时间。

我想使用树的属性来节省遍历时间。 属性是我有重复的子树,在下面的例子中,头1的子树出现在树中3次。

          10
  /        |        \
 5         15        1
 /\        / \      / \
2  1      1   6    3   8
  / \    / \
 3   8  3   8

我寻找改进我的遍历,跳过已经通过的子树。 我的想法是存储我已经通过的每个子树,但我无法将其与后序算法相匹配。

感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

使用哈希集来存储已访问过的节点,并且对于您访问的每个节点,检查它是否已被访问过:如果没有,请将其添加到访问集,然后照常继续,否则返回。

答案 1 :(得分:0)

我不知道你的程序是否允许这种改变....因为我们无法知道在遍历之前是否出现了一个子树,一种可能的方法是按照以下方式重新组织树以共享重复的子树。

          10
  /        |        \
 5         15        1
 /\        / \      / \
2  1 ------   6    3   8
  / \     
 3   8     

答案 2 :(得分:0)

  

打印所有可能的路径

我认为这是主要问题。程序的运行时间与要输出的数据量呈线性关系,大致对于程序执行的树中的每一步,它应输出至少一个符号。只要你保持所需的输出相同(即只要你需要输出所有路径),你就没有加速的空间了,你就不能拥有比O(R)更快的算法,其中R是您需要生成的输出的总大小。

事实上,很可能主要的瓶颈是输出本身(控制台或磁盘性能通常比仅仅在内存中行走更糟),所以我认为如果你对程序进行分析,你会发现90%的时间花在了在输出中。因此,不仅无法获得更好的渐近解决方案,也无法获得更快的解决方案。 (除非您优化输出本身,但这是一个不同的问题。)

但是,如果您不需要打印所有路径,但以某种方式处理它们 - 例如,计算它们存在多少,或找到最长路径等 - 那么您的解决方案你可以从重复的子树中获益。实际上,这意味着您没有,而是有向无环图,这通常允许使用相当简单的动态编程方法。

答案 3 :(得分:0)

假设每个节点代表一个以该节点为根的唯一子树,并且节点值集不是很大,您可以使用它来优化遍历:

string s[MAX_NODE_VALUE + 1];

string post_traverse(Node root)
{
    if(root == NULL)
        return "";
    if(s[root.value].length() == 0)
    {
        s[root.value] += post_traverse(root.left);
        s[root.value] += post_traverse(root.right);
        s[root.value] += itoa(root.value);
        s[root.value] += ' ';
    }
    cout << s[root.value];
    return s[root.value];
}

这只会在你有太多的重复子树和节点值集非常小时才有用,否则会消耗太多空间并花费更多时间。