整理电脑的时候,发现很久之前的课程设计,虽然很简单的课设,但还是想将它分享输来,不然就永远“烂”在我电脑里了,觉得有点可惜。
一、 问题陈述
假设停在铁路调度站入口处的车厢序列的编号一次为1,2,3,4。设计一个程序,求出所有可能由此输出的长度为4的车厢序列。
二、 问题分析与设计
车厢调度问题是实际生活中的一个抽象问题,实际上其本质就是一个N个数的全排列问题,所谓全排列算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。
N个字符的全体排列之间存在一个确定的线性顺序关系。所有的排列中除最后一个排列外,都有一个后继;除第一个排列外都有一个前驱。每一个排列的后继都可以从它的前驱经过最少的变化得到,全排列的生产算法就是从第一个排列开始逐个生产所有的排列的方法。
全排列的生产法通常有几下几种:
字典排序法:从右往左开始找出第一个比右边数字小的数字的序号j,然后在以j为基准再从左往右,找出第一个比它大的最小的数,然后这两个数位置对调。例如:1 5 4 7 6 32 的下一个排列是 1 56 74 3 2
邻位交换法:邻位对换法中下一个排列总是上一个排列某相邻两位对换得到的。例如:以4个元素的排列为例,将最后的元素4逐次与前面的元素交换,可以生成4个新排列:1 2 3 4、1 243、14 2 3、4 1 2 3。然后将最后一个排列的末尾的两个元素交换,再逐次将排头的4与其后的元素交换,又生成四个新排列:4 13 2、143 2、1 3 4 2、1 3 2 4。再将最后一个排列的排头的两个元素交换,再将4从后往前移:3 1 2 4、3 14 2、34 1 2、4
递归类算法:
我将选择递归类算法作为实现该车厢调度的主要算法,通过设计perm递归函数,画出递归流程图如下图所示:
三、 程序代码
#include <stdio.h>
#include<stdlib.h>
int n = 0;
//交换两个数
void swap(int *a, int *b)
{
int m;
m = *a;
*a = *b;
*b = m;
}
//递归
void perm(FILE* fw,int list[], int k, int m)
{
int i;
if(k > m)
{
for(i = 0; i <= m; i++)
{
fprintf(fw,"%d ", list[i]);
printf("%d ", list[i]);
}
printf("\n");
fprintf(fw,"\n");
n++;
}
else
{
for(i = k; i <= m; i++)
{
swap(&list[k], &list[i]);
perm(fw,list, k + 1, m);
swap(&list[k], &list[i]);
}
}
}
int main()
{
FILE* fw=fopen("pailie.txt","wt");
int i;
int j=0;
printf("请输入列车长度N:");
scanf("%d",&i);
int list[i];
for(j;j<i;j++)
{
list[j]=j+1; //0-i
}
perm(fw,list,0,i-1);
printf("total:%d\n", n);
fprintf(fw,"total:%d\n",n);
fclose(fw);
return 0;
}
四、 运行结果
如果输入列车长度为4,则运行结果如下:
将数据库保存到txt:
五、 设计体会与总结
首先谈谈选题,本来可以做一个类似管理系统那一类的题目,但通过网上了解,无论是公司面试,还是各种竞赛,递归算法都是考察的重中之重,也是解决问题的一种非常好的方法,于是我就决定放弃原来的题目,去选择这个车厢调度算法,用递归来实现。通过这半个月的时间,我如期完成了此次的课程设计,车厢调度算法从本质上讲就是N个数的全排列算法,虽然从代码量上看是比较少,但是能够彻底的从分析问题到理解问题再到解决问题,实际上还是没那么容易。通过网上查阅资料发现一些牛人在面试华为公司曾遇到过这类的笔试题,让应试者在二十分钟内能用递归方法写出这个算法那能进的机会就大大增加,从这点来看,就给我们一个启示,我们大学生应该注重算法的分析以及打牢和巩固基础,这样在实现一些比较稍微大型的一些项目来才会显的得心应手。
该算法主要涉及到的一些知识点和难点主要有一下几个方面,这也是我完成了该算法的一些收获:
一、深刻的理解了形参和实参的传递问题,一些基本类型,例如整形,字符型以及字符串型都是以形参的形式传递的,而数组这些是以实参形式传递的。举个例子:如果想要在主函数中通过传参的方式来交换main函数中两个整形变量a,b的值,那么外部函数中的两个参数必须是以指针或者引用类型来作为参数类型,在主函数中这样调用swap(&a,&b),即将两个变量的地址作为参数,去调用外部的swap函数,这样才能实现预期的效果。但是,如果主函数中是一个整形数组,例如intarray[]={a,b},那么在调用外部的swap函数,则不需要用用指针,可以将该array数组名直接作为swap的参数,进行调用,因为数组作为参数传递本身就是传递的地址,也就是实参形式传递。形参与实参的传递是本次实验所获得的最深刻的体会之一。
二、对递归的理解也更加深刻,如果一个方法用递归和非递归都能够实现,那么递归能够减少代码量或许还能够节省时间或空间复杂度,但不易于理解,非递归算法容易理解,但是代码量会比较多,占用的空间会比较多,所谓任何事都用双面性,有利有弊,递归就是一个很好的体现。
三、我将运行的结果保存到文本中,也锻炼和巩固C语言中文本操作的知识,例如:fopen("pailie.txt","wt"),第一个参数是文件名,如果该文件不存在则创建该文件,如果存在则可以通过fprintf()函数往文件中写入,而fopen("pailie.txt","at"),则是对文件进行追加写入。复习文件的写入的时候还巩固了文件的读入,用过fgets()方法来实现对文本中数据的读取。
以上三点就是我在课程设计的过程中觉得收获最大的三点,在课程设计过程中,能够锻炼自己分析问题,解决问题的能力,通过认真仔细的画第二节的那张递归程序的执行流程图,就觉得画图法是理解递归程序的一个非常不错的方法,不然的话就我个人而言觉得想要理解递归算法的话还是一件比较头疼的事,所以觉得无论是学习还是做某个事,如果方法得当,那么能够达到预期的效果就比较快,效率比较高。总的来讲这两周的时间,还是有不少的收获,希望能够将这些收获的方法应用与以后的面试或者工作中!
欢迎关注我的微博: