1. 程式人生 > >【程式設計5】斐波那契數列 + 遞迴+LeetCode50

【程式設計5】斐波那契數列 + 遞迴+LeetCode50

傳送門:https://leetcode-cn.com/problems/powx-n/

一、LeetCode 50. Pow(x, n)

1、題目描述

實現 pow(x, n) ,即計算 x 的 n 次冪函式。

2、示例

示例 1:

輸入: 2.00000, 10
輸出: 1024.00000

示例 2:

輸入: 2.10000, 3
輸出: 9.26100

示例 3:

輸入: 2.00000, -2
輸出: 0.25000
解釋: 2-2 = 1/2^2 = 1/4 = 0.25

3、說明

-100.0 < x < 100.0
n 是 32 位有符號整數,其數值範圍是 [−231, 231 − 1] 。

4、分析

本題重點在於邊界條件的分析和處理

  • 如果 n < 0,是不是 n = -n, x = 1 / x ,再進行遞迴就能解決了呢?
  • 如果 n = Intege.MIN_VALUE,n = -n 會出現溢位,怎麼辦呢?
  • 我們可以先將 n / 2 賦值給 a,再將 a = -n,就不會出現溢位問題。

5、實現

class Solution {
public:
    double myPow(double x, int n) {
        if(n == 0)
            return 1;
        int a = n/2;
        if
(n < 0) { a = -a; x = 1.0/x; } double res = myPow(x, a); if(n % 2 == 0) return res * res; else return res * res * x; } };

二、遞迴

1、定義

  • 若一個物件部分的包含自己或用它自己給自己定義,則這個物件是遞迴的;
  • 若一個過程直接或間接的呼叫自己,則這個過程是遞迴的。

2、思想

把問題分解為規模更小具有與原問題相同解法的子問題
==》優點:思考的方式更加簡單,程式也更加簡練。
==》缺點:增加了壓棧開銷,因此空間複雜度比較高。

3、遞迴條件

  • 減小問題規模,並使子問題與原問題有相同解法。
  • 設定出口,如果沒有出口那麼程式會一直遞迴下去。

三、斐波那契數列

斐波那契數列是一個非常典型的遞迴問題。

1、迴圈方法

long Fibonacci1(size_t N)
{
	long first = 1;
	long second = 1;
	long sum = 0;
	if(N < 3)
		return 1;
	else
	{
		for(size_t i = 3; i <= N; i++)
		{
			sum = first + second;
			first = second;
			second = sum;
		}
		return sum;
	}
}

==》時間複雜度:O(n)
==》空間複雜度:O(1)

2、遞迴方法1

long Fibonacci2(size_t N)
{
	if(N < 3)
		return 1;
	else
		return Fibonacci2(N-1) + Fibonacci2(N-2);
}

==》由後向前計算
==》時間複雜度:O(n2)
==》空間複雜度:O(n)

3、遞迴方法2

long Fibonacci3(long first, long second, size_t N)
{
	if(N < 3)
		return second;
	else
		return Fibonacci3(second, first + second, N - 1);
}

==》前向後計算——尾遞迴方法
==》在進行遞迴時函式只會使用第一次壓棧所開闢的棧空間,在一個棧空間內迴圈,而不會開闢別的棧空間,所以這種方式時間複雜度為O(N),空間複雜度為O(1),是一種非常高效的遞迴方式。

4、遞迴方法3

long *Fibonacci4(size_t N)
{
	long *array = new long[N+1];
	if(N == 0)
		return NULL;
	array[0] = 1;
	array[1] = 1;
	for(size_t i = 2; i <= N; i++)
	{
		array[i] = array[i-1] + array[i-2];
	}
	return array;
}

四、遞迴函式的複雜度

在演算法的分析中,當一個演算法中包含遞迴呼叫時,其時間複雜度的分析==》一個遞迴方程的求解。

而對遞迴方程的求解,目前主流的方法:代入法,迭代法,公式法,母函式法,差分方程法。

1、代入法

  • 首先要對這個問題的時間複雜度做出預測;
  • 然後將預測帶入原來的遞迴方程,如果沒有出現矛盾,則是可能的解;
  • 最後用數學歸納法證明。

示例
遞迴問題:T(n)=4T(n/2)+O(n)
(1)預測時間複雜度:O(n2)
(2)設T(n)=kn2(其中k為常數),將該結果帶入方程
(3)可得:左=kn2,右=4k(n/2)2+O(n)=kn2+O(n)
(4)由於n2 的階高於 n 的階,因而左右兩邊是相等的;
(5)數學歸納法進行驗證即可

2、迭代法

  • 迭代的展開方程的右邊,直到沒有可以迭代的項為止;
  • 這時通過對右邊的和進行估算來估計方程的解。
  • 比較適用於分治問題的求解。

遞迴方程的一般形式
在這裡插入圖片描述

示例
(1)遞迴方程如下: T ( n ) = 2 T ( n / 2 ) + n 2 T(n) = 2T(n / 2) + n^2

(2)迭代過程如下:
T ( n ) = 2 T ( n 2 ) + n 2 T(n) = 2T(\frac{n}{2}) + n^2 = n 2 + 2 ( ( n 2 ) 2 + 2 T ( n 4 ) ) = n^2 +2((\frac{n}{2})^2 + 2T(\frac{n}{4})) = n 2 + 2 ( ( n 2 ) 2 + 2 ( ( n 4 ) 2 + 2 T ( n 8 ) ) ) = n^2 +2((\frac{n}{2})^2 + 2((\frac{n}{4})^2 + 2T(\frac{n}{8}))) = n 2 + 2 ( ( n 2 ) 2 + 2 ( ( n 4 ) 2 + . . . + 2 ( ( n 2 i ) 2 + 2 T ( n 2 i + 1 ) ) ) ) = n^2 +2((\frac{n}{2})^2 + 2((\frac{n}{4})^2 +...+ 2((\frac{n}{2^i})^2 + 2T(\frac{n}{2^{i+1}}))))

容易知道,直到 n 2 i + 1 = 1 \frac{n}{2^{i+1}}=1 時,遞迴過程結束,這時我們計算如下:
  T ( n ) = n 2 + 2 n 2 2 2 + 2 2 n 2 4 2 + 2 2 n 2 8 2 + . . . T(n) = n^2 + 2 \frac{n^2}{2^2}+2^2\frac{n^2}{4^2}+2^2\frac{n^2}{8^2}+... = n 2 ( 1 + 1 2 + 1 4 + 1 8 + . . . ) =n^2(1+\frac{1}{2}+\frac{1}{4}+\frac{1}{8}+...) = 2 n 2 =2n^2
==》該演算法的時間複雜度:O(n2)

3、公式法

針對形如:T(n) = aT(n/b) + f(n)的遞迴方程
==》分治法的時間複雜度,即一個規模為n的問題被分成規模均為n/b的a個子問題,遞迴地求解這a個子問題,然後通過對這a個子問題的解的綜合,得到原問題的解。
==》公式法對於分治問題最好的解法

T ( n ) = { O ( n l o g b a ) , O ( n l o g b a ) &gt; O (