【问题标题】:BST search key in C++C++ 中的 BST 搜索键
【发布时间】:2015-12-17 05:58:50
【问题描述】:

我试图实现 BST 搜索方法以在 BST 中查找密钥。下面是代码。

node* search_key(node **root,int key)
{
    if (*root == NULL || (*root)->data == key ){
        return (*root);
    } 
    if ( key < (*root)->data ) {
         search_key(&(*root)->left, key); 
    }
    else{
         search_key(&(*root)->right, key);
    }
}

上面的代码总是返回空值,除了搜索根节点。我将代码修改为以下内容,并且运行良好。 谁能解释一下这里涉及的递归?

node* search_key(node **root,int key)
{
    if (*root == NULL || (*root)->data == key ){
        return (*root);
    } 
    if ( key < (*root)->data ) {
         return search_key(&(*root)->left, key); // note return 
    }
    else{
         return search_key(&(*root)->right, key);
    }
}

【问题讨论】:

  • 我不禁想知道你为什么要传递node **root 而不仅仅是node *root。使用双指针会使代码不必要地笨拙,而不会带来任何明显的好处。如果您要更改 root 指向的值,双指针将是有益的;就目前而言,我没有看到任何好处,并且确实看到了一些不利因素(它会更慢,除此之外,因为它需要额外的间接级别;当然,这种放缓是否可衡量是另一个问题)。
  • 要扩展@JonathanLeffler 的评论,即使您想修改指针,如果您使用 C++ 编程,您可能应该使用引用而不是额外的指针间接。最终结果将是相同的,但语法更好,并且更容易理解该函数对普通读者(将来包括你)的作用。
  • 我只是在试验指针并想深入学习指针,因此我选择通过保持 root 本地而不是全局来使用 node **root。在生产中实施类似的东西时,我会牢记上述建议。感谢您的宝贵反馈。

标签: c++ binary-search-tree


【解决方案1】:

在第一个代码 sn-p 中,您有一个函数应该返回一些东西,但在某些情况下不会这样做。这将导致未定义的行为

在第二个 sn-p 中,您实际上在函数的所有路径中都返回了一些内容。

【讨论】:

  • 大多数编译器都会抱怨第一个代码中缺少返回值,不是吗?忽略编译器警告是一个糟糕的步骤。
  • @JonathanLeffler 是的,即使没有任何特殊标志,Clang 也会发出警告,不幸的是 GCC 不会(它对 -Wall 会发出警告),我不了解 VC++。
  • 哦,是的;即使 GCC 5.3.0 在没有任何警告选项的情况下也不会发出警告。我经常使用更广泛的警告选项(gcc -std=c11 -O3 -g -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -c bst.c)进行编译,以至于我不知道默认情况下提供了哪些警告(而且我不认为我所知道的遗漏也不是一个主要问题!)。
  • 使用 g++ 编译器编译,没有收到任何警告!
【解决方案2】:

当你用一个函数调用同一个函数或另一个函数时,它会调用该函数,当它完成调用时返回调用者函数
这里调用返回给调用者但没有结果,因为你没有返回任何结果。

所以在将值返回给被调用函数后,它开始显示正确的结果。

请关注这张阶乘函数的图片以更好地理解

【讨论】:

    【解决方案3】:

    在二叉树中,左右根有 2 个根。因此,您的第一个条件表示您是否在最后一个节点(其中 root == NULL)或者您找到键(root->data 上的值与键的值匹配)所以,程序返回包含数据的确切块(它不是递归的)。

    if (*root == NULL || (*root)->data == key ){
        return (*root);
    

    第二个条件说如果键小于存储在root-&gt;data 中的值,则节点转到左根。所以,returnsearch_key(root->left,key)` 行的意思是:重做相同的操作,但现在转到左根。

    if ( key < (*root)->data ) {
         return search_key(&(*root)->left, key);
    }
    

    所以,让我们假设在左根中,root-&gt;data 高于键, 所以return search_key(root-&gt;right,key)这行意味着重做这个操作,但现在转到正确的根目录。

    else{
         return search_key(&(*root)->right, key);
    }
    

    只有当您在某处找到密钥或检查所有内容但没有找到任何内容时,递归才会结束。所以请记住,递归意味着一次又一次地重做。

    【讨论】:

    • 我认为您需要处理第一句话:在二叉树中,左右根有 2 个根。 二叉树只有一个根,但每个二叉树中的节点最多可以有两个子节点,左节点和右节点。 (当你有两个根时,你就有了一片森林,而不仅仅是一棵树。)
    【解决方案4】:

    在实现递归时,我们应该始终确保在递归调用函数时,对于每个条件,都应该有一个“return”语句,以便函数的直接调用者能够获得结果值因此在递归结束时将返回最终值。

    如果不使用return语句怎么办:

    如果没有 return 语句,那么当前函数计算的值将被丢弃或不保存,这样直接调用者就不会得到任何值,并且最终会出现未定义的行为。

    【讨论】:

    • 至少需要有一种基本情况不递归调用函数。
    • @JonathanLeffler:你是对的!但在那种情况下,还需要一个“return”声明对吗?如果不是,主函数中的调用者将如何获得最终值。
    • 是的,即使是基本情况也需要返回一个值。我看到的“问题”是我对您的短语的解释始终确保我们使用函数的每个条件。如果这意味着对于每个条件,代码都必须调用函数,那么这是错误的,因为终止递归所需的基本情况——基本情况通常不会再次调用该函数。如果您的意思是对于递归调用函数的每个条件,都应该有一个return 语句,那么您是正确的。我道歉;这是英语使用中的一个微妙问题。
    • @JonathanLeffler- 抱歉..我的评论中有错字..感谢您纠正我的语法:)
    猜你喜欢
    • 2013-04-15
    • 2021-10-27
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2020-07-30
    • 2016-04-01
    • 1970-01-01
    • 2019-04-09
    相关资源
    最近更新 更多