1. 什么是斐波那契數?
這里我借用百度百科上的解釋:斐波那契數,亦稱之為斐波那契數列(意大利語: Successione di Fibonacci),又稱黃金分割數列、費波那西數列、費波拿契數、費氏數列,指的是這樣一個數列:0、1、1、2、3、5、8、13、21、……在數學上,斐波納契數列以如下被以遞歸的方法定義:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字來說,就是斐波那契數列列由 0 和 1 開始,之后的斐波那契數列系數就由之前的兩數相加。特別指出:0不是第一項,而是第零項。
2. 求第N個斐波那契數
求第N個斐波那契數比較簡單可以直接套用公式n = 0,1 時,fib(n) = 1;n > =2 時,fib(n) = fib(n-2) + fib(n-1)在計算時有兩種算法:遞歸和非遞歸。如下:
1 //非遞歸算法 2 long long fib1(size_t N) { 3 long long a = 0, b = 1, c = 0; 4 if (N < 2) { 5 return N; 6 } 7 else { 8 for (long long i = 2; i <= N; ++i) { 9 c = a + b; 10 a = b; 11 b = c; 12 } 13 } 14 return c; 15 } 16 int main() 17 { 18 printf("%lld", fib1(10)); 19 getchar(); 20 return 0; 21 } //此算法最大的優點是不存在重復計算,故效率比遞歸算法快的多得多。
1 //遞歸算法 2 long long fib2(size_t N) { 3 if (N < 2) 4 return N; 5 return fib2(N - 1) + fib2(N - 2); 6 } 7 int main() 8 { 9 printf("%lld", fib2(10)); 10 getchar(); 11 return 0; 12 }
遞歸可以使程序看起來比較簡潔,但缺點是效率比較低,並且可能導致棧溢出,當需要計算的數稍大一點,就需要很長的計算時間,因此需要靈活使用遞歸。
3. 二分法查找
3.1 二分查找的非遞歸算法
1 template<typename T> 2 T* BinarySearch(T* array,int number,const T& data) //data要查找的數,number查找范圍長度,array要查找的數組 3 { 4 assert(number>=0); 5 int left = 0; 6 int right = number-1; 7 while (right >= left) 8 { 9 int mid = (left&right) + ((left^right)>>1); 10 if (array[mid] > data) 11 { 12 right = mid - 1; 13 } 14 else if (array[mid] < data) 15 { 16 left = mid + 1; 17 } 18 else 19 { 20 return (array + mid); 21 } 22 } 23 return NULL; 24 }
3.2 二分查找遞歸算法
1 template<typename T> 2 T* BinarySearch(T* left,T* right,const T& data) 3 { 4 assert(left); 5 assert(right); 6 if (right >=left) 7 { 8 T* mid =left+(right-left)/2; 9 if (*mid == data) 10 return mid; 11 else 12 return *mid > data ? BinarySearch(left, mid - 1, data) : BinarySearch(mid + 1, right, data); 13 } 14 else 15 { 16 return NULL; 17 } 18 }
4. 時間復雜度與空間復雜度
5. 斐波那契數的時間復雜度與空間復雜度分析
5.1 非遞歸算法時間復雜度分析
使用非遞歸算法求到第n個斐波那契數,是從第2個數開始,將前兩個數相加求求后一個數,再將后一個數賦值給前一個數,再計算前兩個數相加的結果。依次類推直到第n個數,用n-1個數和n-2個數相加求出結果,這樣的好處是,我們只計算了n-1次就求出了結果,即時間復雜度為O(n);我們以代碼中測試數10為例詳解求第十個數的過程。當N=10時,進入函數首先判斷,然后走下面的分支開始計算
計算了N-1次,得出了結果所以時間復雜度是O(N)。
非遞歸算法空間復雜度分析
此函數內部最多時一共開辟了a, b, c, i四個變量空間復雜度是常數,即為O(1)。
5.2 遞歸算法時間復雜度分析
在遞歸算法中,求解fib2(n),把它推到求解fib2(n-1)和fib2(n-2)。也就是說,為計算fib2(n),必須先計算
fib2(n-1)和fib2(n-2),而計算fib2(n-1)和fib2(n-2),時按照表達式及計算法則,需先計算又必須先計算fib2(n-1),而fib2(n-1)由fib2(n-2)和fib2(n-3)計算得來,而這之中的和fib2(n-2)由fib2(n-3)和fib2(n-4)計算得來......依次類推,表面上看不出有何復雜度,但是仔細分析可知,每一個計算fib2(n)的分支都會衍生出計算直至(1)和fib(0),也就是說每個分支都要自己計算數本身到1的斐波那契數列,這樣就增加了龐大且冗雜的運算量,還是以10 為例詳細計算說明
圖中數字代表第N個斐波那契數,圖中沒有全部將計算步驟畫出來,但是已經足夠說明問題,它的每一步計算都被分成計算前兩個斐波那契數,以此類推。那么這就形成了一顆二叉樹,雖然不是滿二叉樹,但是我們分析的是最壞時間復雜度,而且只要估算出來遞歸次數隨N增長的趨勢即可,故可以近似將它看成滿二叉樹,其中的節點數就是計算的次數,也就是復雜度,由公式:節點數=2^h-1(h為樹的高度)可得O(2^n)。
遞歸算法空間復雜度分析:
遞歸最深的那一次所耗費的空間足以容納它所有遞歸過程。遞歸產生的棧偵是要銷毀的,所以空間也就釋放了,要返回上一層棧偵繼續計算+號后面的數,所以它所需要的空間不是一直累加起來的,之后產生的棧偵空間都小於遞歸最深的那一次所耗費的空間。
遞歸的深度*每次遞歸所需的輔助空間的個數 ,所以空間復雜度是:O(N)
6. 求二分法的時間復雜度和空間復雜度
6.1 非遞歸算法分析

6.2 遞歸算法復雜度分析

7. 擴展-----不用循環法和遞歸法求1+2+3+...+N(思考一種復雜度為O(1)的解法)
1 class Temp 2 { 3 public: 4 Temp(){ 5 ++N; 6 Sum += N; 7 } 8 static void Reset(){ 9 N = 0; 10 Sum = 0; 11 } 12 static int GetSum(){ 13 return Sum; 14 } 15 private: 16 static int N; 17 static int Sum; 18 }; 19 int Temp::N = 0; 20 int Temp::Sum = 0; 21 int solution_Sum(int n){ 22 Temp::Reset(); 23 Temp *a = new Temp[n]; 24 delete[]a; 25 a = 0; 26 return Temp::GetSum(); 27 } 28 int main(){ 29 cout << solution_Sum(100) << endl; 30 getchar(); 31 return 0; 32 33 }