C++ 在具有重复子树的树上进行遍历

C++ 在具有重复子树的树上进行遍历,c++,algorithm,tree,traversal,C++,Algorithm,Tree,Traversal,我在一棵树上有一个简单的递归后序遍历,它打印所有可能的路径。问题是这需要很多时间 我想使用我的树的属性来节省遍历时间。 属性是我复制了子树,在下面的示例中,头为1的子树在树中出现了3次 10 / | \ 5 15 1 /\ / \ / \ 2 1 1 6 3 8 / \ / \ 3 8 3 8 我正在为跳过已经通过的子树的遍历寻找改进。

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

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

          10
  /        |        \
 5         15        1
 /\        / \      / \
2  1      1   6    3   8
  / \    / \
 3   8  3   8
我正在为跳过已经通过的子树的遍历寻找改进。 这个想法是存储我通过的每一个子树,但我无法将其应用于后序算法


感谢您的帮助。

我不知道您的程序是否允许此更改……因为我们无法知道在遍历时是否出现过子树,一种可能的方法是按照以下方式重新组织您的树以共享重复的子树

          10
  /        |        \
 5         15        1
 /\        / \      / \
2  1 ------   6    3   8
  / \     
 3   8     
打印所有可能的路径

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

事实上,最主要的瓶颈可能是输出本身(控制台或磁盘性能通常比在内存中行走差得多),因此我认为,如果您分析您的程序,您会发现90%的时间都花在输出上。因此,不仅不能得到更好的渐近解,而且根本不能得到更快的解。(除了优化输出本身,这是另一个问题。)


但是,如果您不需要打印所有路径,而是以某种方式处理它们(例如,计算它们存在的数量,或查找最长的路径等),那么您的解决方案可以从具有重复子树的事实中获益匪浅。实际上,这意味着你没有一棵树,而是一个有向无环图,这通常允许一种非常简单的动态编程方法。

使用哈希集来存储访问过的节点,对于你访问的每个节点,检查它是否已经访问过:如果没有,将它添加到访问集,然后照常进行,否则返回。

假设每个节点都代表一个根在该节点上的唯一子树,并且节点值集不太大,则可以使用此选项优化遍历:

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];
}
字符串s[最大节点值+1];
字符串后置遍历(节点根)
{
if(root==NULL)
返回“”;
如果(s[root.value].length()==0)
{
s[root.value]+=post_遍历(root.left);
s[root.value]+=post_遍历(root.right);
s[root.value]+=itoa(root.value);
s[根值]+='';
}

想想看。当你遍历树时,如果不遍历它,你怎么知道以前出现过子树?所以试着重新组织你的树并在重复的子树上做标记。这可能会有帮助。节点id是否总是与同一个子树关联?例如,如果你看到id为1的节点,你能确定它总是重复吗重新生成同一子树(1、3、8)在您的示例中?在这种情况下,我将构建一个包含所有遍历节点id的哈希表。对于您要检查的每个节点,我将首先询问哈希表您是否已经看到该节点,因此可以跳过它,包括其整个子树。是的,每个节点id都与一个子树关联。我尝试了您的想法,但因为我必须对于树中的所有路径,我不知道如何将我已经在该子树中路径的想法与算法相适应。您似乎实际上有一个DAG,而不是一棵树。因此,您需要通过添加一个数据结构来跟踪访问的节点,从而将树遍历更改为完整的DFS。您所说的“共享”是什么意思?请注意子树(1,3,8)在示例中出现3次如果将10连接到最左边的(1,3,8)子树,它将不再是3,因为您将得到一个循环。@Kfozli如果3个相同的子树(1,3,8)apear 3次,按共享就是只保留子树的一个副本,然后让节点5、10……指向副本。@Adrian yeah……这可能会导致一个循环。我认为这不重要……毕竟必须有一种方法知道是否访问过子树。有,只是没有这种方法。通过这种方法,我们可以将我们的树转换成一个图,它可能不是在我们需要的时候。最好的方法是使用哈希表来存储访问的节点,如上所述。您假设他在遍历时不进行任何计算,这可能不是true@avim,我想它应该是相当繁重的计算来超过输出。无论如何,这不会导致渐近更好的解决方案;而且,如果这是在这种情况下,than OP应该在问题中明确提到这一点,因为问题不是“如何优化遍历”,而是“如何优化计算”.@Petr即使没有实际打印结果,也需要很多时间。似乎可以节省一些时间,因为如果我已经访问了特定子树并访问了他的所有子路径,那么我就不应该再这样做了。@Kfozli,即使删除输出(但仅输出),它仍然是
O(R)
使用
R
所有路径的总长度,因为您仍然必须走每一条路径。“如果我已经访问了特定的子树并访问了他的所有子路径,那么我不应该再这样做”---然后您不打印或不枚举所有可能的路径,因为您省略了其中的一些路径。但这假设如果您遇到与您已经遇到的根节点相同的根节点,您应该返回。实际上,提问者想要的是整个子树与已经遍历的子树相同。Con假设root=1,left=2,right=3。遍历该树,然后遇到