算法设计与分析——递归循环

递归

递归的定义

若一个对象部分地包含它自己, 或用它自己给自己定义, 则称这个对象是递归的;若一个过程直接地或间接地调用自己, 则称这个过程是递归的过程。

递归函数:

一个直接调用自己或通过一系列调用语句间接调用自己的函数。

递归的应用:

数学函数:阶乘函数、2阶Fibonacci数列
数据结构:二叉树、广义表
八皇后、hanoi塔问题

递归算法设计:

将规模较大的原问题分解为一个或多个规模更小的与原问题类似的子问题-递归步骤(递归表达式)
确定一个或多个无须分解可直接求解的子问题-终止条件

递归的实现:

建立递归工作栈
调用时
为调用者生成一个活动记录
压入运行时刻栈
程序控制权转交给被调函数
返回时
被调函数执行完毕,运行时刻栈顶的活动结构退栈,
根据退栈的活动结构中保存的返回地址将程序控制权交给调用者继续执行

在这里插入图片描述

递归的实现-阶乘函数

在这里插入图片描述
代码

int fact(int n)
{   if (n= =0) 
    {  s=1; return 1;}
    else  
    {   s=n*fact(n-1); //<——ReLoc2
        return s;
    }
}
void main()
{ int N= fact(5);}//<——ReLoc1

分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从图中可看到主函数调用fact(5)的过程,在函数过程体中,else语句以参数4,3,2,1,0,即、fact(4)、fact(3)、fact(2)、fact(1、fact(0)。即,fact(5)为主函数调用,其它则为在fact函数内调用。每一次递归调用并未立即得到结果,而是进一步向深度递归调用,直到n=1或n=0时,函数fact结果为1,然后再一一返回计算,最终得到结果。

效率分析

空间效率 : O(n) 与递归树的深度成正比

空间效率 : O(2n) 与递归树的结点数成正比

优缺点

优点:结构清晰,程序易读

缺点:每次调用要生成工作记录,保存状
态信息,入栈;返回时要出栈,恢复状态
信息。时间开销大。

例题—汉诺塔

传说在创世纪时,在一个叫Brahma的寺庙里,有三个柱子,其中一柱上有64个盘子从小到大依次叠放,僧侣的工作是将这64个盘子从一根柱子移到另一个柱子上。
移动时的规则:

  • 每次只能移动一个盘子;
  • 只能小盘子在大盘子上面;
  • 可以使用任一柱子。

当工作做完之后,就标志着世界永远和平。
在这里插入图片描述

分析:

设三根柱子分别为 x,y, z , 盘子在x柱上,要移到z柱上。
1、当n=1时,盘子直接从 x 柱移到 z 柱上;
2、当n>1时, 则

  • ①设法将前n–1个盘子借助y,从x移到y柱上,把 盘子n从x移到z柱上;
  • ② 把n–1个盘子从y移到z柱上。
void  Hanoi(int n,char x,char y,char z)
{  //将n个编号从上到下为1…n 的盘子从 x 柱,借助y柱移到z柱 
	if(n ==1)  
         move(x,1,z); //将编号为1的盘子从x柱移到z柱 
  	else 
	{ //将n-1个编号从上到下为1…n-1的盘子从x柱,借助y柱移到z柱  
        Hanoi(n-1,x,z,y); 
         move(x,n,z);//将编号为  n 的盘子从 x 柱移到 z 柱 
  	//将n-1个编号从上到下为1…n-1的盘子从y柱,借助x柱移到z柱 
  	 Hanoi(n-1,y,x,z);
 	}
} //Hanoi

代码

#include <stdio.h>
int sum=0;//全局变量,记录第几次搬
void hanoi(int n,char x,char y,char z)//递归函数,将编号为n的的盘子借助y柱子,从x柱子到z柱子
{
    if(n==1)//如果是1,就直接搬
        printf("第%d轮,将%d号盘子从%c号柱子到%c号柱子\n",++sum,n,x,z);
    else//不然的话,就要先把上面n-1个盘子从x搬到y,再将n从x搬到z
    {
        hanoi(n-1,x,z,y);//把上面n-1个盘子从x搬到y
        printf("第%d轮,将%d号盘子从%c号柱子到%c号柱子\n",++sum,n,x,z);//再将n号盘子从x搬到z
        hanoi(n-1,y,x,z);//剩下的盘子在y柱子上,就要借助x柱子,从y到z柱子,继续递归
    }
}
int main()
{
    char x='X',y='Y',z='Z';
    int n=5;//定义有5个盘子
    hanoi(n,x,y,z);
}

练习题

用循环实现:

1、 求级数

求:1/1!-1/3!+1/5!-1/7!+…+(-1)n+1/(2n-1)!

#include <bits/stdc++.h> 
using namespace std;
int main( )
{int i,n,j,sign=1;
float s,t=1;
cin>>n;
s=1;
for(i=2;i<=n;i=i+1)
  {t=1;          /*求阶乘*/
   for(j=1;j<=2*i-1;j=j+1)
         t=t*j;
   sign =1; /*求(-1)n+1*/
   for(j=1;j<=i+1;j=j+1)
       sign = sign *(-1);
   s=s+ sign/t;
   }
 cout<<"Sum="<<s;
}

2、 编算法找出1000以内所有完数。

如:28的因子为1、2、4、7,14,而28=1+2+4+7+14。因此28是“完数”。
编算法找出1000之内的所有完数,并按下面格式输出其因子:28 it’s factors are 1,2,4,7,14。

#include <bits/stdc++.h> 
using namespace std;
int main()
{
 int a[20];
 int s,k,n=1000;  //k为数组下标,s为累加和。 
 int i,j;   //循环变量 
 for(i=2;i<n;i++)
 { 
 //判断 i 是否为完数。  
  a[0]=1;k=1;s=1;
  for(j=2;j<i;j++)
  {
   if(i%j==0)
   {s=s+j;a[k]=j;k=k+1;}
  }
   if(s==i)
   {cout<<s<<"  it is a factors are ";  //输出完数 
   for(j=0;j<k;j++)
   {cout<<" "<<a[j];        //输出完数因子 
	} 
	cout<<endl;}
  }
return 0;
}

3、 编写算法:根据参数n打印具有下面规律的图形,如,当n=4时,图形如下

在这里插入图片描述

#include <bits/stdc++.h> 
using namespace std;
int main()
{
	int a[100][100];//存储打印的规则图形的矩阵,下三角矩阵
	int k = 1,n;//打印的数据,1-n
	cin>>n;
	//赋值
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 1; j <= n+1-i; ++j)
		{
			a[i-1+j][j] = k;
			k++;
		}
	}
	//打印
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 1; j <= i; ++j)
		{
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

使用循环和递归两种方法来实现:

4、计算1+2+……+n。

#include <bits/stdc++.h> 
using namespace std;
//循环实现 
int main()
{
	int n,sum=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		sum+=i;
	}
	cout<<sum;
	return 0;
}
//递归实现  
int sum(int n)  //定义sum()函数;
{
 if(1 == n)
  return 1;
 else
  return n+sum(n-1);
}
int main(void)
{
 int n;
 cin>>n; 
 cout<<sum(n); //sum(x)是调用sum()函数; 
 return 0;
}

使用递归的方法来实现:

5、整数的分划问题

对于一个正整数n的分划就是把n写成一系列正整数之和的表达式。例如,对于正整数n=6,它可以分划为:
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

分析:

根据例子发现“包括第一行以后的数据不超过6,包括第二行的数据不超过5,……,第六行的数据不超过1”。

因此,定义一个函数f(n,m),表示整数n的“任何被加数都不超过m”的分划的数目,n的所有分划的数目P(n) =f(n,n)。

模型建立:

一般地f(n.m)有以下递归关系:

1)f(n,n)=1+f(n,n-1) (m=n)

f(n,n-1)表示n的所有其他分划,即最大被加数m<=n-1的划分。

2)f(n,m)=f(n,m-1)+f(n-m,m) (m<n)

f(n,m-1)表示被加数中不包含m的分划的数目;

f(n-m,m)表示被加数中包含(注意不是小于)m的分划的数目。

递归的停止条件:

1)f(n,1)=1,表示当最大的被加数是1时,该整数n只有一种分划,即n个1相加;
2)f(1,m)=1,表示整数n=1只有一个分划,不管最大被加数的上限m是多大。

#include<bits/stdc++.h>
using namespace std;
int f(int n,int m)
{
   if(n==1||m==1) return 1;
   else if(n<m) return f(n,n);
   else if(n==m) return 1+f(n,n-1);
   else return f(n,m-1)+f(n-m,m);
}
int main()
{
    int n,m;
   
    cin>>n>>m;
    cout<<f(n,m);
}

打印图像代码

#include<bits/stdc++.h>
using namespace std;
int arr[11];
long long sum_num=0;
void f(int n, int fn, int step)
{
	if(n==0) 
	{
		cout << arr[0];
		for(int i=1; i<step; i++)
		{
			cout <<"+"<<arr[i];
		
		} 
		cout << endl;
		sum_num++;
		return ;
	}
	for(int i=min(n,fn); i>0; i--)
	{
		arr[step] = i;
		f(n-i, i, step+1);
	}
}

int main()
{
	int n;
	cout << "请输入数字:"; 
	cin >> n;
	//1. 寻找所有情况,并打印 
	cout << endl << "所有情况如下:" << endl;
	f(n, n, 0);
	cout << endl << "共有" << sum_num << "种" << endl; 	 
	return 0;
}

6、任给十进制的正整数,请从低位到高位逐位输出各位数字。

#include<bits/stdc++.h>
using namespace std;
int fun(int num)
{
    if (num>=10)
    {
        cout<<num % 10;
        fun(num / 10);
    }else
	cout<<num<<endl;
    return 0;
}    
int main()
{
    int n;
    cin>>n;
    fun(n);
    return 0;
}

7、任给十进制的正整数,请从高位到低位逐位输出各位数字。

#include<bits/stdc++.h>
using namespace std;
int fun(int num)
{
    if (num>=10)
    {
		fun(num / 10);
        cout<<num % 10<<"  ";
        
    }else
	cout<<num<<" ";
    return 0;
}    
int main()
{
    int n;
    cin>>n;
    fun(n);
    return 0;
}

8、找出n个自然数(1,2,3,…,n)中r个数的组合。

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int a[100];
comb(int m,int k)
{
    int i,j;
    for(i =m;i>=k;i--) {
        a[k]=i;
        if(k>1) {
            comb(i-1,k-1);
        } else {
            for(j=a[0];j>0;j--) {
                cout<<a[j]<<"   ";
            }
            cout<<endl;
        }
    }
}
int main()
{
    int n,r;
   
    cin>>n>>r;
    if(r>n) {
       cout<<"输入的n,r是错误的!";
    } else {
        a[0]=r;
        comb(n,r);
    }

}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐雨风栉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值