递归的基础知识
(1)递归定义
递归定义是数理逻辑和计算机科学用到的一种定义方式,使用被定义对象的自身来为其下定义。
递归定义中使用被定义对象自身来定义,而归纳定义是使用被定义对象的已经定义的部分来定义尚未定义的部分,使用递归定义的函数或集合的性质可以用数学归纳法,通过递归定义的内容来证明。
(2)定义方式
递归定义由三个部分构成:基本情况的定义、递归法则和递归结束的情况。若定义的对象是无限的,那么可以省略第三个部分(递归结束的情况)。
(3)构成
它由初始条件和归纳条件两个部分组成:
-
初始条件:列出哪些个体属于一个给定的集合;
-
归纳条件:当在条件中列出的个体属于给定集合时,则另一些个体也属于该集合。
经典的递归简介
(1)斐波那契数列的概念
斐波那契数列 指的是这样一个数列:1,1,2,3,5,8,13,21,34,55,89… ,这个数列从第 3 项开始,每一项都等于前两项之和。一般地,符合 f(n) = f(n-1) + f(n-2) ,f(n-2) = f(n) - f(n-1) 的整数数列 f(n) ,都是斐波那契—卢卡斯数列。
在数学上,斐波那契数列以如下被以递推的方法定义:F(0) = 0 ,F(1) = 1 ,F(n) = F(n - 1) + F(n - 2)(n ≥ 2,n ∈ N*),显然,斐波那契数列是一个线性递推数列。
(2)斐波那契数列的实现
常用的实现斐波那契数列的方法分为两大类:递归和迭代。
斐波那契数列的实现
迭代方式
(1)在任意目录下创建项目,在该项目下编写一个名为 iteration.go
程序实现获取斐波那契数列的第 N 项元素值,该程序的具体代码如下所示。
package Iteration
import (
"fmt"
)
func Fibonacci(n int) int {
if n == 0 {
return 0
} else if n == 1 || n == 2 {
return 1
}
var (
a = 1
b = 1
c = 0
)
for i := 3; i <= n; i++ {
c = a + b
a = b
b = c
}
return c
}
(2)在该项目下编写一个名为 iteration_test.go
的测试程序来验证以上函数逻辑是否正确,该程序的具体代码如下所示。
package Iteration
import (
"fmt"
"testing"
)
func TestFibonacii(t *testing.T) {
list := make([]int, 10, 20)
n := len(list)
for i := 1; i < n; i++ {
list[i] = Fibonacci(i)
}
fmt.Println("前 10 项斐波那契数为: ", list)
}
(3)在该项目目录下执行 go test
命令,程序运行的结果如下所示。
前 10 项斐波那契数为: [0 1 1 2 3 5 8 13 21 34]
PASS
ok DiGui/test 0.004s
递归方式
(1)在任意目录下创建项目,在该项目下编写一个名为 recursion.go
程序实现获取斐波那契数列的第 N 项元素值,该程序的具体代码如下所示。
package Recursion
func Fibonacci(n int) int {
if n == 0 {
return 0
} else if n == 1 || n == 2 {
return n - 1
} else {
return Fibonacci(n-1) + Fibonacci(n-2)
}
}
(2)在该项目下编写一个名为 recursion_test.go
的测试程序来验证以上函数逻辑是否正确,该程序的具体代码如下所示。
package Recursion
import (
"fmt"
"testing"
)
func TestFibonacii(t *testing.T) {
list := make([]int, 10, 20)
n := len(list)
for i := 1; i < n; i++ {
list[i] = Fibonacci(i)
}
list = append(list[1:], Fibonacci(n))
fmt.Println("前 10 项斐波那契数为: ", list)
}
(3)在该项目目录下执行 go test
命令,程序运行的结果如下所示。
前 10 项斐波那契数为: [0 1 1 2 3 5 8 13 21 34]
PASS
ok DiGui/test 0.004s
非递归方式(栈)
- 顺序栈
(1)在任意目录下创建项目,在该项目下编写一个名为 seqstack.go
程序实现获取斐波那契数列的第 N 项元素值,该程序的具体代码如下所示。
package SeqStack
import (
"errors"
)
const MaxSize = 50
type SeqStack struct {
Data [MaxSize]int
Length int
}
func NewSeqStack() *SeqStack {
return new(SeqStack)
}
// 相应的操作接口
type Operate interface {
InitStack()
Push(e int)
Pop() int
Fibonacci(n int) int
}
// 初始化
func (s *SeqStack) InitStack() {
s.Length = 0
}
// 进栈操作
func (s *SeqStack) Push(e int) {
if s.Length > MaxSize {
panic(errors.New("The s is full!\n"))
}
s.Length++
s.Data[s.Length] = e
}
// 出栈操作
func (s *SeqStack) Pop() int {
if s.Length == 0 {
panic(errors.New("The s is empty!\n"))
}
top := s.Data[s.Length]
s.Length--
return top
}
func (s *SeqStack) Fibonacci(n int) int {
// 递归终止条件
if n == 0 {
return 0
} else if n == 1 || n == 2 {
return n - 1
}
s.Push(0)
s.Push(1)
for i := 2; i < n; i++ {
// 弹出栈顶的 2 个元素,分别赋值给 a 和 b
a := s.Pop()
b := s.Pop()
// 依次压入 a 和 a+b
s.Push(a)
t := a + b
s.Push(t)
}
// 弹出栈顶元素
result := s.Pop()
return result
}
(2)在该项目下编写一个名为 seqstack_test.go
的测试程序来验证以上函数逻辑是否正确,该程序的具体代码如下所示。
package SeqStack
import (
"fmt"
"testing"
)
func TestFibonacii(t *testing.T) {
var s Operate
s = NewSeqStack()
s.InitStack()
list := make([]int, 10, 20)
n := len(list)
for i := 1; i < n; i++ {
list[i] = s.Fibonacci(i)
}
list = append(list[1:], s.Fibonacci(n))
fmt.Println("前 10 项斐波那契数为: ", list)
}
(3)在该项目目录下执行 go test
命令,程序运行的结果如下所示。
前 10 项斐波那契数为: [0 1 1 2 3 5 8 13 21 34]
PASS
ok FeiDiGui/test 0.002s
- Go 内置 List 库方式
(1)在任意目录下创建项目,在该项目下编写一个名为 list.go
程序实现获取斐波那契数列的第 N 项元素值,该程序的具体代码如下所示。
package List
import (
"container/list"
)
var stack *list.List
func init() {
stack = list.New()
}
func Fibonacci(n int) int {
// 递归终止条件
if n == 1 || n == 2 {
return n - 1
}
// 使用 Go 语言中的 List 实现 Stack 的功能
// 进栈
stack.PushBack(0)
stack.PushBack(1)
for i := 2; i < n; i++ {
// 弹出栈顶的 2 个元素,分别赋值给 a 和 b
// stack.Back() 和 stack.Remove() 相当于出栈
a := stack.Back()
stack.Remove(a)
b := stack.Back()
stack.Remove(b)
// 依次压入 a 和 a+b
stack.PushBack(a.Value.(int))
stack.PushBack(a.Value.(int) + b.Value.(int))
}
// 弹出栈顶元素
tmp := stack.Back()
result := stack.Remove(tmp)
return result.(int)
}
(2)在该项目下编写一个名为 list_test.go
的测试程序来验证以上函数逻辑是否正确,该程序的具体代码如下所示。
package List
import (
"fmt"
"testing"
)
func TestFibonacii(t *testing.T) {
list := make([]int, 10, 20)
n := len(list)
for i := 1; i < n; i++ {
list[i] = Fibonacci(i)
}
list = append(list[1:], Fibonacci(n))
fmt.Println("前 10 项斐波那契数为: ", list)
}
(3)在该项目目录下执行 go test
命令,程序运行的结果如下所示。
前 10 项斐波那契数为: [0 1 1 2 3 5 8 13 21 34]
PASS
ok FeiDiGui/test 0.002s
- 链表栈方式
(1)在任意目录下创建项目,在该项目下编写一个名为 linkedstack.go
程序实现获取斐波那契数列的第 N 项元素值,该程序的具体代码如下所示。
package LinkedStack
import (
"errors"
)
type StackNode struct {
Value int
Next *StackNode
}
type LinkedStack struct {
Length int
Top *StackNode
}
func NewStackNode(v int) *StackNode {
return &StackNode{
Value: v,
Next: nil,
}
}
func NewLinkedStack() *LinkedStack {
return &LinkedStack{
0, NewStackNode(0)}
}
// 相关操作接口
type Operate interface {
InitStack()
Push(v int)
Pop() int
Fibonacci(n int) int
}
// 初始化
func (s *LinkedStack) InitStack() {
s.Length = 0
s.Top.Next = nil
}
// 进栈操作
func (s *LinkedStack) Push(v int) {
tmp := &StackNode{
0, s.Top}
tmp.Value = v
s.Top = tmp
s.Length++
}
// 出栈操作
func (s *LinkedStack) Pop() int {
if s.Length == 0 {
panic(errors.New("The LinkedStack is empty!\n"))
} else {
v := s.Top.Value
s.Top = s.Top.Next
s.Length--
return v
}
}
func (s *LinkedStack) Fibonacci(n int) int {
if n == 1 || n == 2 {
return n - 1
}
s.Push(0)
s.Push(1)
for i := 2; i < n; i++ {
// 弹出栈顶的 2 个元素,分别赋值给 a 和 b
a := s.Pop()
b := s.Pop()
// 依次压入 a 和 a+b
s.Push(a)
t := a + b
s.Push(t)
}
// 弹出栈顶元素
result := s.Pop()
return result
}
(2)在该项目下编写一个名为 linkedsatck_test.go
的测试程序来验证以上函数逻辑是否正确,该程序的具体代码如下所示。
package LinkedStack
import (
"fmt"
"testing"
)
func TestFibonacci(t *testing.T) {
var s Operate
s = NewLinkedStack()
s.InitStack()
list := make([]int, 10, 20)
n := len(list)
for i := 1; i < n; i++ {
list[i] = s.Fibonacci(i)
}
list = append(list[1:], s.Fibonacci(n))
fmt.Println("前 10 项斐波那契数为: ", list)
}
(3)在该项目目录下执行 go test
命令,程序运行的结果如下所示。
前 10 项斐波那契数为: [0 1 1 2 3 5 8 13 21 34]
PASS
ok FeiDiGui/test 0.003s
-
参考书籍:《数据结构教程 第6版》(李春葆 主编)
-
参考书籍:《数据结构 C语言版》(严蔚敏、李冬梅、吴伟民著)