栈的应用(算法与数据结构课程设计)
栈的应用
一、问题描述
在数据结构中,栈是一种重要的线性表,它的基本操作是线性表操作的子集,是限定性的数据结构,它有广泛的应用,在表达式求值的算法中就运用了栈的操作。对于车厢调度问题,假设停在铁路调度站入口处的车厢序列的编号依次为1,2,3,...n,可以利用栈求出所有可能由此输出的长度为n的车厢序列。
二、基本要求
1、在表达式求值问题中,选择合适的储存结构设计栈并完成表达式求值;
2、运用顺序栈求解车厢调度问题;
3、运用链式栈求解车厢调度问题。
三、测试数据
1、表达式求值的测试数据:
12-36/6+10*(3+5)
2、车厢调度的测试数据:
数据为5车厢调度
四、算法思想
1、表达式求值的算法思想:
1)定义两个栈,一个储存运算符号的optr,一个储存操作数字与运算结果的opnd,opnd 先入栈#,以在输入表达式后以#表示结束。
2)设计Precede函数,比较优先级,可以利用课本上的运算优先关系表设定多维数组,确定优先级。定义op字符数组储存运算符号,设计In函数判断输入的符号是否为运算符号;设计Operate程序进行运算。
3)表达式求值程序EvaluateExpression主要思想是依次读入表达式的字符,若是操作数则进opnd栈,若是运算符号则与optr顶端元素判断优先级决定是入栈还是运算,以此至整个表达式求值完毕。
2、车厢调度的算法思想:
编写函数search利用三组栈,第一组栈存放初始元素,,第二组与第一组进行元素排序,利用递归的思想进行不同排序,将结果存入第三组栈,对车厢序列进行排序。
五、模块划分
1、栈的相关模块:在表达式求值和车厢调度中都用到了栈的相关操作,它们的名字和作用相同,下面是对一些函数作用进行说明:
void InitStack(SqStack *S):栈的初始化。
void ClearStack(SqStack *S):清空栈。
int StackEmpty(SqStack S):判断栈是否为空。
void ClearStack(SqStack *S):求栈长。
void Push(SqStack *S,ElemType e):元素入栈。
void Pop(SqStack *S,ElemType *e):元素出栈。
void ClearStack(SqStack *S):取栈顶元素。
void StackTraverse(SqStack S):遍历栈并输出: 栈顶向栈底方向输出。
2、表达式求值模块:
char Precede(char c1,char c2):利用算数符号关系表判断运算优先级。
int In(char c,char *p):判断符号是否为计算符号。
char Operate(int a,char theta,int b):完成‘+’‘-’‘*’‘/’的运算。
int EvaluateExpression():完成表达式的求值运算。
3、车厢调度模块:
void search(SqStack s1,SqStack s2,SqStack s3,int a):利用三组栈完成车厢的排序。
六、数据结构//(ADT)
1、顺序栈的存储结构:
typedef struct
{ ElemType *base;
ElemType *top; } SqStack;
2、链式栈的存储结构:
typedef struct SNode
{ ElemType data;
struct SNode *next;} SNode, *LinkStack;
七、源程序
1、表达式求值
#include "stdlib.h"
#include "stdio.h"
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define NULL 0
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef int Status;
/* 定义顺栈类型 */
typedef char SElemType1;
typedef struct
{ SElemType1 *base;
SElemType1 *top;
int StackSize;
} SqStack;
/* 初始化顺栈 */
Status InitStack1(SqStack *S)
{ (*S).base=(SElemType1 *)malloc(STACK_INIT_SIZE*sizeof(SElemType1)); if (!(*S).base) exit(OVERFLOW);
(*S).top=(*S).base;
(*S).StackSize=STACK_INIT_SIZE;
return OK;
}
/* 清空顺栈 */
void ClearStack1(SqStack *S)
{ (*S).top=(*S).base; }
/* 判断顺栈是否为空 */
Status StackEmpty1(SqStack S)
{ if (S.top==S.base)
return TRUE;
else return FALSE;
}
/* 求顺栈长度 */
int StackLength1(SqStack S)
{ return S.top-S.base; }
/* 取顺栈的栈顶元素(注意:形参表与课本有差别) */
SElemType1 GetTop1(SqStack S)
{ if (S.top==S.base) return ERROR;
return *(S.top-1);
}
/* 入顺栈 */
Status Push1(SqStack *S,SElemType1 e)
{ if ((*S).top-(*S).base>=(*S).StackSize)
{(*S).base=(SElemType1*)realloc((*S).base,((*S).StackSize+STACKINCREMENT) *sizeof(SElemType1));
if (!(*S).base) exit(OVERFLOW);
(*S).top=(*S).base+(*S).StackSize;
(*S).StackSize+=STACKINCREMENT;
}
*((*S).top)=e; (*S).top++;
return OK;
}
/* 出顺栈 */
Status Pop1(SqStack *S,SElemType1 *e)
{ if ((*S).top==(*S).base)
return ERROR;
(*S).top--; *e=*((*S).top);
return OK;
}
/* 定义链栈类型 */
typedef int SElemType2;
typedef struct SNode
{ SElemType2 data;
struct SNode *next;
} SNode,*SLinkStack;
/* B2、初始化链栈 */
Status InitStack2(SLinkStack *S)
{ (*S)=(SLinkStack)malloc(sizeof(SNode));
if (!(*S)) exit(OVERFLOW);
(*S)->next=NULL;
return OK;
}
/* 判断顺栈是否为空 */
Status StackEmpty2(SLinkStack S)
{ if (!(S->next))
return TRUE;
else
return FALSE;
}
/* 取顺栈的栈顶元素*/
SElemType2 GetTop2(SLinkStack S)
{ if (StackEmpty2(S))
return ERROR;
return S->next->data;
}
/* 入链栈 */
Status Push2(SLinkStack *S, SElemType2 e)
{ SLinkStack p;
p=(SLinkStack)malloc(sizeof(SNode));
if (!p) exit(OVERFLOW);
p->data=e;
p->next=(*S)->next;
(*S)->next=p;
return OK;
}
/* 出链栈 */
Status Pop2(SLinkStack *S, SElemType2 *e)
{ SLinkStack p;
if (!(*S)->next) return(ERROR);
*e=(*S)->next->data;
p=(*S)->next;
(*S)->next=p->next;
free(p);
return OK;
}
int biao[7][7]={'>', '>', '<', '<', '<', '>', '>', '>', '>', '<', '<', '<', '>', '>', '>', '>', '>', '>', '<', '>', '>', '>', '>', '>', '>', '<', '>', '>', '<', '<', '<', '<', '<', '=', ' ',
'>', '>', '>', '>', ' ', '>', '>',
'<', '<', '<', '<', '<', ' ', '='};
char op[7]={'+','-','*','/','(',')','#'};
/*判断运算符号的优先级*/
char Precede(char c1,char c2)
{ int i,j,k;
for(k=0;k<7;k++)
{
if(c1==op[k]) i=k;
if(c2==op[k]) j=k;
}
return(biao[i][j]);//用上面运算优先关系表判断运算符号的优先关系}
/*判断元素是不是运算符号*/
int In(char c,char *p)//判断是不是运算符号
{ int i=0;
while(i<7)
if(c==p[i++])
return 1;
return 0;
}
/*计算运算式子*/
char Operate(int a,char theta,int b)//计算运算式
{ switch(theta)
{
case '+': return a+b;
case '-': return a-b;
case '*': return a*b;
case '/': return a/b;
}
}
/*计算表达式求值*/
int EvaluateExpression()
{ int a,b,y,flag;
char c,x,theta;
SqStack OPTR; SLinkStack OPND;
InitStack1(&OPTR);
Push1(&OPTR,'#');
InitStack2(&OPND);
c=getchar(); flag=0;
while(c!='#'||GetTop1(OPTR)!='#')
{
if(!In(c,op)) //不是运算符进栈
{ if (flag==0)
{ Push2(&OPND,c-'0'); flag=1;}
else
{ Pop2(&OPND,&y);
y=y*10+c-'0';
Push2(&OPND,y);
}
c=getchar();
}
else
{flag=0;
switch(Precede(GetTop1(OPTR),c))
{ case '<': //脱括号并接收下一字符 Push1(&OPTR,c); c=getchar(); break;
case '=':
Pop1(&OPTR,&x); c=getchar(); break;
case '>': //退栈并将运算结构入栈
Pop1(&OPTR,&theta);
Pop2(&OPND,&b); Pop2(&OPND,&a);
Push2(&OPND,Operate(a,theta,b));
break;
}
}
}
return GetTop2(OPND);
}
void main()
{ printf("\nResult=%d\n",EvaluateExpression()); }
2、车厢调度
顺序存储结构:
#include "stdlib.h"
#include "stdio.h"
#define N 100
/* 定义顺序栈类型 */
typedef int ElemType;
typedef struct
{ ElemType *base;
ElemType *top; } SqStack;
/* 初始化顺序栈 */
void InitStack(SqStack *S)
{ S->base=(ElemType *)malloc(N*sizeof(ElemType)); if (!S->base) exit(0);
S->top=S->base; }
/* 销毁顺序栈 */
void DestroyStack(SqStack *S)
{ free(S->base); }
/* 清空顺序栈 */
void ClearStack(SqStack *S)
{ S->top=S->base; }
/* 判断顺序栈是否为空 */
int StackEmpty(SqStack S)
{ if (S.top==S.base)
return 1;
else return 0; }
/* 求顺序栈长度,即元素个数. */
int StackLength(SqStack S)
{ return S.top-S.base; }
/* 取顺序栈的栈顶元素 */
void GetTop(SqStack S, ElemType *e)
{ if (S.top>S.base) *e=*(S.top-1); }
/* 元素压入顺序栈 */
void Push(SqStack *S,ElemType e)
{ if (S->top-S->base { *(S->top)=e; S->top++;} else printf("\n栈满"); } /* 元素弹出顺序栈 */ void Pop(SqStack *S,ElemType *e) { if (S->top>S->base) S->top--; *e=*(S->top); } /* 遍历顺序栈并输出: 栈底向栈顶方向输出 */ void StackTraverse(SqStack S) { printf("\nStack:"); while (S.base { printf("\t%d",*(S.base)); S.base++; }} /*车厢调度*/ void search(SqStack s1,SqStack s2,SqStack s3,int a) { int x; if(!StackEmpty(s1)) { Pop(&s1,&x); //利用递归将栈s1元素入s2排序 Push(&s2,x); search(s1,s2,s3,a); Pop(&s2,&x); //递归至s2排序完,s1中无元素将s2中入s1 Push(&s1,x); } if(!StackEmpty(s2)) { Pop(&s2,&x); Push(&s3,x); search(s1,s2,s3,a); Pop(&s3,&x); Push(&s2,x); } if( StackLength(s3)==a) StackTraverse(s3); } main() { int n,i; SqStack s1,s2,s3;InitStack(&s1);InitStack(&s2);InitStack(&s3); printf("请输车厢长度n:"); scanf("%d",&n); for(i=n;i>=1;i--) Push(&s1,i); printf("车厢输出序列为:"); search(s1,s2,s3,n); system("pause") ; } 链式存储结构: #include "stdlib.h" #include "stdio.h" #define NULL 0 /* 定义链栈类型 */ typedef int ElemType; typedef struct SNode { ElemType data; struct SNode *next; } SNode, *LinkStack; /* 初始化链栈(没有头结点) */ void InitStack(LinkStack *top) { *top=NULL; } /* 销毁链栈 */ void DestroyStack(LinkStack *top) { LinkStack p; while(*top!=NULL) { p=*top; *top=(*top)->next; free(p); }} /* 清空链栈 */ void ClearStack(LinkStack *top) { LinkStack p; while(*top!=NULL) { p=*top; *top=(*top)->next; free(p); }} /* 判断顺栈是否为空 */ int StackEmpty(LinkStack top) { if (top==NULL) return 1; else return 0; } /* 求链栈长度 */ int StackLength(LinkStack top) { LinkStack p; int n=0; p=top; while (p!=NULL) { n++; p=p->next; } return n; } /* 取链栈栈顶元素 */ void GetTop(LinkStack top, ElemType *e) { if (top!=NULL) *e=top->data; } /* 入链栈 */ void Push(LinkStack *top, ElemType e) { LinkStack new; new=(LinkStack)malloc(sizeof(SNode)); new->data=e; new->next=*top; *top=new; } /* 出链栈 */ void Pop(LinkStack *top, ElemType *e) { LinkStack p; if (*top!=NULL) { *e=(*top)->data; p=*top; (*top)=p->next; free(p); }} /* 遍历链栈并输出: 栈顶向栈底方向输出 */ void StackTraverse(LinkStack top) { LinkStack p; printf("\nStack:"); p=top; while (p) { printf("\t%d",p->data); p=p->next; }} /*车厢调度*/ void search(LinkStack s1,LinkStack s2,LinkStack s3,int a) { int x; if(!StackEmpty(s1)) { Pop(&s1,&x); Push(&s2,x); search(s1,s2,s3,a); Pop(&s2,&x); Push(&s1,x); } if(!StackEmpty(s2)) { Pop(&s2,&x); Push(&s3,x); search(s1,s2,s3,a); Pop(&s3,&x); Push(&s2,x); } if( StackLength(s3)==a) StackTraverse(s3); } main() { int n,i; LinkStack s1,s2,s3;InitStack(&s1);InitStack(&s2);InitStack(&s3); printf("请输车厢长度n:"); scanf("%d",&n); for(i=n;i>=1;i--) Push(&s1,i); printf("车厢输出序列为:"); search(s1,s2,s3,n); system("pause") ; } 八、测试情况 1、表达式求值: 2、车厢调度:顺序栈与链式栈的测试结果相同。 九、参考文献 1、严蔚敏,《数据结构 C语言》,清华大学出版社。 2、谭浩强,《c语言程序设计》,清华大学出版社。 小结 通过本次课程设计,我学会了很多。编程不像做其它事,它要求编程人员有非常缜密的思维,很好的整体把握能力和很好的调试程序的能力等。在编写程序中间我们不仅要想好整个编程思路,打好框架,还需要考虑存储结构的适用性和程序的编写的简洁。在大的方面弄好了,我们也要注意小小的细节问题,像在车厢调度问题中,我们在“search(LinkList s)”中写成了“*s”,而导致整个程序的错误,在编程中像这样的小问题很多,我编程习惯性在第一次写出程序后,用电脑运行来发现一些语法错误,再修改正确,这是我在编程中的一个不好的习惯,如果没有错误提示,我想这会花费我很多的时间去发现和修改小错误,这个习惯我也要努力改正,在编程中要注意细节方面的问题,这样能节省很多的时间,而且在编程中注意细节的问题也能为自己在其他的方面培养良好的习惯。 在这次课程设计中,我是写表达式求值的程序,表达式求值是程序设计编译中的一个最基本的问题,它的实现是栈应用的一个典型例子。因为书本上有思想介绍,写起来并不是很费劲。书上只有主要的运算表达式函数,我要补充运算符的判断,数值计算函数和优先级判断函数,主要的程序都是按照数值栈与运算符号栈的应用方法编写,上课时也有提主要的写法,编写起来主要是注意思路的正确与细节方面的问题。我所写的“EvaluateExpression()”函数是按书上的程序编写,其中主要思想是符号选择入栈,数字入栈,符号要判断优先级再操作。在写表达式求值中间,我感觉自己好像在编写计算器。在我第一次完成程序的时候,修改完细节问题,并没有什么错误,但是在输入表达式的时候,不能显示运算结构,为此我前前后后又检查了好几遍程序,运行没错误,也没发现哪里写的思路是错误的,后来我找学习委员帮我改了下,程序和我之前写的没多大差别,后来在他运行给我看的时候才发现我一直少了一个“#”,表达式求值的程序在数值栈中是以“#”为结束标记,如果在输入表达式的时候没有输入结束标记就不能正确运行。在编程中除了程序的我们组组员的程序是车厢调度问题,是排序列车车厢序列,他们程序基本一样,只是采用不同的储存结构的栈来完成。链式栈与顺序栈的函数调用基本类似,主要的列车排序程序是一样的的,车厢调度中要注意的主要是在利用栈排序的时候要用上递归思想,还有在函数参数是要注意带“*”还是不带,在编程中我们就范了类似的错误。 刚开始我们组有位同学发给我的程序是用数组完成的车厢调度而没用上栈的内容,其实编写表达式求值和车厢调度的方法有很多,不过在利用栈的应用编程,我们更加了解了栈的定义结构以及应用。