- 斐波那契数列(面试题9)
- 青蛙跳台/变态青蛙跳台(变形1)
- 矩形覆盖(变形2)
1.斐波那契数列(面试题9)
题目:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。斐波那契数列的定义如下:
公式如下:
- 有公式自然很简单,但是这道题的重点在于时间复杂度,递归的时间复杂度是O(2^n),循环的时间复杂度度是O(n),要用哪种写法显而易见
- 下面给出的代码还有一种O(logn)的实现方式,但是对于这个类型的问题我觉得太复杂了,看看就行,其采用的方法如下:
- 用后面这个公式递归计算
#include<iostream>
#include<time.h>
using namespace std;
long long Fibonacci_Recursive(unsigned int n)
{
if(n<=0) return 0;
if(n==1) return 1;
return Fibonacci_Recursive(n-1)+Fibonacci_Recursive(n-2);
}
long long Fibonacci_Iterative(unsigned int n)
{
if(n==0) return 0;
if(n==1) return 1;
long long FibN=0,FibSecond=1,FibFirst=0;
for(unsigned i=2;i<=n;++i)
{
FibN = FibSecond + FibFirst;
FibFirst = FibSecond;
FibSecond = FibN;
}
return FibN;
}
struct Matrix2By2 //矩阵定义
{
long long m_00;
long long m_01;
long long m_10;
long long m_11;
Matrix2By2(
long long m00 = 0,
long long m01 = 0,
long long m10 = 0,
long long m11 = 0
):m_00(m00),m_01(m01),m_10(m10),m_11(m11){}
};
Matrix2By2 MatrixMultiply(const Matrix2By2 &matrix1,const Matrix2By2 &matrix2) //矩阵相乘
{
return Matrix2By2(
matrix1.m_00*matrix2.m_00 + matrix1.m_01*matrix2.m_10,
matrix1.m_00*matrix2.m_01 + matrix1.m_01*matrix2.m_11,
matrix1.m_10*matrix2.m_00 + matrix1.m_11*matrix2.m_10,
matrix1.m_10*matrix2.m_01 + matrix1.m_11*matrix2.m_11
);
}
Matrix2By2 MatrixPower(unsigned n) //矩阵的幂运算
{
Matrix2By2 matrix;
if(n==1)
matrix = Matrix2By2(1,1,1,0);
else if(n%2 == 0){
matrix = MatrixPower(n/2);
matrix = MatrixMultiply(matrix,matrix);
}else if(n%2 == 1){
matrix = MatrixPower((n-1)/2);
matrix = MatrixMultiply(matrix,matrix);
matrix = MatrixMultiply(matrix,Matrix2By2(1,1,1,0));
}
return matrix;
}
long long Fibonacci_MatrixRecursive(unsigned n)
{
if(n==0) return 0;
if(n==1) return 1;
Matrix2By2 result = MatrixPower(n-1);
return result.m_00;
}
int main()
{
clock_t start,step1,step2,step3;
const unsigned num=10;
start = clock();
//Test1
cout<<num<<": "<<Fibonacci_Recursive(num)<<endl;
step1 =clock();
cout<<"cost "<<double(step1-start)/CLOCKS_PER_SEC<<" s"<<endl;
//Test2
cout<<num<<": "<<Fibonacci_Iterative(num)<<endl;
step2 =clock();
cout<<"cost "<<double(step2-step1)/CLOCKS_PER_SEC<<" s"<<endl;
//Test3
cout<<num<<": "<<Fibonacci_MatrixRecursive(num)<<endl;
step3 =clock();
cout<<"cost "<<double(step3-step2)/CLOCKS_PER_SEC<<" s"<<endl;
}
测试用例:
- 功能测试(如输入3、5、10等)
- 边界值测试(如输入0、1、2)
- 性能测试(输入较大数字,如40、50、100)
斐波那契的变形题目如下:
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个n级的台阶共有多少种跳法。
分析思路:
- 如果只有1级台阶:1种跳法;如果只有2级台阶:2种跳法;
- 如果有n级台阶:
- 假设第一次跳1级,则跳法等于剩下(n-1)级的跳法
- 假设第一次跳2级,则跳法等于剩下(n-2)级的跳法
- 因此:f(n) = f(n-1)+f(n-2) n>2时
- 显然这是个斐波那契问题
分析思路:
- 假设有n级台阶有f(n)种跳法,若n=1,f(1)=1
- 若n=2,f(2)=f(1)+1,这个1代表可以直接到达
- 若n=3,f(3)=f(2)+f(1)+1;
- 同理,错位相减法
- 当n=n时,f(n)=f(n-1)+f(n-2)+……+f(2)+f(1)+1;
- 当n=n-1时,f(n-1)=f(n-2)+f(n-3)+……+f(2)+f(1)+1;
- 两式相减,f(n)-f(n-1)=f(n-1) => f(n) = 2*f(n-1)=2²*f(n-2)=……=2^(n-1);
- 由以上数学归纳得,f(n)=2^(n-1);
题目:用2×1的小矩形横着或者竖着去覆盖更大的矩形。请问用8个2×1的小矩形无重叠的覆盖一个2×8的大矩形,共有多少种方法
分析思路:
- 设覆盖2×8的方法为f(8)
- 若第一个竖着放,则接下来要计算覆盖2×7的方法f(7)
- 若第一个横着放,则其下方必定是横着放一个2×1,接下来要计算覆盖2×6的方法f(6)
- 因此f(8) = f(7)+f(6)
- 根据以上分析,这是一个类似斐波那契数列的问题,需确定出f(1)=1,f(2)=2;
#include<iostream>
#include<math.h>
using namespace std;
long long FrogJump_Recursive(unsigned int n) //普通递归
{
if(n<1) throw exception("input error");
if(n==1) return 1;
if(n==2) return 2;
return FrogJump_Recursive(n-1)+FrogJump_Recursive(n-2);
}
long long FrogJump_Iterative(unsigned int n) //循环,青蛙跳台
{
if(n<1) throw exception("input error");
if(n==1) return 1;
if(n==2) return 2;
long long FrogN=0,FootStep1=1,FootStep2=2;
for(unsigned i=3;i<=n;++i)
{
FrogN = FootStep2 + FootStep1;
FootStep1 = FootStep2;
FootStep2 = FrogN;
}
return FrogN;
}
long long FrogJumpN_Iterative(unsigned n) //变态青蛙跳台
{
if(n>0)
return pow(2,n-1);
else
return -1;
}
long long SmallRectangleCoverBigRectangle(unsigned n) //n代表2×n
{
if(n==1) return 1;
if(n==2) return 2;
long long MethodN=0,MethodTwo=2,MethodOne=1;
for(int i=3;i<=n;++i)
{
MethodN = MethodTwo+MethodOne;
MethodOne = MethodTwo;
MethodTwo = MethodN;
}
return MethodN;
}
int main()
{
const unsigned num=8;
//Test1
cout<<num<<"青蛙跳台: "<<FrogJump_Iterative(num)<<endl;
//Test2
cout<<num<<"变态青蛙跳台: "<<FrogJumpN_Iterative(num)<<endl;
//Test3
cout<<num<<"矩形覆盖:"<<SmallRectangleCoverBigRectangle(num)<<endl;
return 0;
}