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

目录
  • 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语言递归实现火车调度算法详解的文章就介绍到这了,更多相关用C语言递归实现火车调度算法详解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于C语言实现简单的12306火车售票系统

    程序设计要求用C语言写一个简单的火车售票系统,主要实现的功能为: 录入班次信息 浏览班次信息 按班次号查询 按终点站查询 按余票数量排序保存 售票 退票 更新班次信息 退出系统 所有的班次信息保存在number.dat文件中,排序过后的保存在sort.dat中(.dat是一种二进制文件). 在编写的过程中我觉得在判断火车的状态比较值得深究.这里假设火车主要有四种状态: 1.未发车 2.已发车 3.停止检票 4.停止退票 在程序中,思路是将代表发车时间的字符串转化为整型,再和系统现在的时间进行大小

  • 纯C语言实现火车售票系统

    这是好久之前写的一个火车售票系统, 写的非常粗糙, 后来也没改了, 希望遇见有缘人继续优化吧. 主要的功能是:设置车次,删除车次, 买票, 改签, 退票, 查询等. #include<stdio.h> #include<stdlib.h> #include<malloc.h> #include<string.h> #include<conio.h> #define MAXNUM 10000 #define Num 100 typedef stru

  • 用C语言递归实现火车调度算法详解

    目录 1.代码 2.代码详解 3.用二叉树表示调用过程 4.思维导图 笔者在李云清版的<数据结构>中第二章遇到了这道经典的火车调度题,经过对一些前辈的代码进行学习,以下将这段火车代码进行分析详解,不对之处,还请各位大佬指示,不胜感激! 1.代码 题目如下: 2.8编号为1,2,3,4的四列火车通过一个栈式的列车调度站,可能得到的调度结果有哪些?如果有n列火车通过调度站,请设计一个算法,输出所有可能的调度结果. 算法运用的思想是运用栈+递归,算法的难点也在于此.先上代码: #include &l

  • C语言中递归和排列组合详解

    目录 排列组合三大问题: 1.打印n个数的全排列 2.打印n个数中任意m个数的全排列 3.打印n个数中任意m个数的组合 完整代码如下: 总结 排列组合三大问题: 1.打印n个数的全排列2.打印n个数中任意m个数的全排列3.打印n个数中任意m个数的组合 1.打印n个数的全排列 这个题实际上是可以直接用STL中的next_permutation()函数,代码如下: #include<bits/stdc++.h> using namespace std; int main(){ int data[4

  • Go语言基础模板设计模式示例详解

    目录 概述 模板模式生活案例 策略模式涉及到两个角色 UML 总结 示例 概述 模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式.让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤 确定了步骤的执行顺序,单某些步骤因环境或人等因素具体实现是未知的 模板模式生活案例 请客吃饭[点菜->吃东西->结账],每个人点菜不一样,吃东西不一样,结账也不一样从某地到某地[起点->出行方式->终点]起点和终点不一一样,但是每个人出行方式是不一样的 Go没有封装.

  • C语言 链式二叉树结构详解原理

    目录 前言 二叉树节点声明 二叉树的遍历 构建二叉树 1.前序遍历 2.中序遍历 3.后序遍历 二叉树节点的个数 二叉树叶子节点的个数 二叉树第K层节点个数 二叉树的高度/深度 二叉树查找值为x的节点 整体代码 前言 二叉树不同于顺序表,一颗普通的二叉树是没有增删改查的意义.普通的二叉树用来存储数据是不方便的.但是二叉树的一些基本实现结构,例如前序遍历,中序遍历...等等都是对我们学习更深层次的二叉树打下夯实的基础. 二叉树节点声明 typedef char BTDataType; typede

  • C语言程序环境和预处理详解分析

    目录 一.程序的翻译环境和运行环境 程序的翻译环境 链接阶段 执行环境(运行环境) 二.预处理详解 预定义符号 #define定义标识符 #define定义宏 #define 替换规则 #和##两个预处理的工具 带副作用的宏参数 宏和函数对比 #undef移除宏 命令行定义 条件编译 头文件包含 嵌套文件包含 总结 一.程序的翻译环境和运行环境 重点:任何ANSI C(标准C的程序)的一种实现,存在两个不同的环境 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令. 第2种是执行环境,

  • C语言 模拟实现strlen函数详解

    目录 前言 一.strlen函数的介绍 1.strlen函数的声明 2.strlen函数的简单运用 3.注意事项 二.三种实现strlen函数的方法 1.计数器的方法 2.递归方法 3.指针-指针的方法 前言 用C语言模拟实现strlen函数,我这里有三种方法,快来看看跟你用的方法是否是一样. 一.strlen函数的介绍 1.strlen函数的声明 size_t strlen ( const char * str ): 这里函数的返回值为无符号整形(size_t),传入的是一个常量char*类型

  • C语言算法学习之双向链表详解

    目录 一.练习题目 二.算法思路 1.设计浏览器历史记录 2.扁平化多级双向链表 3.展平多级双向链表 4.二叉搜索树与双向链表 一.练习题目 题目链接 难度 1472. 设计浏览器历史记录 ★★★☆☆ 430. 扁平化多级双向链表 ★★★☆☆ 剑指 Offer II 028. 展平多级双向链表 ★★★☆☆ 剑指 Offer 36. 二叉搜索树与双向链表 ★★★★☆ 二.算法思路 1.设计浏览器历史记录 1.这是一个模拟题: 2.初始化生成一个头结点,记录一个当前结点: 3.向前 和 向后 是两

  • Go语言包和包管理详解

    目录 1 包简介 1.1 工作空间 1.2 源文件 1.3 包命名 1.4 main 包 2导包 2.1 两种方式 2.2 包的别名 2.3 简洁模式 2.4非导入模式(匿名导入) 2.5 导包的路径 2.6 远程导入 3 初始化 init 3.1 init总结 4 包管理 4.1 演变过程 4.2 Go Model优点 4.3 启用go module 4.4 GOPROXY 5 go mod详解 5.1 go mod命令 5.2 go.mod说明 5.2.1 依赖的版本 5.2.2 repla

  • Go语言数据结构之二叉树可视化详解

    目录 题目 源代码 做题思路 扩展 左右并列展示 上下并列展示 总结回顾 题目 以图形展示任意二叉树,如下图,一个中缀表达式表示的二叉树:3.14*r²*h/3 源代码 package main import ( "fmt" "io" "os" "os/exec" "strconv" "strings" ) type any = interface{} type btNode struc

  • go语言编程实现递归函数示例详解

    目录 前言 函数中的 return 递归的问题 总结 前言 本篇文章主要是记录一下在 GScript 中实现递归调用时所遇到的坑,类似的问题在中文互联网上我几乎没有找到相关的内容,所以还是很有必要记录一下. 在开始之前还是简单介绍下本次更新的 GScript v0.0.9 所包含的内容: 支持可变参数 优化 append 函数语义 优化编译错误信息 最后一个就是支持递归调用 先看第一个可变参数: //formats according to a format specifier and writ

随机推荐