通过树

时间:2015-06-26 11:35:32

标签: algorithm recursion tree

我以深度优先的方式遍历树的节点。假设树如下: enter image description here

现在,假设我在节点E中,并且对于某些情况,我想返回节点C并从那里继续。然后应取消先前的遍历,并再次评估节点CDE。由于先前的递归导航已被取消,因此不应遍历节点FG

Usual navigation : A B C D E F G
The desire navigation : A B C D E C D E F G

深度优先遍历的一般代码如下:

void DFS(node x)
{
    z = evaluate(x);
    // if (z != null) DFS(z) 
    // Z could be a node which has been already traversed, 
    // let's suppose it's an ancestor of x

    foreach (node y in c.children)
    {
        DFS(y);
    }
}

请帮助我如何在树中进行此类导航?

3 个答案:

答案 0 :(得分:2)

我将尝试使用全局变量cancel来概述伪代码。

boolean cancel = false;

void DFS(node x, parent p)
{
    if(!cancel) {
        foreach (node y in x.children) {
            DFS(y, x);
        }
    } else {
      cancel = false;
      DFS(p, findParent(p));
    }
}

然而,这种方法存在问题。一旦在foreach部分中开始遍历,循环内对DFS方法的每次后续调用都将从父节点调用DFS。为了解决这个问题,我建议你使用自己的堆栈来模拟深度优先遍历,而不是采用递归方法。这样,当cancel变为true时,您可以清除堆栈并确保来自父级的DFS调用仅发生一次。希望这有帮助!

以下几行中的某些内容应该有效:

boolean cancel = false;
Stack<Node> s;

void DFSIterative(Node x, Node p) {
    if(cancel) {
        resetDFS(p);
    } else {
        s.push(x);
        while(!s.isEmpty()) {
            x = s.pop();
            p = findParent(x);
            if(cancel) resetDFS;
            else {
                foreach(node y in x.children) {
                    s.push(y);
                }
            }
        }
    }
}

void resetDFS(Node p) {
    s.clear();
    cancel = false;
    DFSIterative(p, findParent(p));
}

我将findParent()辅助方法的实现留给你。请注意,您还需要注意将节点标记为已访问,然后在取消DFS时将相关节点取消标记为未访问。

答案 1 :(得分:1)

根据你想要去的树的备份距离,这样的事情应该有效。

DFS函数返回要重试的级别数:

  • 0正常进行
  • 1重试同一节点
  • 2重试父母......

代码:

int DFS(node x)
{
    if (some condition)
    {
        // return the number of parent levels you want to back up
        return 2;
    }

    for (int i = 0; i < x.children.size; ++i)
    {
        int redo = DFS(x.children[i]);

        if (redo == 1) {
            // redo == 1 means retry the current node
            --i;
        }
        if (redo > 1) {
        {
            // redo > 1 means retry an ancestor node
            return redo - 1;
        }
    }
    return 0;
}

显然你必须小心自己的状况,否则你将陷入无限循环。

使用上面的基本代码,以下条件将返回A B C D E C D E F G

boolean retryE = true;

int DFS(node x)
{
    if (x.value == "E" && retryE)
    {
        retryE = false;
        return 2;
    }

    // remaining code as above
}

<强>更新

再看一下,如果你的evaluate函数返回一个祖先节点而不是多个级别,这可能更接近你原来想要的......如果返回的节点不是当前子节点的祖先,则会失败... < / p>

// returns null to continue DFS, or a node value to repeat from that node
Node DFS(Node x)
{
    Node z = evaluate(x)

    if (z != null)
    {
        return z;
    }

    for (int i = 0; i < x.children.size; ++i)
    {
        Node child = x.children[i];
        Node result = DFS(child);

        if (result != null)
        {
            if (result == child)
            {
                // current child is the one to retry so just
                // decrement the counter to retry it
                --i;
            } else {
                // retry a node but not this child so return it up the stack
                return result;
            }
        }
    }

    return null;
}

更新2

使用相同的DFS函数,考虑这个evaluate函数,该函数在E和F的第一次出现时返回C

boolean retryE = true;
boolean retryF = true;

evaluate(Node x)
{
    if (x.value == "E" && retryE)
    {
        retryE = false;
        return C;
    }
    if (x.value == "F" && retryF)
    {
        retryF = false;
        return C;
    }
    return null;
}

这将使用--i递减方法(返回A B C D E - C D E F - C D E F G)正常工作,但如果直接调用DFS(child)则不行,除非以某种方式处理第二次调用的结果。

干杯

答案 2 :(得分:0)

看到这里我可以看到你使用了一个void DFS,你的函数没有返回任何东西,所以你可以使用那个值来检查是否需要重新评估某些东西。

喜欢这个

int DFS(node x)
{
    int ret=0;
    z = evaluate(x);
    // if (z != null) DFS(z) Z could be a node which has been already traversed 
    foreach (node y in c.children)
    {
        ret=DFS(y);
        if(ret==1)
          break;
    }
    if(ret==1)
       DFS(x);

    if(z==(want to reevaluate))
        return 1;
    else
        return 0;
}

现在通过这个你可以简单地返回到父1,如果你想让它重做所有孩子的DFS,你可以简单地返回0,如果你想让它简单地继续。 如果A的任何子节点在这种情况下返回1,则将重新评估所有子节点和该节点,并且其上方的节点将以与它们相同的方式继续。

所以你的形象。如果E返回1,那么所有的节点C,D,E都将被重新评估。如果您将返回值固定为返回距离或其他内容,那么也可以使用变量来完成,您只需要将其地址发送给所有孩子并观察其值。