笔者在李云清版的《数据结构》中第二章遇到了这道经典的火车调度题,经过对一些前辈的代码进行学习,以下将这段火车代码进行分析详解,不对之处,还请各位大佬指示,不胜感激!

用递归实现火车调度

  • 1、代码
  • 2、代码详解
  • 3、用二叉树表示调用过程
  • 4、思维导图

1、代码

题目如下:
2.8编号为1,2,3,4的四列火车通过一个栈式的列车调度站,可能得到的调度结果有哪些?如果有n列火车通过调度站,请设计一个算法,输出所有可能的调度结果。

算法运用的思想是运用栈+递归,算法的难点也在于此。先上代码:

#include <stdio.h>
#define MAX 100
typedef struct s{char a[MAX];int top;
}Stack;/*定义栈的数据*/
/*定义一些全局变量*/
Stack S;/*定义全局性的栈*/char d[MAX],seq[MAX];
/*d[MAX]用于存储原始入栈序列,seq[MAX]用于存储输出序列*/
int len;/*定义将通过栈的元素个数*/
int count=0;/* 用于统计输出序列的个数  */void initStack(Stack *S) /*初始化空栈*/
{S->top=-1;
}void push(Stack *S,char x) /*进栈*/
{if(S->top>=MAX) return;S->top++;S->a[S->top]=x;
}char pop(Stack *S) /*出栈*/
{if (S->top==-1) { printf("ERROR, POP Empty Stack");  return -1; }  S->top--;    return S->a[S->top+1];
} int isEmpty(Stack *S)/*判断栈是否为空*/
{     if (S->top==-1) return 1; return 0;
} void outSeq(char *seq, int len)/*输出顶点序列*/
{    int i; for(i=0; i<len; i++)  printf("%2c",seq[i]); printf("\n");
} void scheduler(int pos, int seq_pos)
{    /* pos: 处理到原始数据中的第pos个元素, seq_pos:若出栈,应放在当前输出数组的第seq_pos个位置
*/ int i=0;char t;
/*对任何一个数,总是先进栈,再出栈。另外,这里不需要循环,类似于"查找数组中元素"用递归*/ if(pos<len){/*一个数进栈后,有两种处理方式:要么立刻出栈,要么进行下一个数的进栈*/ push(&S,d[pos]); scheduler(pos+1,seq_pos); pop(&S); } if (!isEmpty(&S)){/*一个数出栈后,有两种处理方式:要么继续出栈,要么继续下一个数的进栈*/ t=pop(&S); seq[seq_pos++]=t; scheduler(pos,seq_pos); push(&S,t); } if (pos>=len && isEmpty(&S))  { outSeq(seq,len); count++; }
}int main(){ int i; printf("\nplease input the num be scheduled: "); scanf("%d", &len); /*用len存储待调度的火车数量*/ for(i=0; i<len; i++) d[i]='1'+i; /*创建火车编号,如a、b、c、...等*/ printf("the original seq is:"); outSeq(d,len); initStack(&S); scheduler(0,0); printf("\n count=%d", count); return 0;
} 

输入3(即三列火车),得到的结果如下:

2、代码详解

本算法主要是运用了栈+递归+回溯的思想,主要的代码块有三个:
代码块1

if(pos<len){  push(&S,d[pos]); scheduler(pos+1,seq_pos); pop(&S); }

代码块2

if (!isEmpty(&S)){t=pop(&S); seq[seq_pos++]=t; scheduler(pos,seq_pos); push(&S,t); }

代码块3

if (pos>=len && isEmpty(&S))  { outSeq(seq,len); count++; }

这里需要注意的是判定元素pos,是处理原始数据中第pos个元素,pos从0开始
代码块1根据你输入的len和第pos个元素来判定是否执行代码块1
例如当你输入了3,
通过代码

scanf("%d", &len);for(i=0; i<len; i++) d[i]='1'+i;

即有三列火车,分别代号为1,2,3
数组d中的位置分别是0,1,2

当代码第一次执行

void scheduler(int pos, int seq_pos)

函数的时候,进入了判定
此时参数pos和seq_pos都为0
那么0<len=3,执行代码块1
代码块1把数组第0个元素压入栈中,即1号火车进入车站

接着进行第一次调用函数scheduler

此时参数pos为1,seq_pos为0
因为1<3,继续执行代码块1
代码块1把数组第1个元素压入栈中,即2号火车进入车站

进行第二次调用函数scheduler

此时参数pos为2,seq_pos为0
因为2<3,继续执行代码块1
代码块1把数组第2个元素压入栈中,即3号火车进入车站

进行第三次调用函数scheduler

此时参数pos为3,seq_pos为0
因为3=len=3,所以开始执行代码块2

在代码块2中,把栈顶的元素赋值给t,同时把t放入seq数组的第0个位置中,seq++
即3号列车驶出火车站

进行第四次调用函数sceduler

此时参数pos=3,seq_pos=1
继续执行代码块2,把栈顶的元素赋值给t,同时把t放入seq数组的第1个位置中,seq++
即2号列车驶出火车站

进行第五次调用函数sceduler

此时参数pos=3,seq_pos=2
继续执行代码块2,把栈顶的元素赋值给t,同时把t放入seq数组的第2个位置中,seq++
即1号列车驶出火车站

进行第六次调用函数scheduler

此时参数pos=3,seq_pos=3,现在的情况是三列火车都已经驶出火车站了,也就是栈已经空了,同时满足pos>=len的条件,所以执行代码块3

代码块3把结果进行了输出,
输出结果是3,2,1
第六次调用函数scheduler整个过程结束

此时,代码开始进行回溯

回到了第五次调用函数scheduler
代码块2中scheduler执行完,执行push,也就是压栈操作,可是现在已经没有火车进站了,因为三列火车都已经走了

代码回到了第四次调用函数scheduler
代码块2中scheduler执行完,执行push,也就是压栈操作,也没有火车能进车站了
为什么?
还记不记得这个时候是3号列车和2号列车已经出去了,1号列车在车站里,所以没有多余的进站的车了

代码代码回到了第三次调用函数scheduler
还记不记得这个时候是3号列车、已经出去了,1号列车和2号列车在车站里,所以没有多余的进站的车了

代码代码回到了第二次调用函数scheduler

代码重新回到了代码块1

注意,是代码块1

此时,执行了pop,也就是进行了出栈操作
什么意思?
栈顶的3号列车驶出了车站

这里是笔者出现了思维误区的地方,读者不理解递归思想的需要特别注意,当时我在想,3号列车驶出后是不是回到了第一次调用函数?忽略了下面的if语句,错误的认为执行了代码块1后不会执行代码块2,混淆了if-else和if,if语句的关系

代码1执行完,开始执行代码2
注意此时的列车只有两辆,是1号列车和2号列车,参数是pos=2,seq_pos=0

代码块2进行了出栈操作,让在栈顶的2号列车出车站,然后seq_pos++

进行第七次调用函数sceduler

此时代码参数pos=2,seq_pos=1
pos=2<len=3,进入代码块1
代码块1把pos=2的元素压入栈中
什么意思?
把三号列车驶入车站

进行第八次调用函数sceduler

此时代码参数pos=3,seq_pos=1
pos=3=len=3,进入代码块2
代码块2进行了出栈操作,让在栈顶的3号列车出车站
然后seq_pos++

进行第九次调用函数scheduler

此时代码参数pos=3,seq_pos=2
pos=3=len=3,进入代码块2
代码块2进行了出栈操作,让在栈顶的1号列车出车站
然后seq_pos++

进行第十次调用函数scheduler

pos=3=len=3,同时栈里的三辆列车已经全部驶出车站了,所以进行执行代码块3
代码块3把结果进行了输出
输出结果是2,3,1

以此类推…

3、用二叉树表示调用过程

左子树表示压栈(进站),右子树表示出栈(驶出车站),线上数字表示调用函数次数,负数表示出栈,例如-1表示1号列车驶出车站

4、思维导图


本文代码参考自李云清《数据结构》第三版课本习题火车调度算法答案

本文有参考作者@littlehedgehog的火车调度详解,但作者@littlehedgehog并未对代码块1中pop的作用和代码块2中push进行分析,在此表示感谢

通过本题,可以加深对栈和递归算法的理解,感谢各位读者的浏览,谢谢!
如有错字问题还请谅解!

用C语言递归实现火车调度算法详解相关推荐

  1. C语言递归及经典例题详解

    什么是递归? 什么时候使用递归 例题1 顺序打印问题 例题2 求n的阶乘 例题3 求第n个斐波那契数 经典 汉诺塔问题 经典 青蛙跳台阶问题 什么是递归? 递归就是程序调用自身的编程技巧.递归通常把一 ...

  2. C语言递归实现字符串反转(详解)

    我看到这个题目后,甚至我看完答案以及解析后,我还是不能理解,怎么就搞完了,怎么转的? 这个递归搞得我云里雾里的,以至于我花了点时间在笔记和代码调试上面一步一步的运行,终于搞懂了,首先代码具体是这样: ...

  3. R语言可视化绘图基础知识详解

    R语言可视化绘图基础知识详解 图形参数:字体.坐标.颜色.标签等: 图像符号和线条: 文本属性: 图像尺寸及边界: 坐标轴.图例自定义等: 图像的组合: #install.packages(c(&qu ...

  4. php函数find的用法,c语言find函数的用法详解

    c语言find函数的用法详解 C语言之find()函数 find函数用于查找数组中的某一个指定元素的位置. 比如:有一个数组[0, 0, 5, 4, 4]: 问:元素5的在什么位置,find函数 返回 ...

  5. python爬虫beautifulsoup爬当当网_Python爬虫包 BeautifulSoup 递归抓取实例详解_python_脚本之家...

    Python爬虫包 BeautifulSoup  递归抓取实例详解 概要: 爬虫的主要目的就是为了沿着网络抓取需要的内容.它们的本质是一种递归的过程.它们首先需要获得网页的内容,然后分析页面内容并找到 ...

  6. php 递归中的全局变量,PHP中递归的实现实例详解

    递归的定义 递归(http:/en.wikipedia.org/wiki/Recursive)是一种函数调用自身(直接或间接)的一种机制,这种强大的思想可以把某些复杂的概念变得极为简单.在计算机科学之 ...

  7. java语言链栈_Java语言实现数据结构栈代码详解

    近来复习数据结构,自己动手实现了栈.栈是一种限制插入和删除只能在一个位置上的表.最基本的操作是进栈和出栈,因此,又被叫作"先进后出"表. 首先了解下栈的概念: 栈是限定仅在表头进行 ...

  8. 大二c语言期末考试题库及详解答案,大学C语言期末考试练习题(带详解答案)...

    <大学C语言期末考试练习题(带详解答案)>由会员分享,可在线阅读,更多相关<大学C语言期末考试练习题(带详解答案)(55页珍藏版)>请在金锄头文库上搜索. 1.一. 单项选择题 ...

  9. c语言线性表库函数大全,数据结构(C语言版)-线性表习题详解

    <数据结构(C语言版)-线性表习题详解>由会员分享,可在线阅读,更多相关<数据结构(C语言版)-线性表习题详解(23页珍藏版)>请在人人文库网上搜索. 1.数 据 结 构 ,线 ...

  10. c语言 read 文件字节没超过数组大小时会怎样_剑指信奥 | C 语言之信奥试题详解(四)...

    趣乐博思剑指信奥系列 ❝ 趣乐博思剑指信奥系列,专门针对全国青少年信息学奥林匹克联赛 NOIP 而开展的专业教育方案.开设的课程有 C 语言基础,C++ 语言基础,算法设计入门与进阶,经典试题分析与详 ...

最新文章

  1. C++继承中构造函数、析构函数调用顺序及虚析构函数
  2. 小程序 sha1和服务器有关系吗,微信小程序使用sha1实现密码加密的方法介绍
  3. Failed to resolve hostname 192: The name does not resolve for the supplied parameters
  4. AsyncDisplayKit
  5. [转] AKKA简介
  6. 【无码专区2】序列划分(数学)
  7. 牛客 21302 被3整除的子序列 (动态规划、Python)
  8. Luogu P2055 [ZJOI2009]假期的宿舍
  9. MySQL数据库(九) 一一 处理重复和SQL注入
  10. Leetcode c语言-Implement strStr()
  11. appium启动app失败_Python学下教程:另辟蹊径,appium抓取app应用数据了解一下
  12. IDL实现植被覆盖度计算
  13. try catch无法捕获 StackOverflowException
  14. DotNet开发的微商分销系统源码,微信三级分销系统源码
  15. Java程序员职业生涯规划
  16. 华为服务器 自动安装 黑屏,华为服务器显示器黑屏如何解决
  17. 闭锁java_Java项目实践,CountDownLatch实现多线程闭锁
  18. 阻塞非阻塞使用,initial assign always区别
  19. python实用小工具
  20. Windows XP系统正版验证出现的黑屏解决方法

热门文章

  1. 使用APP inventor制作蓝牙串口助手【智能浇灌模型中用到】
  2. journalctl用法详解
  3. 【Matlab】符号运算总结
  4. python图像风格迁移_快速图像风格迁移
  5. 自相关性,偏自相关性分析,时间序列相关性分析(spearman相关性,pearson相关性)
  6. mysql间隙锁 打开_MySQL数据库间隙锁
  7. 如何把word ppt 思维导图这类文件转化为高清晰度的图片(要干货只看粗体黑字)...
  8. 【软考 系统架构设计师】软件工程⑥ 软件系统建模
  9. python抓取贴吧_Python抓取图片(贴吧)
  10. psf点扩散函数matlab,点扩散函数(PSF)调制