C 确保具有多个返回的递归函数”;保留;预期结果

C 确保具有多个返回的递归函数”;保留;预期结果,c,recursion,C,Recursion,考虑以下代码(引用自作者Tushar Roy),如果从根到叶的路径的键总和为指定值,则该代码计算true或false: bool hasPathSum(struct node* node, int sum) { /* return true if we run out of tree and sum==0 */ if (node == NULL) { return (sum == 0); } else { bool ans = 0; /*

考虑以下代码(引用自作者Tushar Roy),如果从根到叶的路径的键总和为指定值,则该代码计算true或false:

bool hasPathSum(struct node* node, int sum)
{
  /* return true if we run out of tree and sum==0 */
  if (node == NULL)
  {
     return (sum == 0);
  }

  else
  {
    bool ans = 0; 

    /* otherwise check both subtrees */
    int subSum = sum - node->data;

    /* If we reach a leaf node and sum becomes 0 then return true*/
    if ( subSum == 0 && node->left == NULL && node->right == NULL )
      return 1;

    if(node->left)
      ans = ans || hasPathSum(node->left, subSum);
    if(node->right)
      ans = ans || hasPathSum(node->right, subSum);

    return ans;
  }
}
在这段代码中,作者在对变量
ans
赋值时使用了逻辑OR运算符,并返回该变量,以避免用返回的false覆盖返回的true。我已将代码重构为:

int hasPathSum(Treelink tree, int sum){
    if ( !tree ) { //succesful path found
        return (sum == 0);
    } else {
        sum -= tree->item;
        if ( (sum == 0) && (!tree->left && !tree->right)) 
            return 1;
        return hasPathSum(tree->left, sum) || hasPathSum(tree->right, sum);
    } 
}
虽然在这种情况下,使用临时变量和/或逻辑or运算符可以有效防止递归返回的覆盖,但在递归调用中向上携带值的最佳方法是什么?

编辑

相反,让我们考虑一个稍微做作的例子;给定一个顺序不正确的二叉树(例如,键100作为根,左子键为5,右子键为2),递归地从树中查找最小键。我会天真地这样处理:

免责声明:已编写但未编译


我们也可能有两个单独的变量(毕竟这是一个二叉树)来指定左min和右min,并且只返回这两个变量中的min。在这些情况下,我仍然在使用某种临时变量,以便将其返回到其原始调用方。最后,我对设计递归算法时避免这些类型错误的一般启发法感到好奇。编辑:我误解了原始问题,因此phoku的答案更相关


C(和大多数其他语言)中的逻辑or运算符执行所谓的短路:一旦找到一个正确的操作数,它就会停止计算操作数。因此,如果
hasPathSum(tree->left,sum)
返回1,
hasPathSum(tree->right,sum)
将永远不会被调用


有更详细的解释。它是为Java编写的,但C中的运算符的工作方式相同。

这里是没有优化的原始示例。你会发现算法现在更简洁了

bool hasPathSum(struct node* node, int sum)
{
  /* return true if we run out of tree and sum==0 */
  if (node == NULL)
     return (sum == 0);

  /* otherwise check both subtrees */
  int subSum = sum - node->data;
  return hasPathSum(node->left, subSum) ||  hasPathSum(node->right, subSum);
}
关于实际问题-如何在这种递归中返回值,其中数据结构中的数据没有约束。返回子树(或您实际看到的任何数据结构)的当前最小值效果很好

简单地考虑问题而不考虑实际的数据结构。

current = useful_start_value
for value in data:
    if binaryOp(current, value):
       current = value   
因此,对于每个值,您都必须根据
当前数据对其进行测试。对于findMin函数,这将产生以下代码。我已经适应了使用原始数据结构

int findMin(struct node* node, int min) {
    if (!node) return min;
    if (node->data < min)
       min = node->data;

    int leftMin = findMin(node->left, min);
    if(leftMin < min)
       min = leftMin;

    int rightMin = findMin(node->right, min);
    if(rightMin < min)
       min = rightMin;

    return min;
}
intfindmin(结构节点*node,intmin){
如果(!node)返回min;
如果(节点->数据<分钟)
min=节点->数据;
int leftMin=findMin(节点->左,min);
if(leftMin右侧,最小值);
if(rightMin
作为这样写的一个侧面,您还可以清楚地看到访问节点的实际顺序

我的一般建议是:

  • 不要过早优化(而且很糟糕)
  • 在源代码中的一个位置执行递归调用(如果可以的话)
  • 试着把这个问题当作局部问题来处理——就像在findMin中,我们只需要找到三个值中的最小值——所以我们就这样做了

我上传了用于测试和生成测试树的附加功能。

您使用的术语“覆盖返回”非常混乱。您没有“覆盖”任何内容,您有一个函数,如果它所做的任何递归调用返回
true
,则返回
true
,否则返回
false
;如果
left
right
中正好有一个为NULL,它将调用
hasPathSum(NULL
),而原来的不为NULL。@Fernando是的,我正试图想出一个更好的术语,但我现在不知所措。我意识到为问题编写递归解决方案有时是行不通的,因为我“clobber results”除非我做了某种检查并确保正确的结果被撤回。@MattMcNabb实际上,重构后的版本应该给出与原始版本相同的结果,不管他是否调用
hasPathSum(NULL)
,因为他处理得很好。他甚至可以删除
if((sum==0)&&(!tree->left&&!tree->right))返回1;
,但仍然会得到相同的结果。值得注意的是,在递归调用中尝试“保留”值与将其视为单个函数调用没有什么不同。您评估某组计算的结果,并返回“正确”的结果“。如果返回到递归调用或从递归调用返回,只要算法设计正确,它就可以正常工作。非BST仍然可以计算一分钟,并且任何设计良好的算法都不能依赖于二叉搜索树的定义(无序)将使用标准的分治方法在两个子项中查找最小值,并返回较小值。只有当左侧返回true(非0)时,逻辑才会短路。如果左侧返回false(0),它将继续计算树的右侧。我想我不明白“覆盖”是什么意思函数本质上是回答一个是或否的问题,因此,一旦您知道“是的,具有给定和的叶的路径存在”,您为什么需要更多信息?如果您回答的问题没有是/否答案,例如“任何路径上的最大和是什么?”或“具有最大和的路径是什么?”那么,想要存储或返回更多信息是有意义的。看起来你有一个有效的问题,我只是还不太确定它是什么(我一开始确实误解了,对不起)。我可能会尝试在以后说更多关于我认为你可能会得到什么,但如果你
current = useful_start_value
for value in data:
    if binaryOp(current, value):
       current = value   
int findMin(struct node* node, int min) {
    if (!node) return min;
    if (node->data < min)
       min = node->data;

    int leftMin = findMin(node->left, min);
    if(leftMin < min)
       min = leftMin;

    int rightMin = findMin(node->right, min);
    if(rightMin < min)
       min = rightMin;

    return min;
}