• 栈与递归的实现(Hanoi塔问题等等)



    函数中有直接或间接地调用自身函数的语句,这样的函数称为递归函数。递归函数用
    得好,可简化编程工作。但函数自己调用自己,有可能造成死循环。为了避免死循环,要
    做到两点:
    (1) 降阶。递归函数虽然调用自身,但并不是简单地重复。它的实参值每次是不一样
    的。一般逐渐减小,称为降阶。如教科书式(33)的Ackerman 函数,当m≠0 时,求
    Ack(m,n)可由Ack(m-1,⋯)得到,Ack()函数的第1 个参数减小了。
    (2) 有出口。即在某种条件下,不再进行递归调用。仍以教科书式(33)的Ackerman

    函数为例,当m=0 时,Ack(0,n)=n+1,终止了递归调用。所以,递归函数总有条件语
    句。m=0 的条件是由逐渐降阶形成的。如取Ack(m,n)函数的实参m=-1,即使通过降阶也
    不会出现m=0 的情况,这就造成了死循环。
    编译软件在遇到函数调用时,会将调用函数的实参、返回地址等信息存入软件自身所
    带的栈中,再去运行被调函数。从被调函数返回时,再将调用函数的信息出栈,接着运行
    调用函数。编译软件开辟的栈空间是有限的,当递归调用时,嵌套的层次往往很多,就有
    可能使栈发生溢出现象,从而出现不可预料的后果。运行algo3-8.cpp 时,m、n 的取值就
    不可过大。

    // algo3-8.cpp 用递归调用求Ackerman(m,n)的值
    #include<stdio.h>
    int ack(int m,int n)
    {
    	int z;
    	if(m==0)
    		z=n+1; // 出口
    	else if(n==0)
    		z=ack(m-1,1); // 对形参m降阶
    	else
    		z=ack(m-1,ack(m,n-1)); // 对形参m、n降阶
    	return z;
    }
    void main()
    {
    	int m,n;
    	printf("Please input m,n:");
    	scanf("%d,%d",&m,&n);
    	printf("Ack(%d,%d)=%d
    ",m,n,ack(m,n));
    }

    代码运行结果:
    /*
    Please input m,n:3,9
    Ack(3,9)=4093
    Press any key to continue
    
    */
    // algo3-9.cpp 用递归函数求解迷宫问题(求出所有解)
    #include"c1.h" // 根据《PASCAL程序设计》(郑启华编著)中的程序改编
    #include"func3-1.cpp" // 定义墙元素值为0,可通过路径为-1,通过路径为足迹
    void Try(PosType cur,int curstep)
    { // 由当前位置cur、当前步骤curstep试探下一点
    	int i;
    	PosType next; // 下一个位置
    	PosType direc[4]={{0,1},{1,0},{0,-1},{-1,0}}; // {行增量,列增量},移动方向,依次为东南西北
    	for(i=0;i<=3;i++) // 依次试探东南西北四个方向
    	{
    		next.x=cur.x+direc[i].x; // 根据移动方向,给下一位置赋值
    		next.y=cur.y+direc[i].y;
    		if(m[next.x][next.y]==-1) // 下一个位置是通路
    		{
    			m[next.x][next.y]=++curstep; // 将下一个位置设为足迹
    			if(next.x!=end.x||next.y!=end.y) // 没到终点
    				Try(next,curstep); // 由下一个位置继续试探(降阶递归调用,离终点更近)
    			else // 到终点
    			{
    				Print(); // 输出结果(出口,不再递归调用)
    				printf("
    ");
    			}
    			m[next.x][next.y]=-1; // 恢复为通路,以便在另一个方向试探另一条路
    			curstep--; // 足迹也减1
    		}
    	}
    }
    void main()
    {
    	Init(-1); // 初始化迷宫,通道值为-1
    	printf("此迷宫从入口到出口的路径如下:
    ");
    	m[begin.x][begin.y]=1; // 入口的足迹为1
    	Try(begin,1); // 由第1步入口试探起
    }

    代码运行结果如下:


    此迷宫从入口到出口有2 条路径。由入口(1 行1 列足迹1)处向4 个方向试探,只有2
    个方向(东、南)是通路(-1)。首先朝东继续试探。足迹2~4 都只有1 个方向是通路,足
    迹5 到达出口。输出路径且逐一恢复足迹为-1(通路)。恢复后的情况是除入口为1 外,迷
    宫其它各点与初态相同,以便第2 条路径(由入口向南)继续试探。

    // algo3-10.cpp Hanoi塔问题,调用算法3.5的程序
    #include<stdio.h>
    int c=0; // 全局变量,搬动次数
    void move(char x,int n,char z)
    { // 第n个圆盘从塔座x搬到塔座z
    	printf("第%i步: 将%i号盘从%c移到%c
    ",++c,n,x,z);
    }
    void hanoi(int n,char x,char y,char z) // 算法3.5
    { // 将塔座x上按直径由小到大且自上而下编号为1至n的n个圆盘
    	// 按规则搬到塔座z上。y可用作辅助塔座
    	if(n==1) // (出口)
    		move(x,1,z); // 将编号为1的圆盘从x移到z
    	else
    	{
    		hanoi(n-1,x,z,y); // 将x上编号为1至n-1的圆盘移到y,z作辅助塔(降阶递归调用)
    		move(x,n,z); // 将编号为n的圆盘从x移到z
    		hanoi(n-1,y,x,z); // 将y上编号为1至n-1的圆盘移到z,x作辅助塔(降阶递归调用)
    	}
    }
    void main()
    {
    	int n;
    	printf("3个塔座为a、b、c,圆盘最初在a座,借助b座移到c座。请输入圆盘数:");
    	scanf("%d",&n);
    	hanoi(n,′a′,′b′,′c′);
    }

    代码运行结果如下:
    /*
    3个塔座为a、b、c,圆盘最初在a座,借助b座移到c座。请输入圆盘数:3
    第1步: 将1号盘从a移到c
    第2步: 将2号盘从a移到b
    第3步: 将1号盘从c移到b
    第4步: 将3号盘从a移到c
    第5步: 将1号盘从b移到a
    第6步: 将2号盘从b移到c
    第7步: 将1号盘从a移到c
    Press any key to continue
    
    
    */



  • 相关阅读:
    EL表达式 (详解)
    宜信面试整理
    Java 合并两个排序数组
    动态规划初识(爬楼梯问题)
    二叉树的最小深度
    ElasticSearch 单字符串多字段查询评分问题
    ES 分词
    汽车之家 面试总结
    浪潮之巅读书笔记
    闲徕互娱 面试总结
  • 原文地址:https://www.cnblogs.com/KongkOngL/p/3945963.html
Copyright © 2020-2023  润新知