Files
LeetCode-Go/website/content/ChapterFour/0509.Fibonacci-Number.md
2020-08-09 00:39:24 +08:00

171 lines
4.0 KiB
Markdown
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [509. Fibonacci Number](https://leetcode.com/problems/fibonacci-number/)
## 题目
The **Fibonacci numbers**, commonly denoted `F(n)` form a sequence, called the **Fibonacci sequence**, such that each number is the sum of the two preceding ones, starting from `0` and `1`. That is,
F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), for N > 1.
Given `N`, calculate `F(N)`.
**Example 1**:
Input: 2
Output: 1
Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1.
**Example 2**:
Input: 3
Output: 2
Explanation: F(3) = F(2) + F(1) = 1 + 1 = 2.
**Example 3**:
Input: 4
Output: 3
Explanation: F(4) = F(3) + F(2) = 2 + 1 = 3.
**Note**:
0 ≤ `N` ≤ 30.
## 题目大意
斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
```
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
```
给定 N计算 F(N)。
提示0 ≤ N ≤ 30
## 解题思路
- 求斐波那契数列
- 这一题解法很多,大的分类是四种,递归,记忆化搜索(dp),矩阵快速幂,通项公式。其中记忆化搜索可以写 3 种方法,自底向上的,自顶向下的,优化空间复杂度版的。通项公式方法实质是求 a^b 这个还可以用快速幂优化时间复杂度到 O(log n) 。
## 代码
```go
package leetcode
import "math"
// 解法一 递归法 时间复杂度 O(2^n),空间复杂度 O(n)
func fib(N int) int {
if N <= 1 {
return N
}
return fib(N-1) + fib(N-2)
}
// 解法二 自底向上的记忆化搜索 时间复杂度 O(n),空间复杂度 O(n)
func fib1(N int) int {
if N <= 1 {
return N
}
cache := map[int]int{0: 0, 1: 1}
for i := 2; i <= N; i++ {
cache[i] = cache[i-1] + cache[i-2]
}
return cache[N]
}
// 解法三 自顶向下的记忆化搜索 时间复杂度 O(n),空间复杂度 O(n)
func fib2(N int) int {
if N <= 1 {
return N
}
return memoize(N, map[int]int{0: 0, 1: 1})
}
func memoize(N int, cache map[int]int) int {
if _, ok := cache[N]; ok {
return cache[N]
}
cache[N] = memoize(N-1, cache) + memoize(N-2, cache)
return memoize(N, cache)
}
// 解法四 优化版的 dp节约内存空间 时间复杂度 O(n),空间复杂度 O(1)
func fib3(N int) int {
if N <= 1 {
return N
}
if N == 2 {
return 1
}
current, prev1, prev2 := 0, 1, 1
for i := 3; i <= N; i++ {
current = prev1 + prev2
prev2 = prev1
prev1 = current
}
return current
}
// 解法五 矩阵快速幂 时间复杂度 O(log n),空间复杂度 O(log n)
// | 1 1 | ^ n = | F(n+1) F(n) |
// | 1 0 | | F(n) F(n-1) |
func fib4(N int) int {
if N <= 1 {
return N
}
var A = [2][2]int{
{1, 1},
{1, 0},
}
A = matrixPower(A, N-1)
return A[0][0]
}
func matrixPower(A [2][2]int, N int) [2][2]int {
if N <= 1 {
return A
}
A = matrixPower(A, N/2)
A = multiply(A, A)
var B = [2][2]int{
{1, 1},
{1, 0},
}
if N%2 != 0 {
A = multiply(A, B)
}
return A
}
func multiply(A [2][2]int, B [2][2]int) [2][2]int {
x := A[0][0]*B[0][0] + A[0][1]*B[1][0]
y := A[0][0]*B[0][1] + A[0][1]*B[1][1]
z := A[1][0]*B[0][0] + A[1][1]*B[1][0]
w := A[1][0]*B[0][1] + A[1][1]*B[1][1]
A[0][0] = x
A[0][1] = y
A[1][0] = z
A[1][1] = w
return A
}
// 解法六 公式法 f(n)=(1/√5)*{[(1+√5)/2]^n -[(1-√5)/2]^n},用 时间复杂度在 O(log n) 和 O(n) 之间,空间复杂度 O(1)
// 经过实际测试,会发现 pow() 系统函数比快速幂慢,说明 pow() 比 O(log n) 慢
// 斐波那契数列是一个自然数的数列,通项公式却是用无理数来表达的。而且当 n 趋向于无穷大时,前一项与后一项的比值越来越逼近黄金分割 0.618(或者说后一项与前一项的比值小数部分越来越逼近 0.618)。
// 斐波那契数列用计算机计算的时候可以直接用四舍五入函数 Round 来计算。
func fib5(N int) int {
var goldenRatio float64 = float64((1 + math.Sqrt(5)) / 2)
return int(math.Round(math.Pow(goldenRatio, float64(N)) / math.Sqrt(5)))
}
```