目录
求取斐波那契数列第N位的值。
斐波那契数列:每一位的值等于他前两位数字之和。前两位固定 0,1,1,2,3,5,8...
斐波那契数列通常定义如下:
F(0) = 0
F(1) = 1
- 对于
n > 1
,有F(n) = F(n - 1) + F(n - 2)
暴力递归
public static int calculate (int num) {
if (num == 0) {
return 0;
} else if (num == 1) {
return 1;
} else {
return calculate(num - 1) + calculate(num - 2);
}
}
这个方法的逻辑:
基本情况:
- 如果
num
等于 0,函数返回 0,因为斐波那契数列的第 0 项是 0。- 如果
num
等于 1,函数返回 1,因为斐波那契数列的第 1 项是 1。递归情况:
- 如果
num
大于 1,函数递归地调用自身两次,一次是计算num - 1
的斐波那契数,另一次是计算num - 2
的斐波那契数,并将这两个值相加得到num
的斐波那契数。这种递归方法非常直观,但它的效率并不高。原因在于它进行了大量的重复计算。例如,为了计算
F(5)
,它将计算F(4)
和F(3)
,但在计算F(4)
的过程中,它又会计算F(3)
和F(2)
。这意味着F(3)
被计算了两次,随着num
的增大,重复计算的次数会急剧增加。
这个方法的时间复杂度是指数级的,大约是 O(2^n),因为每增加一个
num
,计算量大约翻倍。对于较大的num
值,这个方法将会非常慢。
记忆化递归
使用记忆化递归的方法来计算斐波那契数列的第 num
项的值。
记忆化是一种优化技术,用于提高递归函数处理重复子问题时的效率,是动态规划的一种形式。在这种方法中,将已经计算过的斐波那契数存储在一个数组 arr
中,以避免重复计算。
public static int calculate2 (int num) {
int[] arr = new int[num + 1];
return recurse(arr, num);
}
public static int recurse (int[] arr, int num) {
if (num == 0) {
return 0;
}
if (num == 1) {
return 1;
}
if (arr[num] != 0) {
return arr[num];
}
arr[num] = recurse(arr, num -1) + recurse(arr, num -2);
return arr[num];
}
这个方法的逻辑:
calculate2
方法初始化一个数组arr
,其长度为num + 1
,用于存储从 0 到num
的所有斐波那契数。然后它调用recurse
方法来实际计算斐波那契数。
recurse
方法是一个递归函数,它接受数组arr
和要计算的斐波那契数的位置num
作为参数。在
recurse
方法中,首先检查基本情况:
- 如果
num
等于 0,返回 0。- 如果
num
等于 1,返回 1。如果
arr[num]
不等于 0,这意味着arr[num]
的值已经被计算过并存储在数组中,直接返回该值,避免了重复计算。如果
arr[num]
等于 0,说明我们尚未计算过num
的斐波那契数。在这种情况下,递归地调用recurse
来计算F(num - 1)
和F(num - 2)
,将结果相加后存储在arr[num]
中,并返回该结果。
通过这种方式,每个斐波那契数只计算一次,其余的计算都通过查找数组
arr
来完成,这极大地提高了效率。这个方法的时间复杂度降为 O(n),因为每个数只需要计算一次,并存储在数组中用于后续的查找。
双指针迭代
基于记忆化递归优化,集合没有必要保存每一个下标值,只需保存前两位即可,向后遍历,得出N的值
public static int iterate (int num) {
if (num == 0) {
return 0;
}
if (num == 1) {
return 1;
}
int low = 0, high = 1;
for (int i = 2; i <= num; i++) {
int sum = low + high;
low = high;
high = sum;
}
return high;
}
这个迭代方法的逻辑:
特殊情况处理:
- 如果
num
等于 0,直接返回 0,因为斐波那契数列的第 0 项是 0。- 如果
num
等于 1,直接返回 1,因为斐波那契数列的第 1 项是 1。初始化两个变量:
low
被初始化为 0,表示斐波那契数列的第 0 项。high
被初始化为 1,表示斐波那契数列的第 1 项。迭代计算:
- 使用一个
for
循环从 2 迭代到num
,在每次迭代中计算当前的斐波那契数。- 在循环内部,计算
sum
为low
和high
的和,这是当前斐波那契数的值。- 然后更新
low
为之前的high
,更新high
为新计算的sum
。返回结果:
- 循环结束后,
high
存储了斐波那契数列的第num
项的值,将其返回作为结果。
这种方法只需要两个变量来存储前两个斐波那契数,并通过迭代更新这两个值来计算整个数列,因此它的空间复杂度为 O(1),即它只需要常量级的额外空间。