需要一些关于C树的解释

时间:2013-05-01 13:53:51

标签: c data-structures tree

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);
 }
}

这些代码片段工作正常,但我想知道它们是如何工作的? 我需要一个关于递归的简短解释。我很感谢你的帮助。

5 个答案:

答案 0 :(得分:4)

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 ){

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

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

    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();

对它进行操作

BST

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的性质的博客文章。

答案 1 :(得分:2)

每个Leaf包含三个值:

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

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

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

递归的关键是,如果你按照一个Leaf的路径,剩下的问题是完全相同的(但“一个更小”)就像你在根时遇到的问题。所以你可以调用相同的函数来解决问题。最终例程将在一个以NULL为指针的Leaf上,你已经解决了这个问题。

在理解树之前理解列表可能最容易。因此,不是具有两个指针{@ 1}}和left的Leaf,而是只有一个指针right的节点。要以递归方式跟踪列表:

next

你的findLeaf有什么不同?好吧,它使用Node findEnd(Node node) { if(node->next == NULL) { return node; // Solved!! } else { return findEnd(node->next); } } 参数来决定是否遵循dataleft指针,否则它会完全相同。

你应该能够用这种知识理解right。它使用相同的递归原理来访问结构中的每个Leaf。

答案 2 :(得分:1)

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

让我们假设您要计算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

答案 3 :(得分:1)

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

  1. 解决问题的一步,并用问题的其余部分调用自己
  2. 解决问题的最后一步
  3. 递归只是循环代码的另一种方式。

答案 4 :(得分:1)

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

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

那么,让我们看一下遍历算法:

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

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

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

总而言之 - 最终的结果是你以左边的方式穿过整棵树。

告诉我它是否不清楚并尝试在findLeaf递归算法上应用相同的模式。