学习日志 ━━ 理解递归(使用go语法举例)

理解循环和递归的区别

循环

循环比较好理解,就是一条通道不停开门,不停前行,直到开不了门,就跳出这个通道。
如果是死循环,则相当于此通道永无止尽,

  1. 可能是一个环形通道,
  2. 可能是一条串行无限的通道。

递归

递归则相当于一条通道不停开门,不停前行,直到开不了门,则从 原路返回
所以“递归”从字面上看就非常准确地表达了其含义。

  1. :依次、一层层
  2. :回来,回头执行

实例分析递归的实现过程

一、实例一

1、 递归函数及输出结果

现在我们写一个递归

// 主程序
func main() {
	digui(3)
}

//递归函数
func digui(n int) {
	if n < 1 {
		fmt.Println("结束", 99)
		return
	}
	n--
	fmt.Println("进", n)
	// 递归调用
	digui(n)
	n += 1000
	fmt.Println("出", n)
}
// 输出
/*
进 2
进 1
进 0
结束 99
出 1000
出 1001
出 1002
*/

2、 解析执行过程

那么他内部是怎么执行的呢?现在我很傻瓜的把函数一层层嵌入进去解析一下~~+

// 主程序
func main() {
	digui(3)
}

//传一个参数n=3的拷贝进入digui()函数
func digui(n int) {
	//此时n=3,不符合本条件
	if n < 1 {
		fmt.Println("结束", 99)
		return
	}
	
	// 执行n--,此时n=2
	n--
	// 打印第一行:进 2
	fmt.Println("进", n)

	//digui(n)	
	// 这里的递归相当于执行了一个闭包函数
	// 传一个参数n=2的拷贝进入函数
	func(n int) {
		//此时n=2,不符合本条件
		if n < 1 {
			fmt.Println("结束", 99)
			return
		}
		
		// 执行n--,此时n=1
		n--
		// 打印第二行:进 1
		fmt.Println("进", n)
	
		//digui(n)	
		// 这里的递归相当于执行了一个闭包函数
		// 传一个参数n=1的拷贝进入函数
		func(n int) {
			//此时n=1,不符合本条件
			if n < 1 {
				fmt.Println("结束", 99)
				return
			}
		
			// 执行n--,此时n=0
			n--
			// 打印第三行:进 0
			fmt.Println("进", n)
		
			//digui(n)	
			// 这里的递归相当于执行了一个闭包函数
			// 传一个参数n=0的拷贝进入函数
			func(n int) {
				// 此时n<1,符合条件
				if n < 1 {
					// 打印第四行:结束 99
					fmt.Println("结束", 99)
					//结束本函数,后面的都不执行了
					return
				}
				//因为不执行了,所以后面都注释了
				/*
				n--
			
				fmt.Println("进", n)
			
				digui(n)
			
				n += 1000
			
				fmt.Println("出", n)
				*/
			}(n)
		
			// 这一层里的n=0,n+=1000的值为10000
			n += 1000
			// 第五行打印:出 1000
			fmt.Println("出", n)
		}(n)
		
		// 这一层里的n=1,n+=1000的值为10001
		n += 1000
		// 第六行打印:出 1001
		fmt.Println("出", n)
	}(n)

	// 这一层里的n=2,n+=1000的值为10002
	n += 1000
	// 第七行打印:出 1002
	fmt.Println("出", n)
}

注意:

  1. 本例中函数传参都是值传递,即拷贝,这样外层函数的n=2传入内层函数后执行n--,得到n=1,但该内层的n与外层的n并无关系,因此,在内层函数结束完成后再去执行外层的n+=1000,这里n的值为1002
  2. 解析的例子中可以看出,每层函数结束后都还需要执行一次n+=1000fmt.Println("出",n),这也就是所谓的“回头路”了,其本质仍旧是串联执行,一层函数结束后继续执行下去直到本层结束再继续执行~~并没有真的“走回头路”;
  3. 如果把递归的调用放在函数末尾则叫“尾递归”,实行尾递归时,一层结束后没有任务需要执行就跳到下一层,于是函数就“快速”地一层层结束了,让人感觉没有走回头路,所以尾递归用for循环也能实现。

二、实例二

假设有一题:6层台阶,可以一次走两层或者一次走一层,那么有几种走法?
这里就可以用到递归,本题我们只研究递归的实现过程,不研究算法。

1、 递归函数及输出结果

单看这个递归不好理解,递归是一路走下去,直到没路再原路返回,那这个例子中返回值是两个返回值相加,怎么就能一头越变越小,另一头又越来越大呢?而且返回值是两个函数返回值的和,那这两个函数又会分别分出两个函数去生成返回值,那这样分叉越来越多,岂不是无限循环了?

func main() {
	fmt.Println(f(6))
}

func f(n int) int {
	if n == 1 {
		return 1
	}
	if n == 2 {
		return 2
	}
	return f(n-2) + f(n-1)
}
// 输出:13

2、 解析执行过程

带着一脸的疑惑,我们来看一看具体的执行流程。
假定每一层都有ab两个变量分别接收两个函数的返回值,然后再将a+b之和返回给当前函数。
下面的解析过程已经去掉了不符合条件的语句,否则太长了。

func main() {
	fmt.Println(f(6))
}

// n = 6
func f(n int) int {
	// 输入6-2=4
	a := func(n int) int {
		// 输入4-2=2
		a := func(n int) int {
			return 2
		}(n - 2) // 返回2
		// 输入4-1=3
		b := func(n int) int {
			// 输入3-2=1
			a := func(n int) int {
				return 1
			}(n - 2) // 返回1
			// 输入3-1=2
			b := func(n int) int {
				return 2
			}(n - 1) // 返回2
			// 返回1+2=3
			return a + b
		}(n - 1) // 返回3
		// 返回2+3=5
		return a + b
	}(n - 2) // 返回5

	// 输入6-1=5
	b := func(n int) int {
		// 输入5-2=3
		a := func(n int) int {
			// 输入3-2=1
			a := func(n int) int {
				return 1
			}(n - 2) //返回1
			// 输入3-1=2
			b := func(n int) int {
				return 2
			}(n - 1) //返回2
			//返回1+2=3
			return a + b
		}(n - 2) // 返回3
		// 输入5-1=4
		b := func(n int) int {
			// 输入4-2=2
			a := func(n int) int {
				return 2
			}(n - 2) // 返回2
			// 输入4-1=3
			b := func(n int) int {
				// 输入3-2=1
				a := func(n int) int {
					return 1
				}(n - 2) // 返回1
				// 输入3-1=2
				b := func(n int) int {
					return 2
				}(n - 1) // 返回2
				// 返回1+2=3
				return a + b
			}(n - 1) // 返回3
			// 返回2+3=5
			return a + b
		}(n - 1) // 返回5
		// 返回3+5=8
		return a + b
	}(n - 1) // 返回8
	// 返回5+8=13
	return a + b
} // 返回13

看代码的执行过程,我们能看出:

  1. n-2n-1n不断变小,n传递至下一层后,在符合n==1n==2的条件之前,n一直在不断变小;
  2. n遇到条件n==1n==2时,它(n)终于撞南墙returnreturn的意思就是结束本层,进入下一层。
  3. 到达上一层后先执行a+b,两者之和return回上一层,并执行上一层的a+b
  4. 因此n在不断减小,a+b不断变大返回。
  5. 与上个例子一样,将递归函数作为标准,该函数之前的行为依次执行,直至遇到返回条件后,再依次执行递归函数之后的程序。
  6. 从过程中可以总结本题:进入下一层的目的是不断创建分支直至条件满足返回,返回上一层的目的是将分支中的值进行加法计算再供上一层使用。

按照上面的总结,我们怎么让自己一眼看出递归的执行流程呢?

以本例中return f(n-2)+f(n-1)作为标准;

  1. if:两句条件语句n==1n==2在调用递归函数之前,因此先执行;
  2. n-1 n-2n-1n-2作为参数传入f(),因此这两参数是第二位执行,此后函数在不断执行 n-1n-2
  3. if return:直到n满足了条件n==1n==2后返回1或者2
  4. + return:此后不断返回外层,再将f(n-2)+f(n-1)(即a+b)之和返回,直至程序结束。
发布了44 篇原创文章 · 获赞 1 · 访问量 3589

猜你喜欢

转载自blog.csdn.net/snans/article/details/105442949