编译原理——递归程序基本思想和编程方法

一、实验目的:

(1) 掌握递归程序基本思想和编程方法。

二、实验内容:

  1. 查阅资料了解递归思想和递归程序编写;
    递归(recursion):程序调用自身的编程技巧。
    递归满足2个条件:
    1)有反复执行的过程(调用自身)
    2)有跳出反复执行过程的条件(递归出口)

  2. 调试、编译、自定义主程序,运行程序。

三、实验原理:

  1. 递归(recursion)就是子程序(或函数)直接调用自己或通过一系列调用语句间接调用自己,是一种描述问题和解决问题的基本方法。
  2. 递归通常用来解决结构自相似的问题。所谓结构自相似,是指构成原问题的子问题与原问题在结构上相似,可以用类似的方法解决。具体地,整个问题的解决,可以分为两部分:第一部分是一些特殊情况,有直接的解法;第二部分与原问题相似,但比原问题的规模小。实际上,递归是把一个不能或不好解决的大问题转化为一个或几个小问题,再把这些小问题进一步分解成更小的问题,直至每个小问题都可以直接解决。因此,递归有两个基本要素:
    (1)边界条件:确定递归到何时终止,也称为递归出口。
    (2)递归模式:大问题是如何分解为小问题的,也称为递归体。递归函数只有具备了这两个要素,才能在有限次计算后得出结果
  3. 在递归函数中,调用函数和被调用函数是同一个函数,需要注意的是递归函数的调用层次,如果把调用递归函数的主函数称为第0层,进入函数后,首次递归调用自身称为第1层调用;从第i层递归调用自身称为第i+1层。反之,退出第i+1层调用应该返回第i层。
  4. 递归函数的内部执行过程:
    一个递归函数的调用过程类似于多个函数的嵌套的调用,只不过调用函数和被调用函数是同一个函数。为了保证递归函数的正确执行,系统需设立一个工作栈。具体地说,递归调用的内部执行过程如下:
    (1)运动开始时,首先为递归调用建立一个工作栈,其结构包括值参、局部变量和返回地址;
    (2)每次执行递归调用之前,把递归函数的值参和局部变量的当前值以及调用后的返回地址压栈;
    (3)每次递归调用结束后,将栈顶元素出栈,使相应的值参和局部变量恢复为调用前的值,然后转向返回地址指定的位置继续执行。

四、实验代码:

递归例子:
1.

//阶乘
int recursive(int i)
{
	int sum = 0;
	if (0 == i)
		return (1);
	else
		sum = i * recursive(i-1);
	return sum;
}

//汉诺塔
void hanoi(int n,int p1,int p2,int p3)
{
	if(1==n)
		cout<<"盘子从"<<p1<<"移到"<<p3<<endl;
	else
	{
		hanoi(n-1,p1,p3,p2);
		cout<<"盘子从"<<p1<<"移到"<<p3<<endl;
		hanoi(n-1,p2,p1,p3);
	}
}

//全排列
inline void Swap(int &a,int &b)
{
	int temp=a;
	a=b;
	b=temp;
}
void Perm(int list[],int k,int m)
{
	if (k == m-1) 
	{
		for(int i=0;i<m;i++)
		{
			printf("%d",list[i]);
		}
		printf("n");
	}
	else
	{
		for(int i=k;i<m;i++)
		{
			Swap(list[k],list[i]); 
			Perm(list,k+1,m);
			Swap(list[k],list[i]); 
		}
	}
}

//斐波那契
long Fib(int n)
{
 if (n == 0) 
  return 0;
 if (n == 1) 
  return 1;
 if (n > 1) 
  return Fib(n-1) + Fib(n-2);
}

五、实验结果:

  1. 递归函数——阶乘
    代码:
#include <iostream>
using namespace std;

int recursive(int i)
{
	int sum = 0;   
	//定义结果返回变量,初值为0
	if (0 == i)    
		return (1);   
    //当参数i等于0的时候,返回1,递归函数退出
	else
		sum = i * recursive(i-1);   
    //i不等于0的时候,用i乘上以i-1为参数的递归函数
	return sum;   
	//当i减为1的时候,递归结束,退出返回i的阶乘
}

int main()
{
    int a;   
    //定义一个数,表示要计算a的阶乘
    cout<<"请输入要计算的阶乘数:";
    cin>>a;   
    //输入的值赋给a
    cout<<a<<"的阶乘是"<<recursive(a);  
    //调用递归函数并输出
    
    return 0;
}

测试:
在这里插入图片描述

  1. 递归函数——汉诺塔
    代码:
#include <iostream>
using namespace std;

void hanoi(int n,int p1,int p2,int p3)
//p1,p2,p3表示利用2号将1号的n个珠子移动到3号
{
	if(1==n)
		cout<<"盘子从"<<p1<<"移到"<<p3<<endl;
    //递归函数退出点,当珠子数量为1时,直接移动,结束
	else
	{
		hanoi(n-1,p1,p3,p2);
		//第一步p1,p3,p2表示利用3号将1号的n-1个珠子移动到2号
		cout<<"盘子从"<<p1<<"移到"<<p3<<endl;
		//第二步将剩余的一个珠子从1移到3
		hanoi(n-1,p2,p1,p3);
		//第三步p2,p1,p3表示利用1号将2号的n-1个珠子移动到3号
	}
}

int main()
{
    int a,p1=1,p2=2,p3=3;
    //定义移动的珠子数a,以及三个杆子
    cout<<"请输入在一号杆上的珠子数:";
    cin>>a;
    //用a接受输入的珠子数
    hanoi(a,p1,p2,p3);
    //调用递归函数,实现a个珠子通过p2从p1移动到p3

    return 0;
}

测试:
在这里插入图片描述

  1. 递归函数——全排列
#include <iostream>
#include <stdio.h>
using namespace std;
//交换
inline void Swap(int &a,int &b)
{
	int temp=a;
	a=b;
	b=temp;
	//实现参数a和b的数值交换
}
 //全排列递归算法
void Perm(int list[],int k,int m)
{
	//list 数组存放排列的数,K表示层,代表第几个数,m表示数组的长度
	if (k == m)
	{
	    //K==m 表示到达最后一个数,不能再交换,最终的排列的数需要输出;
		for(int i=0;i<m;i++)
		{
			printf("%d",list[i]);
		}
		printf("\n");
		//递归调用出口,顺序输出一排列完毕的数组
	}
	else
	{
		for(int i=k;i<m;i++)
		{
			Swap(list[k],list[i]);
			//交换数组中k位置和i位置的数
			Perm(list,k+1,m);
			//递归调用
			Swap(list[k],list[i]);
			//再次交换数组中k位置和i位置的数
		}
	}
}

int main()
{
    int a[]={1,2,3,4};
    //定义待排列数组a,默认使用1,2,3,4
    Perm(a,0,4);
    //对数组a从第一个数字开始排列,共排列4个数字
    //即对数组a的所有数字全排列

    return 0;
}

测试:以1,2,3,4为例
在这里插入图片描述

  1. 递归函数——斐波拉契数列
    代码:
#include <iostream>
using namespace std;

long Fib(int n)
{
    if (n == 0)
        return 0;
    //递归调用出口,0月繁殖量为0
    if (n == 1)
        return 1;
    //递归调用出口,一个月繁殖出1对
    if (n > 1)
        return Fib(n-1) + Fib(n-2);
    //当月的对数为前两个月的对数总和
    return 0;
}

int main()
{
    int month;
    //定义兔子繁殖的月数month
    cout<<"请输入小兔子的繁殖月数:";
    cin>>month;
    //输入值赋给参数month
    cout<<"经过"<<month<<"个月后,小兔子总共有:"<<Fib(month)+Fib(month-1)<<"对";
    //调用两次递归函数,参数分别是month和month-1
    //计算幼仔对数和成兔对数总和,总体对数

    return 0;
}

以12个月为测试:
在这里插入图片描述

六、实验体会:

  1. 把一个不能或不好解决的大问题转化为一个或几个小问题,再把这些小问题进一步分解成更小的小问题
  2. 递归的关键在于找出递归定义和递归终止条件。
  3. 递归定义:使问题向边界条件转化的规则。递归定义必须能使问题越来越简单。
  4. 递归终止条件:也就是所描述问题的最简单情况,它本身不再使用递归的定义。
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值