C 使用带树的回溯

C 使用带树的回溯,c,tree,stack,backtracking,C,Tree,Stack,Backtracking,我在树中使用了回溯算法,这要感谢一个带有push和pop的堆栈。 它能工作,但我有个问题。堆栈给出的路径是错误的 bool Prefix(Node*root, stack *tas, char *search) { if (!isEmpty(root)) { if (strcmp(search,root->data) != 0) push(tas, root->data, search, root); if (

我在树中使用了回溯算法,这要感谢一个带有push和pop的堆栈。 它能工作,但我有个问题。堆栈给出的路径是错误的

bool Prefix(Node*root, stack *tas, char *search)
{
    if (!isEmpty(root))
    {
        if (strcmp(search,root->data) != 0)
            push(tas, root->data, search, root);

        if (strcmp(search,root->data) == 0)
        {
            return True ;
        }

        Prefix(root->left, tas, search);
        Prefix(root->right, tas, search);
    }
    return False;
}
例如,我有一棵树:

     Root
    /    \    
   A      B
  / \    / \
 C   D  E   F    
例如,当我想要C的路径时,这个函数返回R和A的R A B ok,而不是B

我不知道是这个功能还是推功能。 如果您在函数中没有看到任何错误,我将粘贴push,但它相当长

编辑:我现在更明白了, 我在函数中添加了: 如果节点是叶,则弹出。 如果我搜索F,它会返回R A B F而不是R B F。我不知道如何避免在堆栈中保留A

编辑2:

下面是代码:返回R A B F而不是R B F

bool Prefix(Node*root, stack *tas, char *search)
{
    if (!isEmpty(root))
    {
        if (strcmp(search,root->data) == 0)
            return True ;

        if (LeafOrNot(root) == True ){  //it's a leaf, pop()
            pop(tas);

        if (Prefix(root->left, tas, search))
            return True;
        if (Prefix(root->right, tas, search))
            return True;
    }
    return False;
}
我不明白如何弹出遍历子节点以获得好的结果,可能是添加 a如果前缀为root->left,tas,search,还有其他选项吗?有人有主意吗


谢谢

至少有一个问题是,您没有检查对Prefix的调用的返回值,因此您不知道递归调用是否完成,您应该停止探索

查看这一点的一个简单方法是浏览给定示例树的函数调用:

Prefix("Root") found "C"? no: Prefix("A") found "C"? no: Prefix("C") found "C"? yes: return true (without check of Prefix("C")): Prefix("D") found "C"? no: return false Prefix("B") found "C"? no: Prefix("E") found "C"? no: return false Prefix("F") found "C"? no: return false return false return false 这显示了调用的顺序和缩进大致对应于调用堆栈上的帧


您可以看到,检查对Prefix的调用是否返回true将允许您在适当的时间退出。

至少有一个问题是您没有检查对Prefix的调用的返回值,因此您不知道递归调用是否完成,您应该停止探索

查看这一点的一个简单方法是浏览给定示例树的函数调用:

Prefix("Root") found "C"? no: Prefix("A") found "C"? no: Prefix("C") found "C"? yes: return true (without check of Prefix("C")): Prefix("D") found "C"? no: return false Prefix("B") found "C"? no: Prefix("E") found "C"? no: return false Prefix("F") found "C"? no: return false return false return false 这显示了调用的顺序和缩进大致对应于调用堆栈上的帧


您可以看到,检查对Prefix的调用是否返回true将允许您在适当的时间退出。

我不得不同意@Mark Elliot的观点,但乍一看,他的答案可能令人困惑。您确实需要一个停止条件,这样您就不会继续探索其他节点并将它们添加到堆栈中。您将返回一个bool,以便您可以使用它来测试调用是否找到您要查找的节点

如果您打算在堆栈中包含C路径中的最后一个节点,那么在添加到堆栈时应该删除字符串compare

例如,您可以这样做

bool Prefix(Node*root, stack *tas, char *search)
{
    if (!isEmpty(root))
    {
        push(tas, root->data, search, root);

        if (strcmp(search,root->data) == 0)
        {
            return True;
        }

        if (Prefix(root->left, tas, search))
            return True;
        if (Prefix(root->right, tas, search))
            return True;
    }
    return False;
}

我不得不同意@Mark Elliot的观点,但乍一看,他的回答可能令人困惑。您确实需要一个停止条件,这样您就不会继续探索其他节点并将它们添加到堆栈中。您将返回一个bool,以便您可以使用它来测试调用是否找到您要查找的节点

如果您打算在堆栈中包含C路径中的最后一个节点,那么在添加到堆栈时应该删除字符串compare

例如,您可以这样做

bool Prefix(Node*root, stack *tas, char *search)
{
    if (!isEmpty(root))
    {
        push(tas, root->data, search, root);

        if (strcmp(search,root->data) == 0)
        {
            return True;
        }

        if (Prefix(root->left, tas, search))
            return True;
        if (Prefix(root->right, tas, search))
            return True;
    }
    return False;
}

此外,海报是否需要同时添加这两个分支?您至少可以看到每一步都得到了什么。简单printf可以提供帮助:@Mitch:否则该算法将如何探索这两个子节点?@Mitch:假设您要查找到D的路径,而您只探索左分支,则无法找到该路径,尽管存在一条路径。OP的函数只记录算法访问过的节点,而不是所有候选访问,因此要枚举路径,它必须搜索所有可用的分支。@米奇:当然,二叉搜索树有这个属性,直二叉树没有排序要求。鉴于OP的原始功能和描述,我认为没有理由认为它是BST。此外,OP公开声明使用回溯算法,这需要……回溯。此外,海报是否需要同时添加两个分支?你至少可以看到每一步都得到了什么。简单printf可以提供帮助:@Mitch:否则该算法将如何探索这两个子节点?@Mitch:假设您要查找到D的路径,而您只探索左分支,则无法找到该路径,尽管存在一条路径。OP的函数只记录算法访问过的节点,而不是所有候选访问,因此要枚举路径,它必须搜索所有可用的分支。@米奇:当然,二叉搜索树有这个属性,直二叉树没有排序要求。鉴于OP的原始功能和描述,我认为没有理由认为它是BST。此外,OP公开声明使用回溯算法,这需要…回溯这里的推力过大;唯一需要存储在堆栈上的是节点*。一种有效的方法是从程序堆栈中分配节点堆栈元素,然后在找到匹配节点时处理节点堆栈。e、 例如,typedef结构NodeStack NodeStack;结构NodeStack{Node*节点;NodeStack*链接;}。。。bool PrefixNode*node,NodeStack*link,char*search{if!isEmptynode{NodeStack elem={node,link};ifstrcmpsearch,node->data==0{processStack&elem;return true;}ifPrefixnode->left,&elem,search return true;等等。另外,尾部r
只要改变if,ecursion就可以变成迭代!我是空的。。。等待一段时间并替换第二个ifPrefix。。。使用node=node->right;我将根替换为节点,因为它是任意节点,而不是根;不好的名字会导致不好的代码。所有的优点都是好的,但正如你所看到的,我的代码示例只是对原始代码的一个轻微修改,以指出马克的答案。是的,我对它投了更高的票,但这是一个教育网站。我不是批评你的代码,而是批评OP的push调用,并指出了额外的优化。注:我之所以在这里发表这些评论,是因为你的评论是最好的答案,也是OP最有可能看到的地方,因此是指出这些改进的最佳地方;唯一需要存储在堆栈上的是节点*。一种有效的方法是从程序堆栈中分配节点堆栈元素,然后在找到匹配节点时处理节点堆栈。e、 例如,typedef结构NodeStack NodeStack;结构NodeStack{Node*节点;NodeStack*链接;}。。。bool PrefixNode*node,NodeStack*link,char*search{if!isEmptynode{NodeStack elem={node,link};ifstrcmpsearch,node->data==0{processStack&elem;return true;}ifPrefixnode->left,&elem,search return true;等等。另外,只需将if!isEmpty…改为一段时间,并将第二个ifPrefix…替换为node=node->right;我将root替换为node,因为它是一个任意节点,而不是根;坏名称会导致坏代码。所有优点都是尽可能的看,我的代码示例只是对原始代码的一个小小修改,以指出Mark的答案。是的,我对它进行了投票,但这是一个教育网站。我不是批评你的代码,而是批评OP的推送调用,并指出了额外的优化。注意:我之所以在这里包含这些评论,是因为你的是最好的答案,在哪里OP最有可能是查看的,因此是指出这些改进的最佳位置。您就快到了。如果您从堆栈中弹出,不仅是在叶节点,而是在遍历任何子节点后,这将为您提供所需的结果。只需确保在找到节点时不会弹出堆栈。如果您正在搜索D,则会弹出堆栈按根键,按A键,按C键,按C键,按D键,找到…将调用链一直向后展开。如果您正在搜索F:按根键,按A键,按C键,按C键,按D键,按D键,按A键,按B键,按E键,按E键,按F键,找到,解开…希望这有帮助。我尝试了另一个poptas,但它不起作用。我需要更改代码的结构?谢谢'就快到了。如果您不仅在叶节点,而且在遍历任何子节点后从堆栈中弹出,这将为您提供所需的结果。只要确保在找到节点时不会从堆栈中弹出。如果您正在搜索D,它将是push Root,push A,push C,pop C,push D,found…一路放回调用cha如果你正在搜索F:push Root,push A,push C,pop C,push D,pop A,push B,push E,pop E,push F,found,unwind…希望这能帮上忙。我尝试过另一个poptas,但它不起作用。我需要更改代码的结构?谢谢