【问题标题】:Need some explanation about trees in C需要一些关于 C 中的树的解释
【发布时间】:2013-05-01 13:53:51
【问题描述】:
Leaf *findLeaf(Leaf *R,int data)
{
  if(R->data >= data )
  {
    if(R->left == NULL) return R;
    else return findLeaf(R->left,data);
  }
  else
  {
     if(R->right == NULL) return R;
     else return findLeaf(R->right,data);
  }
}


void traverse(Leaf *R)
{
 if(R==root){printf("ROOT is %d\n",R->data);}
 if(R->left != NULL)
 {
    printf("Left data %d\n",R->left->data);
    traverse(R->left);
 }
 if(R->right != NULL)
 {
    printf("Right data %d\n",R->right->data);
    traverse(R->right);
 }
}

这些代码 sn-ps 工作正常,但我想知道它们是如何工作的? 我需要关于递归的简要说明。感谢您的帮助。

【问题讨论】:

标签: c data-structures tree


【解决方案1】:

Leaf 结构将如下所示:

typedef struct struct_t {
    int data;
    Leaf * left;   //These allow structs to be chained together where each node 
    Leaf * right;  //has two pointers to two more nodes, causing exponential
} Leaf;            //growth.

该函数接受一个指向我们称为 R 的 Leaf 的指针和一些要搜索的数据,它返回一个指向 Leaf 的指针

Leaf *findLeaf(Leaf *R,int data){

这段代码决定了我们应该向左还是向右,这棵树是有序的,因为插入函数遵循相同的左右规则。

  if(R->data >= data ){

这是函数递归性质的边缘情况,如果我们已经到达树中的最后一个节点,称为叶子,则返回该叶子。

递归函数的边缘情况具有结束递归并返回结果的任务。没有这个,函数将无法完成。

    if(R->left == NULL) return R;

这就是我们遍历树的方式,在这里,我们沿着左侧向下遍历,因为数据更大。 (较大的数据始终插入左侧以保持有序。) 正在发生的事情是,现在我们用R->left 调用 findLeaf(),但想象一下,如果我们在下一次调用中再次到达这一点。

参考第一次调用,它将变为R->left->left。如果数据小于我们正在操作的当前节点,我们将改为正确。

    else return findLeaf(R->left,data);

现在我们处于数据小于当前节点的情况,所以我们走对了。

  } else {

这和左边完全一样。

     if(R->right == NULL) return R;
     else return findLeaf(R->right,data);
  }
}

最后,函数的返回可以概念化为R->right->right->left->NULL

让我们获取这棵树并使用 findLeaf() 对其进行操作

findLeaf(Leaf * root, 4) //In this example, root is already pointing to (8)

我们从根开始,在树的顶部,它包含 8 个。

首先我们检查R->data >= data,我们知道R->data(8)data(4)。由于我们知道data 小于R->data(当前节点),我们输入if 语句。

这里我们对左边的Leaf进行操作,检查是否为NULL。它不是,所以我们跳到else

现在我们返回findLeaf(R->left, data);,但是要返回它,我们必须先解决它。这导致我们进入第二次迭代,我们将(3)(4) 进行比较并重试。

再过一遍整个过程,我们会比较(6) to (4),然后当我们比较(4) to (4)时最终找到我们的节点。现在我们将回溯该函数并返回如下所示的链:

R(8)->(3)->(6)->(4)

编辑:另外,巧合的是,我写了一篇关于遍历链表的博客文章,以解释二叉搜索树的本质here

【讨论】:

  • 我添加了更多细节和一棵树的演练。给你@user1941070
  • @user1941070 请注意,它与 traverse 函数的概念完全相同,只是边缘情况刚好到达函数的末尾。
【解决方案2】:

每个叶子包含三个值:

  • data - 一个整数
  • leftright,都是指向另一个叶子的指针。

leftright 或两者都可能为 NULL,这意味着该方向没有另一片叶子。

所以那是一棵树。根有一个 Leaf,您可以跟踪 leftright 指针,直到到达 NULL。

递归的关键是,如果你沿着路径走一叶,剩下的问题与你在根时遇到的问题完全相同(但“小一个”) .所以你可以调用相同的函数来解决问题。最终,例程将位于以 NULL 为指针的 Leaf,并且您已经解决了问题。

在了解树之前了解列表可能是最容易的。因此,不是带有两个指针的 Leaf,leftright,而是一个只有一个指针的节点,next。递归地跟随列表到其末尾:

Node findEnd(Node node) {
     if(node->next == NULL) {
        return node; // Solved!!
     } else {
        return findEnd(node->next);
     }
}

您的 findLeaf 有什么不同?嗯,它使用data参数来决定是跟随left还是right指针,否则完全一样。

有了这些知识,您应该能够理解traverse()。它使用相同的递归原理来访问结构中的每个 Leaf。

【讨论】:

    【解决方案3】:

    递归是将问题分解为两种变体的函数:

    1. 解决问题的一步,然后用剩下的问题调用自己
    2. 解决问题的最后一步

    递归只是循环代码的另一种方式。

    【讨论】:

    • 如果不使用遍历函数中的return语句,工作原理是否相同?
    • 是的。唯一的区别是是否有返回值。 Traverse 不返回任何内容。
    【解决方案4】:

    递归算法通常与某种形式的数据结构一起工作 - 在您的情况下是树。您需要将递归(非常高级)想象为“在问题的子集上重新应用相同的逻辑”。

    在您的情况下,问题的子集是右侧三个的分支或左侧三个的分支。

    那么,我们来看看遍历算法:

    它将您传递给方法的叶子和 - 如果它是根叶子声明它 然后,如果有一个“左”子叶,它会显示附加到它的数据并重新启动算法(递归),这意味着......在左节点上 如果左节点是ROOT,说明它(第一次递归后没有机会,因为ROOT在顶部) 然后,如果我们的左节点有一个“左”子叶,则显示它并在这个左,左重新启动算法

    当到达左下角时,即当没有左叶时(跟随?:)),它在第一片右叶上做同样的事情。如果既没有左叶也没有右叶,这意味着我们在没有子叶的真实叶处,递归调用结束,这意味着算法从递归之前的位置重新开始,并且所有他们当时所处状态的变量。

    在第一次递归终止后,你将从左下叶向上移动一叶,如果有则从右叶向下移动,然后再次开始打印并在左侧移动。

    总而言之 - 最终结果是您以左侧优先方式穿过整棵树。

    如果不是很清楚,请告诉我,并尝试在 findLeaf 递归算法上应用相同的模式。

    【讨论】:

    • 它从根开始,到最左边的叶子,然后从底部返回到根。返回时它访问右叶。当它到达根时,开始遍历右子树。我理解很好。谢谢。
    【解决方案5】:

    关于递归的一点评论,然后是关于在树上搜索的一点评论:

    假设您要计算 n!。你可以做(​​伪代码)

    fac 0     = 1
    fac (n+1) = (n+1) * fac n
    

    因此,递归是通过操纵用较小数据解决相同问题的结果来解决问题。见http://en.wikipedia.org/wiki/Recursion

    现在,假设我们有一个数据结构树

    T = (L, e, R)
    

    L 是左子树,e 是根,R 是右子树......所以假设你想在那棵树中找到值 v,你会这样做

    find v LEAF      = false // you cant find any value in an empty tree, base case
    find v (L, e, R) = 
    
    if v == e 
      then something(e) 
    else
      if v < e 
        find v L  (here we have recursion, we say 'go and search for v in the left subtree)
      else
        find v R  (here we have recursion, we say 'go and search for v in the right subtree)
      end
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多