由于为计算a 的值而进行的重复递归调用,此递归函数的堆栈最初会增长;也就是说,我们会一直调用q1(x/2),直到x/2 < 1,在这种情况下,我们已经达到了递归的基本情况,可以简单地返回1。
每次我们从最初的q1(x/2) 调用之一返回时,我们都必须遵循用于计算b 的q1(x-2) 调用。该递归调用还将对a 进行一系列连续递归调用(因为a 在您的函数中首先计算),它们遵循相同的规则;在每个返回后,我们对b 进行递归调用,并重复此过程,直到我们到达所有调用分支中的基本情况。
这是堆栈的外观。阅读它的顺序是尽可能先按照垂直箭头,然后返回,然后按照对角箭头。遵循对角箭头后重复此过程。当没有箭头可以跟随时,返回。
顺便说一句,返回函数的堆栈帧将被完全释放,并且如果进行了新的函数调用,它将取而代之。您可以看到,在任何给定时间,活动的堆栈帧都不超过 4 个。当最后一个最顶层的栈帧完成后,它被释放,它的位置被下面和右边的栈占据。你从那里回来,等等......
希望这张图能帮助你理清思路。
| | |
| | |
| | |
+--------+ | | |
| a = | | | |
| b = | | | |
+--------+ | | |
| x = 0 |
+--------+
returns 1
^ +--------+
| | a = |
| | b = |
| +--------+
| / | x = -1 |
| / +--------+
| / returns 1
| /
+--------+ /
| a = 4 | /
| b = 3 |/
+--------|
| x = 1 |
+--------+
returns 7
^ +--------+
| | a = |
| | b = |
| +--------+
| / | x = 0 |
| / +--------+
| / returns 1
| /
+--------+ /
| a = 10 | /
| b = 3 |/
+--------+
| x = 2 |
+--------+
returns 13
^ +--------+
| | a = |
| | b = |
| +--------+
| | x = 0 |
| +--------+
| returns 1
|
| ^ +--------+
| | | a = |
| | | b = |
| | +--------+
| | / | x = -1 |
| | / +--------+
| | / returns 1
| | /
| +--------+ /
| | a = 4 | /
| | b = 3 |/
| +--------+
| | x = 1 |
| +--------+
| returns 7
|
| ^ +--------+
| | | a = |
| | | b = |
| | +--------+
| | | x = 0 |
| | +--------+
| | returns 1
| |
| | ^ +--------+
| | | | a = |
| | | | b = |
| | | +--------+
| | | / | x = -1 |
| | | / +--------+
| | | / returns 1
| | | /
| | +--------+ /
| | | a = 4 | /
| | | b = 3 |/
| | +--------+
| | / | x = 1 |
| | / +--------+
| | / returns 7
| | /
| | /
| +--------+ /
| | a = 10 |/
| | b = 15 |
| +--------+
| / | x = 3 |
| / +--------+
| / returns 25
| /
+--------+ /
| a = 16 | /
| b = 51 |/
+--------+ | | |
| x = 5 | | | |
+--------+ | | |
returns 67 | | |
| | |
| | |
| | |
Brofessor 有一个很好的理论方法,但他说的有些不准确;当他说q1(x/2) 的递归速度比q1(x-2) 快时,他的意思是前者将比后者更快地达到其基本情况。考虑大于 5 的数字。对于较大的 x 值,x/2 远小于 x-2。因此,x-2 的情况最终会比x/2 的情况进行更多的递归调用,因此x-2 调用主导了堆栈的增长。
例如,q1(64) 将对q1(x/2) 进行 7 次递归调用(64/2、32/2、...、1/2 = 0)。但它会对q1(x-2) (64-2, 62-2, 60-2, ..., 2-2 = 0) 进行更多的递归调用。
在他的画中,如果右子树更大一些会更真实,因为该子树需要更长的时间才能触底。事实上,你可以在我的图表中看到这一点。如果将垂直和对角箭头视为树的分支,则使用 x/2 的第一个递归调用的子树只有 5 个节点,而使用 x-2 的第一个递归调用的子树有 7 个节点。几乎总是如此。