目录
一、递归
递归的概念
直接或间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数。在计算机算法设计与分析中,递归技术是十分有用的。使用递归技术往往使函数的定义和算法的描述简洁且易于理解。有些数据结构如二又树等,由于其本身固有的递归特性,特别适合用递归的形式来描述。另外,还有一些问题,虽然其本身并没有明显的递归结构,但用递归技术来求解使设计出的算法简洁易懂且易于分析。
示例1:阶乘函数
我们都知道n!=n*(n-1)*(n-2)*(n-3)*......*1
推导出公式
所以可以从n开始,递推出下一个数,这里利用递推式即可实现递归函数的设计
JAVA代码
public static int factorial(int n){
if(n==0)return 0 ;
return n*factorial(n-1);
}
Python3代码
def factorial(n):
if n == 0:
return 1
return n*factorial(n-1)
print(factorial(3))
示例2:斐波那契数列
无穷数列1,1,2,3,5,8,13,21,34,55,......,称为斐波那契数列。
原理:从第三个数开始,数的值为前两个数的和。
所以第n(n>2)个数可以由第(n-1),(n-2)个数推出。
总结出推出递归式子
所以可写出代码求第n个斐波那契数
JAVA代码
public static int fibonacci(int n){
if(n<=1)return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
python3代码
def fib(n)
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
print(fib(2))
示例3 全排列问题
从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
现有数组
int xlist = [1, 2, 3, 4]
全排列即为
1 2 3 4
1 3 2 4
2 1 3 4
2 3 1 4
3 2 1 4
3 1 2 4
4 2 3 1
4 3 2 1
2 4 3 1
2 3 4 1
3 2 4 1
3 4 2 1
1 4 3 2
1 3 4 2
简单点取数组=【1,2, 3 】
第一行表示待进行排列的第一个数的待选数。
红色表示选择
第二行表示排列第一个数被选出后第二个数的待选数。
红色表示选择
第三行由于只剩一个待选数,直接构成排列。
可以看出共计6种排列情况,123,132,213,231,312,321
代码实现输出一组数的全排列
python
#定义函数
def perm(xlist, k, m):
if k == m:
for x in xlist:
print(x, end=' ')#这样将末尾的换行符换成空格好看点
print()#这个是为了换行
else:
for i in range(k-1, m-1):
xlist[k-1], xlist[i] = xlist[i], xlist[k-1]
perm(xlist, k+1, m)
xlist[k-1], xlist[i] = xlist[i], xlist[k-1]
#执行函数
xlist = [1, 2, 3, 4]
k = 0
m = len(xlist)
print(perm(xlist, k, m))
结果如下
JAVA(swap()报错的话大家自己解决一下)
public static void perm(int [] list, int k, int m) {
if (k == m) {
for (int i = 0; i <= m; i++) {
System.out.println(list[i]);
}
System.out.println();
} else {
for (int i = k; i <= m; i++) {
swap(list, k, i);//算法没问题,我不会java,不知道咋解决这个Mymath库的问题
perm(list, k + 1, m);
swap(list, k, i);
}
}
}
public static void main(String[] args) {
int []list={1,2,3};
int k=0;
int m=list.length;
perm(list, k, m);
}
示例4整数划分问题
将正整数n表示为一系列正整数之和
n=n1+n2+n3+n4+......+nk ( )
正整数n的这种表示成为正整数n的划分。正整数n的不同划分个数成为正整数n的划分数,记作p(n)。
例如,正整数6有如下11种划分,所以p(6)=11:
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1;
我们就以这个为实例,推导递推式
在正整数n的所有不同规划中,将最大加数n1不大于m的划分方法个数记作q(n,m)。
我们考虑4种情况
(1)最大加数n1不大于1,此时任何整数n只有一种划分方式
即q(n,1)=1
q(6,1)=1 即1+1+1+1+1+1。 就是n个1相加。
(2)最大加数n1大于等于n,q(n,m)=q(n,n),m>=n
但实际上不可能大于n,所以也只有一种可能
q(6,6)=1 即6 这一项。
(3)正整数n的划分由n1=n的划分和n1<=n-1的划分组成。
q(n,n)=1+q(n,n-1)。
(4)正整数n的最大加数n1不大于m的划分由n1=m的划分和n1<=m-1的划分组成。
q(n,m)=q(n,m-1)+q(n-m,m) n>m>1
综合4条关系,可得出递归式
JAVA代码
public static int q(int n,int m){
if((n<1)||(m<1))
return 0;
if((n==1)||(m==1))
return 1;
if(n<m)
return q(n,n);
if(n==m)
return q(n,m-1)+1;
return q(n,m-1)+q(n-m,m);
}
python代码
def q(n, m):
if n < 1 or m < 1:
return 0
if n == 1 or m == 1:
return 1
if n < m:
return q(n, n)
if n == m:
return q(n, m-1)+1
return q(n, m-1)+q(n-m, m)
print(q(4, 4))
示例5汉诺塔
设x,y,z为3个塔座,开始时,x上有n个圆盘,这些圆盘自上而下,由大到小叠在一起.先要求将x上的一摞圆盘移动到z上。
且要满足规则
规则(1):每次只能移动1个圆盘。
规则(2):任何时刻都不允许将较大的圆盘压在较小的圆盘之上。
规则(3):在满足移动规则(1)和规则(2)的前提下,可将圆盘移至x,y,z中任一塔座上。
下面是动画演示
上述算法简洁明确,可以证明它是正确的。但只看算法的计算步骤,很难理解它的道理,也很难理解它的设计思想。下面用递归技术来解决同一问题。
当n=1时,问题比较简单,只要将编号为1的圆盘从塔座x直接移至塔座z上即可。
当n>1时,需要利用塔座y作为辅助塔座。此时若能设法将n-1个较小的圆盘依照移动规则从塔座x移至塔座y,然后,将剩下的最大圆盘从塔座x移至塔座y,最后,再设法将n-1个较小的圆盘依照移动规则从塔座y移至塔座z。
由此可见,n个圆盘的移动问题可分为两次n-1个圆盘的移动问题,这又可以递归地用上述方法来做。由此可以设计出解Hanoi塔问题的递归算法如下:
java代码
public static void hanoi(int n,int x,int y,int z)
{
if(n>0){
hanoi(n-1,x,y,z);
move(x,z);
hanoi(n-1,y,z,x);
}
}
//move函数此处不给出,别问,问就是我写不出
python代码(以列表x,y,z做塔座)
def hanoi(n, x, y, z):
if n > 0:
hanoi(n-1, x, y, z)
move(x, z)
hanoi(n-1, y, z, x)
#move函数此处不给出,别问,问就是我写不出