持续学习&持续更新中…
学习态度:脚踏实地
已知斐波那契数列:0、1、1、2、3、5、8、13、21 …
方法一:递归实现
public static int fib1(int n) {
if(n <= 1) return n;
return fib1(n - 2) + fib1(n - 1);
}
方法一时间复杂度分析:
方法二:非递归实现(要求掌握)
public static int fib2(int n) {
if(n <= 1) return n;
int first = 0;
int second = 1;
int sum;
for (int i = 0; i < n - 1; i++) {
sum = first + second;
first = second;
second = sum;
}
return second;
}
上述两种方法的时间复杂度分析:
方法三:数学方程实现
注意:并不是每种算法都会有方程式来解决该问题。
方法四:数组缓存实现(从n-0)
注意:上述分析的数列是从1 1 2 3 5 8 13 21...
开始的,没有0,下述代码是从0开始的。
/*
数组缓存实现
斐波那契数列:0 1 1 2 3 5 8 13 21...
*/
public static int fib3(int n) {
// if (n == 0) return 0;
// else if (n == 1 || n == 2) return 1;
// else {
// final int[] buff = new int[n + 1];
// buff[0] = 0;
// buff[1] = 1;
// buff[2] = 1;
// return fib3(buff, n);
// }
final int[] buff = new int[n + 3];
buff[0] = 0;
buff[1] = 1;
buff[2] = 1;
if (n <= 2) {
if (n == 0) return 0;
else if (n == 1 || n == 2) return 1;
else return -1;
}
return fib3(buff, n);
}
private static int fib3(int[] buff, int n) {
if (buff[n] == 0) {
buff[n] = fib3(buff, n - 1) + fib3(buff, n - 2);
}
return buff[n];
}
方法五:数组缓存实现(从0-n)
/*
fib3数组缓存是从n开始,然后到n-1,n-2,n-3......,显然会有重复调用
可以考虑从0,1,2,3,4...... n实现
*/
private static int fib4(int n) {
// if (n == 0) return 0;
// else if (n == 1 || n == 2) return 1;
// else {
// final int[] buff = new int[n + 1];
// buff[0] = 0;
// buff[1] = 1;
// buff[2] = 1;
//
// int current = 3;
// while (current <= n) {
// buff[current] = buff[current - 1] + buff[current - 2];
// current++;
// }
// return buff[n];
// }
final int[] buff = new int[n + 3]; // 确保至少有3个容量大小
buff[0] = 0;
buff[1] = 1;
buff[2] = 1;
if (n >= 3) {
int current = 3;
while (current <= n) {
buff[current] = buff[current - 1] + buff[current - 2];
current++;
}
}
return buff[n];
}
注意:两种数组缓存方法中,方法五(从0-n)更好,因为没有进行递归操作,没有重复调用,都是直接相加。
方法六:滚动数组
/*
滚动数组来实现
*/
private static int fib5(int n) {
if (n <= 1) return n;
final int[] buff = new int[2];
buff[0] = 0;
buff[1] = 1;
for (int i = 1; i < n; i++) {
final int temp = buff[0] + buff[1];
if (buff[0] < buff[1]) {
buff[0] = temp;
} else {
buff[1] = temp;
}
}
return Math.max(buff[0], buff[1]);
}
位运算对2取模
当n对2进行取模运算时,则根据n的二进制代码最低位就可以得到取模的结果:
- 若n的二进制最低位为1,那么n肯定是奇数,那么n对2取模肯定为1
- 若n的二进制最低位为0,那么n肯定是偶数,那么n对2取模肯定为0
那么如何取出n的最低位呢:
final int a = 1; // 0b0001 & 0b0001 = 0b0001 = 1
final int b = 2; // 0b0010 & 0b0001 = 0b0000 = 0
final int c = 3; // 0b0011 & 0b0001 = 0b0001 = 1
final int d = 4; // 0b0100 & 0b0001 = 0b0000 = 0
因此,n对2取模只需让n&1即可。
注意
-
使用int类型计算斐波那契数列,如果斐波那契数列项数是47(在本程序从0开始是46)的话,就会得到最大值:
1836311903
;因此,如果项数比较多,可以考虑使用更大的数据类型(例如long
,BigDecimal
)来计算斐波那契数列。 -
乘、除、取模运算在编程语言中效率都很低,所以可以使用其它方式来进行优化代码。
参考
李明杰:开发用不到算法?那是因为你不会!.
本文完,感谢您的关注支持!