栈和队列
四、存储结构说明和定义:
StackInt用来存放操作数和运算结果 StackChar用来存放运算符
typedef struct {//存数字
ElemInt *base;
ElemInt *top;
int stacksize;
}StackInt;
typedef struct{//存符号
ElemChar *base;
ElemChar *top;
int stacksize;
}StackChar;
五、主要算法:
- 给出程序的概要设计或主要算法的程序流程图
采用算符优先法,使用两个工作栈,一个是OPTR 用来寄存运算符,另外一个OPND用来寄存操作数或者是运算结果;
算法的基本思想是首先将操作数栈为空栈,表达式的起始符‘=’为运算符栈得到底栈元素,用字符数组存放输入的表达式,依次判断字符,如果字符是运算符则进入OPTR栈,如果是操作数则进入OPND栈,直到结束。其中对运算数的操作是利用数组Num存放,再将其转换为数型。
Precede()函数根据下表的算符之间的优先关系进行了比较,判断了算符的优先。
- 给出核心函数的算法(用类C语言,请注意算法与源程序书写方式的区别)
int EvaluateExpression(StackInt OPND,StackChar OPTR)//算符优先法 :OPTR 运算符栈 OPND运算数栈
{
InitChar(OPTR);
PushChar(OPTR,'=');//
InitInt(OPND);
//数字的处理
gets(SS); //输入表达式
p=SS;
while(*p!='='||GetTopChar(OPTR)!='=')
{
if(!In(*p)){//不是符号 是数字
Num[i]=*p;
i++;
p++;
if(In(*p))
{
Num[i]='\0';
Data=atof(Num);
PushInt(OPND,Data);
i=0;
}
}
else //是符号
switch(Precede(GetTopChar(OPTR),*p))//判断符号的优先性
{
case '<': PushChar(OPTR,*p);p++;break;
case '=': PopChar(OPTR,x);p++;break;// )
case '>': PopChar(OPTR,theta); //栈顶元素和当前元素
PopInt(OPND,b);
PopInt(OPND,a);
PushInt(OPND,Operate(a,theta,b)); //计算结果入栈
break;
}
}
return GetTopInt(OPND);
}
ElemChar Precede(ElemChar a, ElemChar b)
{
switch(a)
{
case '+':if(b == '+' || b == '-' || b == ')' || b == '=') return ('>');
else return ('<'); break;
case '-':if(b == '+' || b == '-' || b == ')' || b == '=') return ('>');
else return ('<'); break;
case '*':if(b == '(') return ('<');
else return ('>'); break;
case '/':if(b == '(') return ('<');
else return ('>'); break;
case '(':if(b == ')') return ('=');
else return ('<'); break;
case ')':return ('>'); break;
case '=':if(b == '=') return ('=');
else return ('<'); break;
}
}
- 给出每个子函数的函数头
1、ElemInt EvaluateExpression()算符优先法 :OPTR 运算符栈 OPND运算数栈
2、ElemChar GetTopChar(StackChar S) //返回运算符栈的栈顶元素
3、ElemInt GetTopInt(StackInt S) //返回运算数栈的栈顶元素
4、Status In(char c)判断是否为运算符,是运算符则返回 TRUE, 否则返回 FALSE
5、Status InitChar(StackChar &S) //运算符栈的初始化操作
6、Status InitInt(StackInt &S) //运算数栈的初始化操作
7、double Operate(ElemInt a, ElemChar theta, ElemInt b) a,b为运算数theta 为运算符,进行a theta b的四种运算
8、Status PopChar(StackChar &S, ElemChar &e) //运算符栈的出栈操作
9、Status PopInt(StackInt &S, ElemInt &e) //运算数栈的出栈操作
10、ElemChar Precede(ElemChar a, ElemChar b) 判断a b两个运算符的优先级别,a级别高返会> ,b的优先级高返回<,两者相等返回=;
11、Status PushChar(StackChar &S, ElemChar e) //运算符栈的进栈操作
12、Status PushInt(StackInt &S, ElemInt e) //运算数栈的进栈操作
六、程序运行结果测试:
1、错误的示例。
2、计算:8+9-(6+2)*2=
预测的结果:1
栈的展示:
3、计算:(12+8-9)6+(8-(2+1)(6+1))=
预测的结果:53
七、本次实验小结:
- 分析自己的主要算法
ElemInt EvaluateExpression()算符优先法,对字符只进行一次访问,时间复杂度是O(n);巧妙的利用了数栈和符号栈,进行了表达式的运算。 - 说明本次实验中遇到的问题和你的解决方案
(1)、在实验过程中,我选择了两个栈分别是用来存整型和字符型的数据,但是在输入表达式的过程中采用getchar(),函数getchar()读取stdin流中的下一个字符,因此的入读的是char型数据,导致结果一直是错误的。
解决方案:使用gets()函数进行输入表达式,简单的讲输入一次进行完成,只需要判断输入的字符串中的字符,只需要对字符进行操作。 - 对本次实验的完成情况自我评价
本次实验的算法按照《[数据结构(C语言版)].严蔚敏_吴伟民》
选题2:迷宫
四、存储结构说明和定义:
typedef struct
{
int m, n; //行号列号
int arr[Size][Size];
}MazeType;
typedef struct
{
int row; //迷宫中的行
int col; //迷宫中的列
}PosType;
typedef struct
{
int step; //当前位置在路径上的"序号"
PosType seat;//当前的坐标位置
DirectiveType di;//往下一个坐标位置的方向
}SElemType; //栈的元素类型
typedef struct
{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
五、主要算法:
4. 给出程序的概要设计或主要算法的程序流程图
程序的主要流程图:
MazePath算法的主要流程图:MazePath(迷宫,起点,终点)
- 给出核心函数的算法(用类C语言,请注意算法与源程序书写方式的区别)
Status MazePath(MazeType& maze, PosType start, PosType end)
{
InitStack(s);
PosType curpos = start;
int curstep = 1; //探索第一步
do {
if (Pass(maze, curpos))
{ //没有被访问过
FootPrint(maze, curpos);//标为 2
e = CreateSElem(curstep, curpos, 1);//创建元素
Push(s, e);//加入路径
if (PosEnd(curpos, end))//到达终点
return true;
curpos = NextPos(curpos, 1);//获得下一节点
curstep++; //探索下一步
}
else
{ //当前位置不能通过
if (!StackEmpty(s))
{
Pop(s, e);
while (e.di==4&&!StackEmpty(s)) //四个方向都访问过了
{
MarkPrint(maze, e.seat);//标记3
Pop(s, e); //退回一步
}
if (e.di < 4)
{
e.di++; //换一个方向探索
Push(s, e);
curpos = NextPos(e.seat, e.di);
}
}
}
} while (!StackEmpty(s));
return false;
}
- 给出每个子函数的函数头
1、SElemType CreateSElem(int step, PosType pos, int di)创建下一个步的元素,step表示步数,pos是位置,di是方向;
2、Status DestoryStack(SqStack& s) //销毁栈
3、bool FootPrint(MazeType& maze, PosType curpos) //访问过的路需要修改标记,这个路径可以通过,标记为2
4、bool MarkPrint(MazeType& maze, PosType curpos) // 访问过的路 ,不能走的通,标记为3
5、Status GetTop(SqStack s, SElemType& e)获得栈顶元素
6、bool InitMaze(MazeType& maze, int a[ROW][COL], int row, int col)//初始化迷宫;
7、bool Pass(MazeType maze, PosType curpos)//判断这个点是否被访问过
8、Status InitStack(SqStack& s)初始化栈
9、Status MazePath(MazeType& maze, PosType start, PosType end)//查找迷宫,maze是迷宫,start是迷宫的起点,end是迷宫的终点(出口)
10、PosType NextPos(PosType curpos, DirectiveType di) curpos是当前位置,di是下一步的方向,修改下一步的位置;
11、bool Pop(SqStack& s, SElemType& e)出栈
12、bool PosEnd(PosType pos1, PosType pos2)判断pos1的位置和pos2的位置是否相同,即判断是否是终点
13、void PrintMaze(MazeType maze, int row, int col)//打印迷宫
14、Status Push(SqStack& s, SElemType e)入栈
15、bool StackEmpty(SqStack s)判断栈是否为空
六、程序运行结果测试:
1、输入:
1 1 1 1 1 1 1 1 1 1
1 0 1 1 0 0 0 1 0 1
1 0 0 1 0 0 0 1 0 1
1 0 1 0 0 1 1 0 0 1
1 0 1 0 1 0 0 1 0 1
1 0 0 0 1 0 0 0 1 1
1 0 0 0 0 0 1 0 1 1
1 0 1 1 1 0 1 0 0 1
1 1 0 0 0 1 0 0 0 1
1 1 1 1 1 1 1 1 1 1
1 1 8 8
2
输入:
1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 0 1 0 1
1 0 0 1 0 0 0 1 0 1
1 0 0 0 0 1 1 0 0 1
1 0 1 0 1 0 0 1 0 1
1 0 0 0 1 0 0 0 0 1
1 0 1 0 0 0 1 0 1 1
1 0 1 1 1 0 1 0 0 1
1 1 0 0 0 1 0 0 0 1
1 1 1 1 1 1 1 1 1 1
1 1 8 2
3、输入
1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 0 1 0 1
1 0 0 1 0 0 0 1 0 1
1 0 0 0 0 1 1 0 0 1
1 0 1 1 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 1
1 0 1 0 0 0 1 0 0 1
1 0 1 1 1 0 1 1 0 1
1 1 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1
1 1 8 8
九、本次实验小结:
(此栏不允许空白)
4. 分析自己的主要算法
本次实验主要是利用栈先进后出的特点,利用穷举法进行迷宫的求解。其中的主要算法是MazePath();时间复杂度:O(n^2);本次实验用二维数组存放迷宫,用栈存放走过的路径。
5. 说明本次实验中遇到的问题和你的解决方案
1、由于输出函数的打印方式的空格的问题,使迷宫的的呈现误导实验;
2、创建下一个节点时没有返回节点;
解决方案:细化迷宫的打印方式,0,1,2,3用不同的方式;
6. 对本次实验的完成情况自我评价
本次实验利用穷举法进行迷宫问题的求解,很好的利用了栈的先进后出的特点,查找迷宫的出口,用不同的数字标记迷宫访问的路径,清晰的展示了迷宫的访问路径。
缺点:采用穷举法,时间复杂度比较大。