数据结构 第3章 中缀表达式.docx
《数据结构 第3章 中缀表达式.docx》由会员分享,可在线阅读,更多相关《数据结构 第3章 中缀表达式.docx(47页珍藏版)》请在冰豆网上搜索。
![数据结构 第3章 中缀表达式.docx](https://file1.bdocx.com/fileroot1/2022-11/24/915b2b5e-e81d-4c6e-af70-55fc41c6fad7/915b2b5e-e81d-4c6e-af70-55fc41c6fad71.gif)
数据结构第3章中缀表达式
数据结构
实验报告(第三章)
实验类型:
综合性实验
班级:
学号:
姓名:
实验日期:
2014年5月24日
一、表达式求值
1.问题描述
表达式是数据运算的基本形式。
人们的书写习惯是中缀式,如:
11+22*(7-4)/3。
中缀式的计算按运算符的优先级及括号优先的原则,相同级别从左到右进行计算。
表达式还有后缀式(如:
2274-*3/11+)和前缀式(如:
+11/*22–743)。
后缀表达式和前缀表达式中没有括号,给计算带来方便。
如后缀式计算时按运算符出现的先后进行计算。
本设计的主要任务是进行表达式形式的转换及不同形式的表达式计算。
2.基本要求
●从文件或键盘读入中缀表达式。
●设计操作数为多位整数,操作符为加、减、乘、除、求模的中缀表达式求值算法。
●设计将中缀表达式转换为后缀表达式的算法。
●设计将中缀表达式转换为前缀表达式的算法。
●设计后缀表达式求值算法。
●设计前缀表达式求值算法。
●输出各种形式的表达式。
3.数据结构设计
任何一个表达式都是由操作符,运算符和界限符组成的。
我们分别用顺序栈来寄存表达式的操作数和运算符。
栈是限定于紧仅在表尾进行插入或删除操作的线性表。
顺序栈的存储结构是利用一组连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置,base为栈底指针,在顺序栈中,它始终指向栈底,即top=base可作为栈空的标记,每当插入新的栈顶元素时,指针top增1,删除栈顶元素时,指针top减1。
typedefstruct
{
int*base;
int*top;
intnumstacksize;//数字栈
}numstack;
typedefstruct
{
char*base;
char*top;
intcharstacksize;//字符栈
}charstack;
4.算法设计
(1)中缀表达式求值
1.从左到右读入中缀表达式,每次一个字符。
2.如果是操作数,压入操作数栈。
3.如果是操作符,压入操作符栈。
4.如果是左括号,直接忽略。
5.如果是有括号,弹出操作符栈栈顶元素,然后弹出操作数栈两个元素,进行操作以后结果压入操作数栈。
(2)中缀表达式变后缀表达式
1.从左到右读入中缀表达式,每次一个字符。
2.如果字符是操作数,直接输出。
3.如果是'(',入栈。
4.如果是')',则依次把栈中的的运算符加入后缀表达式中,直到出现'(',从栈中删除'('。
5.若为除括号外的其他运算符,当其优先级高于除'('以外的栈顶运算符时,直接入栈。
否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,直到一个比它优先级低的或者遇到了一个左括号为止。
6.当扫描的中缀表达式结束时,栈中的的所有运算符出栈。
(3)中缀表达式变前缀表达式
1.从右至左扫描中缀表达式,从右边第一个字符开始判断。
2.如果是数字,则分析到数字串的结尾并将数字串直接输出。
3.如果是运算符,则比较优先级。
如果当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),则将运算符直接入栈;否则将栈顶运算符出栈并输出,直到当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),再将当前运算符入栈。
4.如果是括号,根据括号的方向处理。
如果是右括号,则直接入栈;否则,遇右括号前将所有的运算符全部出栈并输出,遇右括号后将左右的两括号一起删除。
5.重复上述操作2直至扫描结束,将栈内剩余运算符全部出栈并输出。
6.再逆序输出字符串。
中缀表达式就转换为前缀表达式了。
(4)后缀表达式求值
1.从左到右读入后缀表达式
2.如果字符是一个操作数,把它压入栈。
3.如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。
4.到后缀表达式末尾,从堆栈中弹出结果。
若后缀表达式格式正确,那么堆栈应该为空。
(5)前缀表达式求值
1.从右至左扫描中缀表达式,从右边第一个字符开始判断,
2.如果当前字符是数字,则分析到数字串的结尾并将数字入栈。
3.如果是运算符,则将栈中最上面两个数字弹出并进行相应的运算。
结果入栈。
4.一直扫描到表达式的最左端时,最后栈中的值也就是表达式的值。
5.主要操作
对数字栈的操作和对字符栈的操作类似,以下只写出对字符栈的操作。
//构建一个字符空栈
statusinitstack(charstack&s)
{
s.base=(char*)malloc(stack_init_size*sizeof(char));
if(!
s.base)exit(-2);//如果地址申请失败,退出
s.top=s.base;//栈顶栈尾指向同一位置
s.charstacksize=stack_init_size;
cout<<"准备完毕,请输入表达式:
"<return1;
}
//用e返回字符栈顶的元素
chargettop(charstacks)
{chare;
if(s.top==s.base)return0;
e=*(s.top-1);
returne;
}
//字符入字符栈
statuspush(charstack&s,chare)
{
if(s.top-s.base>=s.charstacksize)
{
s.base=(char*)realloc(s.base,(s.charstacksize+stackincrement)*sizeof(char));
if(s.base)exit(-2);
s.top=s.base+s.charstacksize;
s.charstacksize+=stackincrement;
}
*s.top++=e;
return1;
}
//出字符站,用e返回
statuspop(charstack&s,char&e)
{
if(s.top==s.base)return0;
e=*--s.top;
return1;
}
//出字符站
statuspop(charstack&s)
{
if(s.top==s.base)return0;
--s.top;
return1;
}
//判断运算符优先级
charprecede(chara,charb)
{
charyouxian[9][9]={{'0','+','-','*','/','(',')','%','='},
{'+','>','>','<','<','<','>','<','>'},
{'-','>','>','<','<','<','>','<','>'},
{'*','>','>','>','>','<','>','>','>'},
{'/','>','>','>','>','<','>','>','>'},
{'(','<','<','<','<','<','=','<',''},
{')','>','>','>','>','','>','>','>'},
{'%','>','>','>','>','<','>','>','>'},
{'=','<','<','<','<','<','','<','='}
};
inti,j;
for(j=0;j<9;j++)
{
if(youxian[0][j]==b)break;
};
for(i=0;i<9;i++)
{
if(youxian[i][0]==a)break;
};
returnyouxian[i][j];
}
//判断ch是否是运算符
intin(charch)
{
charptr[10]={'+','-','*','/','(',')','%','='};
inti;
for(i=0;i<8;i++)
{
if(ch==ptr[i])
return1;
}
return0;
}
//计算二元表达式的值
intoperate(intaa,charthetaa,intbb)
{
intcc;
if((thetaa=='/'||thetaa=='%')&&bb==0)
{cout<<"输入有误!
"<if(thetaa=='+')
cc=aa+bb;
elseif(thetaa=='-')
cc=aa-bb;
elseif(thetaa=='*')
cc=aa*bb;
elseif(thetaa=='/')
cc=aa/bb;
elsecc=aa%bb;
returncc;
}
6.运行、测试与分析
(1)中缀表达式求值
1 求值
2 容错检验
(2)中缀表达式变后缀表达式
(3)中缀表达式变前缀表达式
(4)后缀表达式求值
(5)前缀表达式求值
2、任务调度
1.问题描述
多用户多任务操作系统中,多个任务同时共享计算机系统资源。
为了使多个任务均能够顺利执行,操作系统要按一定的原则对它们进行调度,使它们按一定的次序进行。
设只有一个CPU,现有多个任务,它们需要CPU服务的时间已知。
在下列假设下,按平均等待时间最短为原则,设计算法求出任务的执行顺序。
●忽略任务提交的时间差,即认为各任务同时提交。
●各任务不同时提交。
2.基本要求
●为任务列表设计数据结构和存储结构。
●任务输入,至少包括任务编号及所需CPU的服务时间,任务数不得少于5个。
●如果按提交顺序执行,求出每个任务的开始执行时间、终止时间、等待时间和所有任务的平均等待时间。
●按平均等待时间最短,设计任务调度算法,输出任务的执行序列;求出每个任务的开始执行时间、终止时间、等待时间和所有任务的平均等待时间;并把结果与上一时间对比。
3.数据结构设计
要使任务平均等待时间最短,则应遵循所需时间最短的任务优先执行的原则,
当每次提交新的任务时,都应重新排序,找出所需时间最短的任务,则定义了以下链式存储的队列。
typedefstructnode
{
node*next;
intnumber;//任务编号
inttime;//所需cpu的服务时间
inttijiao;//提交时间
intwait;//结点的等待时间
}sqList,*sqlistptr;
typedefstructlinkqueue
{
sqlistptrfront,rear;//队头、队尾指针
};
4.算法设计
1 任务同时提交
1)依次执行
计算出每个任务开始执行时时间,并把它们相加求出总和sum,再除以总任务数n便得到平均等待时间。
2)按等待时间最短方式执行
先对整个队列按所需时间升序排列,再按1)方法计算。
2 任务不同时提交
1)依次执行
求出每个任务到达时间a、执行完毕时的时间b、和所需的执行时间c,则s=b-a-c就是每个任务的等待时间。
每个任务等待时间的和再除以总任务数n得平均等待时间。
2)按等待时间最短执行
每次有新任务提交时,都要找出所需时间最短的任务并且把它放在队列第一位,
每过一秒,把第一个任务后面所有任务的等待时间都加一,当队头任务执行完后,出队,并且进入另一个队列k,反复执行上述步骤,直到队列为空,再输出队列k,把所有任务等待时间相加得sum再除以总任务数n得平均等待时间。
5.主要操作
//构造一个空队列Q
intinitqueue(linkqueue&Q)
{
if(!
(Q.front=Q.rear=(sqlistptr)malloc(sizeof(node))))
exit(-2);
Q.front->next=NULL;
return1;
}
intqueuelength(linkqueueQ)
{//求队列的长度
inti=0;
sqlistptrp;
p=Q.front;
while(Q.rear!
=p)
{
i++;
p=p->next;
}
returni;
}
//若队列不空,则用e返回Q的队头元素,并返回1,否则返回0
intgethead(linkqueueQ,node&e)
{
sqlistptrp;
if(Q.front==Q.rear)
return0;
p=Q.front->next;
e.number=p->number;
e.time=p->time;
e.tijiao=p->tijiao;
e.wait=p->wait;
return1;
}
//插入元素e为Q的新的队尾元素
intenqueue(linkqueue&Q,nodee)
{
sqlistptrp;
if(!
(p=(sqlistptr)malloc(sizeof(node))))//存储分配失败
exit(-2);
p->number=e.number;//编号
p->time=e.time;//所需时间
p->tijiao=e.tijiao;//提交时间
p->wait=e.wait;//等待时间
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return1;
}
//若队列不空,删除Q的队头元素,用e返回其值,并返回1,否则返回0
intdequeue(linkqueue&Q,node&e)
{
sqlistptrp;
if(Q.front==Q.rear)
return0;
p=Q.front->next;
e.number=p->number;
e.time=p->time;
e.tijiao=p->tijiao;
e.wait=p->wait;
Q.front->next=p->next;
if(Q.rear==p)
Q.rear=Q.front;
free(p);
return1;
}
//把time最小的放在首位
voidsort(linkqueue&l)
{
node*p,*q;
inti,j,temp;
n=queuelength(l);//任务数量n
p=l.front->next;//p指向第一个任务
for(i=1;i<=n-1;i++)
{
q=p->next;//q指向p的下一个任务
if(p->time>q->time)
{
temp=p->number;//交换number
p->number=q->number;
q->number=temp;
temp=p->time;//交换time
p->time=q->time;
q->time=temp;
temp=p->tijiao;//交换tijiao
p->tijiao=q->tijiao;
q->tijiao=temp;
temp=p->wait;//交换wait
p->wait=q->wait;
q->wait=temp;
}
if(p->next->next==NULL)break;//当p指向倒数第二的结点时,退出
p=p->next;
}//endfor循环
}
//任务不同时提交,按平均等待时间最短执行时的操作过程
cout<<"第"<e.number=nn;//第一个任务输入
cout<<"所需cpu时间:
";
cin>>e.time;
e.tijiao=0;
e.wait=0;
enqueue(l,e);//把新的任务入队
while
(1)//时间流逝,ss是流逝的时间
{
pp=l.front->next;//pp是第一个任务
pp->time=(pp->time)-1;//头指针所需时间减一秒
++ss;//时间加一秒
qq=pp;
nm=queuelength(l);//任务数量nm
for(inti=1;i<=nm-1;i++)//为头结点后的每一个结点wait加一
{
qq=qq->next;
qq->wait++;
}
if(pp->time==0)//如果第一个结点所需时间为0,则删除,并入另一个队列k
{
dequeue(l,ee);
enqueue(k,ee);
}
nm=queuelength(l);
if(nm==0)break;//当l队列为空时,退出
cout<"<cout<<"是否有新任务?
1.是0.否"<cin>>j;
if(j)//输入新的任务
{
++nn;//任务号
e.number=nn;
cout<<"第"<cout<<"所需cpu时间:
";
cin>>e.time;
e.tijiao=ss;
e.wait=0;
enqueue(l,e);//把新的任务入队
}
sort(l);//插入新任务后,把time最小的任务放在队首
}//while,跳出while后,输出k队列相应信息等到平均等待时间
//输出k队列的信息
node*qwe;
nn=queuelength(k);//队列k的长度
qwe=k.front->next;//pp是k的第一个结点
for(inti=1;i<=nn;i++)//依次输出队列k信息
{
cout<<"任务"<number<<"的提交时间是:
"<tijiao<<"等待时间是:
"<wait<<"开始执行时间是:
"<tijiao+qwe->wait<s=s+qwe->wait;
if(qwe->next==NULL)break;
qwe=qwe->next;
}
cout<<"平均等待时间是:
"<<1.0*s/nn<6.运行、测试与分析
1)运行程序,如图
2)任务同时提交
3)任务不同时提交
4)容错检验
实验收获及思考
遇到的问题:
1.无法编译。
2.输出结果不正确。
3.容错机制不正确。
解决办法:
1.通过错误提示,找出了错误所在,一般是因为部分变量拼写错误造成的。
2.在表达式求值问题中,重新梳理算法,检查程序执行过程,按照程序在纸上一步一步模拟运算过程,发现错误之处并且改正。
在cpu调度问题中,通过程序分块检查、测试,发现是排序函数出错,通过纸上的一步一步运算,改正了其中的错误。
最后输出正确结果。
3.在每个getchar处进行判断,进行多重限制,比如,只允许输入“+、-、
*、/、%、(、)”和“1、2、3、4、5、6、7、8、9”。
实验的收获:
第一次编写这么庞大的程序,编写过程中虽然有很多错误,但通过花费大量的时间,不断改正错误,提高了编写、调试较大程序的能力,同时体会到了自己编写的头文件为写程序提供的方便。
通过编写不同表达式求值的程序,更熟悉其运算过程,通过测试不同调度方式也切实体会到了好算法的重要性,同时也更熟悉栈和队列的操作。
思考:
最短作业优先,存在“长任务饥饿”的问题,即如果动态地不断加入作业,只要提交作业所需要的CPU服务时间比较短,则先提交的长任务将一直得不到服务,如何解决该问题?
答:
可以在每个任务结点中加一个int型变量shu,用来记录此任务到达cpu后等待的时间,当该任务不执行时,每过1秒就对该任务的shu加1,当该任务执行时,每过1秒对该任务的shu减1,再设定当有任务的shu大于某一值v时,立刻执行此任务,直到小于另一设定值u为止,再检查是否有shu超过特定值v,如有,执行此任务,如没有,按最短作业优先执行。
这样就可以解决“长任务饥饿”问题。
附录:
一、表达式求值
//stack.h表达式求值时用到的栈操作和数据结构
#include
#include
#include
#include
usingnamespacestd;
#definestack_init_size100
#definestackincrement10
typedefintstatus;
typedefstruct//数字栈
{
int*base;
int*top;
intnumstacksize;
}numstack;
typedefstruct//字符栈
{
char*base;
char*top;
intcharstacksize;
}charstack;
//********************************************************************
//栈的操作
//构建一个数字空栈
statusinitstack(numstack&s)
{
s.base=(int*)malloc(stack_init_size*sizeof(int));
if(!
s.base)exit(-2);
s.top=s.base;
s.numstacksize=stack_init_size;
cout<return1;
}
//构建一个字符空栈
statusinitstack(charstack&s)
{
s.base=(char*)malloc(stack_init_size*sizeof(char));
if(!
s.base)exit(-2);
s.top=s.base;
s.charstacksize=stack_init_size;
cout<<"准备完毕,请输入表达式:
"<return1;
}
//用e返回数字栈顶的元素
intgettop(numstacks)
{inte;
if(s.top==s.base)return0;
e=*(s.top-1);
returne;
}
//用e返回字符栈顶的元素
chargettop(charstack