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);
}
}
这些代码片段工作正常,但我想知道它们是如何工作的? 我需要一个关于递归的简短解释。我很感谢你的帮助。
答案 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();
对它进行操作
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
- 一个整数left
和right
,指向另一片叶子的指针。 left
,right
或两者都可能为NULL,这意味着该方向上没有另一片叶子。
这是一棵树。根部有一个Leaf,您可以跟踪left
或right
指针的跟踪,直到达到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);
}
}
参数来决定是否遵循data
或left
指针,否则它会完全相同。
你应该能够用这种知识理解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种变体的函数:
递归只是循环代码的另一种方式。
答案 4 :(得分:1)
递归算法通常与某种形式的数据结构协同工作 - 在您的情况下是树。您需要将递归 - 非常高的级别 - 想象为“在问题的子集上重新应用相同的逻辑”。
在您的情况下,问题的子集是右侧三个分支或左侧三个分支。
那么,让我们看一下遍历算法:
它传递给方法的叶子 - 如果它是ROOT叶子说明它 然后,如果有一个“左”子叶子,它会显示附加到它的数据并重新启动算法(递归),这意味着...在左侧节点上 如果左节点是ROOT,请说明它(由于ROOT位于顶部,因此在第一次递归后没有机会) 然后,如果我们的左侧节点有一个“左”子叶,显示它并在左侧重新启动算法,左侧
当到达左下角时,即当没有剩下左叶时(跟随?:)),它在第一个右叶上做同样的事情。如果既没有左叶也没有右叶,这意味着我们在没有子叶的真叶上,递归调用结束,这意味着算法再次从递归前的位置开始,并且所有他们所在州的变量。
在第一次递归终止后,您将从左下叶向上移动一片叶子,如果有一片叶子则向右移动,然后再次开始打印并向左移动。
总而言之 - 最终的结果是你以左边的方式穿过整棵树。
告诉我它是否不清楚并尝试在findLeaf递归算法上应用相同的模式。