栈和队列.docx

上传人:b****5 文档编号:5705656 上传时间:2022-12-31 格式:DOCX 页数:18 大小:23.73KB
下载 相关 举报
栈和队列.docx_第1页
第1页 / 共18页
栈和队列.docx_第2页
第2页 / 共18页
栈和队列.docx_第3页
第3页 / 共18页
栈和队列.docx_第4页
第4页 / 共18页
栈和队列.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

栈和队列.docx

《栈和队列.docx》由会员分享,可在线阅读,更多相关《栈和队列.docx(18页珍藏版)》请在冰豆网上搜索。

栈和队列.docx

栈和队列

数据结构学习(C++)之栈和队列

栈和队列是操作受限的线性表,似乎每本讲数据结构的数都是这么说的。

有些书按照这个思路给出了定义和实现;但是很遗憾,本文没有这样做,所以,有些书中的做法是重复建设,这或许可以用不是一个人写的这样的理由来开脱。

  

  

  顺序表示的栈和队列,必须预先分配空间,并且空间大小受限,使用起来限制比较多。

而且,由于限定存取位置,顺序表示的随机存取的优点就没有了,所以,链式结构应该是首选。

  

  栈的定义和实现

  #ifndefStack_H

  #defineStack_H

  #include"List.h"

  

  templateclassStack:

List//栈类定义

  {

   public:

  voidPush(Typevalue)

  {

   Insert(value);

  }

  

   TypePop()

   {

  Typep=*GetNext();

  RemoveAfter();

  returnp;

   }

  

   TypeGetTop()

   {

  return*GetNext();

   }

  

   List:

:

MakeEmpty;

   List:

:

IsEmpty;

  

  };

  

  #endif  队列的定义和实现

  #ifndefQueue_H

  #defineQueue_H

  #include"List.h"

  

  templateclassQueue:

List//队列定义

  {

   public:

  voidEnQueue(constType&value)

  {

   LastInsert(value);

  }

  

   TypeDeQueue()

   {

  Typep=*GetNext();

  RemoveAfter();

  IsEmpty();

  returnp;

   }

  

   TypeGetFront()

   {

  return*GetNext();

   }

  

   List:

:

MakeEmpty;

   List:

:

IsEmpty;

  

  };

  #endif  测试程序

  #ifndefStackTest_H

  #defineStackTest_H

  #include"Stack.h"

  

  voidStackTest_int()

  {

   cout<

   cout<

   Stacka;

   cout<<"将1~20入栈,然后再出栈"<

   for(inti=1;i<=20;i++)a.Push(i);

  while(!

a.IsEmpty())cout<

  cout<

  }

  #endif

  

  #ifndefQueueTest_H

  #defineQueueTest_H

  #include"Queue.h"

  

  voidQueueTest_int()

  {

   cout<

   cout<

   Queuea;

   cout<<"将1~20入队,然后再出队"<

   for(inti=1;i<=20;i++)a.EnQueue(i);

   while(!

a.IsEmpty())cout<

   cout<

  }

  #endif  没什么好说的,你可以清楚的看到,在单链表的基础上,栈和队列的实现是如此的简单。

更多内容请看数据结构数据结构教程数据结构相关文章专题,或栈应用

  

  栈的应用很广泛,栈的最大的用途是解决回溯问题,这也包含了消解递归;而当你用栈解决回溯问题成了习惯的时候,你就很少想到用递归了,比如迷宫求解。

另外,人的习惯也是先入为主的,比如树的遍历,从学的那天开始,就是递归算法,虽然书上也教了用栈实现的方法,但应用的时候,你首先想到的还是递归;当然了,假如语言本身不支持递归(如BASIC),那栈就是唯一的选择了——似乎现在的高级语言都是支持递归的。

  

  如下是表达式类的定义和实现,表达式可以是中缀表示也可以是后缀表示,用头节点数据域里的type区分,这里有一点说明的是,由于单链表的赋值函数,我原来写的时候没有复制头节点的内容,所以,要是在两个表达式之间赋值,头节点里存的信息就丢了。

你可以改写单链表的赋值函数来解决这个隐患,或者你根本不不在两个表达式之间赋值也行。

  #ifndefEXPression_H

  #defineExpression_H

  #include"List.h"

  #include"Stack.h"

  #defineINFIX0

  #definePOSTFIX1

  #defineOPND4

  #defineOPTR8

  

  templateclassExpNode

  {

   public:

  inttype;

  union{Typeopnd;charoptr;};

  ExpNode():

type(INFIX),optr('='){}

  ExpNode(Typeopnd):

type(OPND),opnd(opnd){}

  ExpNode(charoptr):

type(OPTR),optr(optr){}

  };

  

  templateclassExpression:

List>

  {

   public:

  voidInput()

  {

   MakeEmpty();Get()->type=INFIX;

   cout<

   Typeopnd;charoptr='';

   while(optr!

='=')

   {

  cin>>opnd;

  if(opnd!

=0)

  {

   ExpNodenewopnd(opnd);

   LastInsert(newopnd);

  }

  cin>>optr;

  ExpNodenewoptr(optr);

  LastInsert(newoptr);

   }

  }

  voidPrint()

  {

   First();

   cout<

   for(ExpNode*p=Next();p!

=NULL;p=Next())

   {

  switch(p->type)

  {

   caseOPND:

  cout<opnd;break;

   caseOPTR:

  cout<optr;break;

   default:

break;

  }

  cout<<'';

   }

   cout<

  }

  Expression&Postfix()//将中缀表达式转变为后缀表达式

  {

   First();

   if(Get()->type==POSTFIX)return*this;

   Stacks;s.Push('=');

   Expressiontemp;

   ExpNode*p=Next();

   while(p!

=NULL)

   {

  switch(p->type)

  {

   caseOPND:

   temp.LastInsert(*p);p=Next();break;

   caseOPTR:

   while(isp(s.GetTop())>icp(p->optr))

   {

  ExpNodenewoptr(s.Pop());

  temp.LastInsert(newoptr);

   }

   if(isp(s.GetTop())==icp(p->optr))

   {

  s.Pop();p=Next();break;

   }

   s.Push(p->optr);p=Next();break;

   default:

break;

  }

   }

   *this=temp;

   pGetFirst()->data.type=POSTFIX;

   return*this;

  }

  

  TypeCalculate()

  {

   Expressiontemp=*this;

   if(pGetFirst()->data.type!

=POSTFIX)temp.Postfix();

   Stacks;Typeleft,right;

   for(ExpNode*p=temp.Next();p!

=NULL;p=temp.Next())

   {

  switch(p->type)

  {

   caseOPND:

  s.Push(p->opnd);break;

   caseOPTR:

  right=s.Pop();left=s.Pop();

  switch(p->optr)

  {

   case'+':

s.Push(left+right);break;

   case'-':

s.Push(left-right);break;

   case'*':

s.Push(left*right);break;

   case'/':

if(right!

=0)s.Push(left/right);elsereturn0;break;

  //case'%':

if(right!

=0)s.Push(left%right);elsereturn0;break;

  //case'^':

s.Push(Power(left,right));break;

   default:

break;

  }

  default:

break;

   }

  }

  returns.Pop();

  }

  

  private:

   intisp(charoptr)

   {

  switch(optr)

  {

   case'=':

return0;

   case'(':

return1;

   case'^':

return7;

   case'*':

return5;

   case'/':

return5;

   case'%':

return5;

   case'+':

return3;

   case'-':

return3;

   case')':

return8;

   default:

return0;

  }

   }

  

   inticp(charoptr)

   {

  switch(optr)

  {

   case'=':

return0;

   case'(':

return8;

   case'^':

return6;

   case'*':

return4;

   case'/':

return4;

   case'%':

return4;

   case'+':

return2;

   case'-':

return2;

   case')':

return1;

   default:

return0;

  }

   }

  };

  

  #endif

    几点说明

  

  1、表达式用单链表储存,你可以看到这个链表中既有操作数又有操作符,假如你看过我的《如何在一个链表中链入不同类型的对象》,这里的方法也是对那篇文章的补充。

  

  2、输入表达式时,会将原来的内容清空,并且必须按照中缀表示输入。

假如你细看一下中缀表达式,你就会发现,除了括号,表达式的结构是“操作数”、“操作符”、“操作数”、……“操作符(=)”,为了统一这个规律,同时也为了使输入函数简单一点,规定括号必须这样输入“0(”、“)0”;这样一来,“0”就不能作为操作数出现在表达式中了。

因为我没有在输入函数中增加容错的语句,所以一旦输错了,那程序就“死”了。

  

  3、表达式求值的过程是,先变成后缀表示,然后用后缀表示求值。

因为原书讲解的是这两个算法,并且用这两个算法就能完成中缀表达式的求值,所以我就没写中缀表达式的直接求值算法。

具体算法说明参见原书,我就不废话了。

  

  4、Calculate()注释掉的两行,“%”是因为只对整型表达式合法,“^”的Power()函数没有完成。

  

  5、isp(),icp()的返回值,原书说的不细,我来多说两句。

‘=’(表达式开始和结束标志)的栈内栈外优先级都是最低。

‘(’栈外最高,栈内次最低。

‘)’栈外次最低,不进栈。

‘^’栈内次最高,栈外比栈内低。

‘×÷%’栈内比‘^’栈外低,栈外比栈内低。

‘+-’栈内比‘×’栈外低,栈外比栈内低。

这样,综合起来,就有9个优先级,于是就得出了书上的那个表。

更多内容请看数据结构数据结构教程数据结构相关文章专题,或队列应用

  

  我看的两本教科书(《数据结构(C语言版)》还有这本黄皮书)都是以这个讲解队列应用的,而且都是银行营业模拟(太没新意了)。

细比较,这两本书模拟的银行营业的方式还是不同的。

--framecontents-->

--/framecontents-->

1997版的《数据结构(C语言版)》的银行还是老式的营业模式(究竟是1997年的事了),现在的很多地方还是这种营业模式——几个窗口同时排队。

这种方式其实不太合理,经常会出现先来的还没有后来的先办理业务(经常前面一个人磨磨蹭蹭,别的队越来越短,让你恨不得把前面那人干掉)。

1999版的这本黄皮书的银行改成了一种挂牌的营业方式,每个来到的顾客发一个号码,假如哪个柜台空闲了,就叫号码最靠前的顾客来办理业务;假如同时几个柜台空闲,就按照一种法则来决定这几个柜台叫号的顺序(最简单的是按柜台号码顺序)。

这样,就能保证顾客按照先来后到的顺序接受服务——因为大家排在一个队里。

这样的营业模式我在北京的西直门工商银行见过,应该说这是比较合理的一种营业模式。

不过,在本文中最重要的是,这样的营业模式比较好模拟(一个队列总比N个队列好操作)。

  

  原书的这部分太难看了,我看的晕晕的,我也不知道按照原书的方法能不能做出来,因为我没看懂(旁白:

靠,你小子这样还来现眼)。

我按照实际情况模拟,实现如下:

  #ifndefSimulation_H

  #defineSimulation_H

  

  #include

  #include

  #include

  classTeller

  {

   public:

  inttotalCustomerCount;

  inttotalServiceTime;

  intfinishServiceTime;

  Teller():

totalCustomerCount(0),totalServiceTime(0),

  finishServiceTime(0){}

  };

  

  //#definePRINTPROCESS

  

  classSimulation

  {

   public:

  Simulation()

  {

   cout<

   cout<<"柜台数量:

";cin>>tellerNum;

   cout<<"营业时间:

";cin>>simuTime;

   cout<<"两个顾客来到的最小间隔时间:

";cin>>arrivalLow;

   cout<<"两个顾客来到的最大间隔时间:

";cin>>arrivalHigh;

   cout<<"柜台服务最短时间:

";cin>>serviceLow;

   cout<<"柜台服务最长时间:

";cin>>serviceHigh;

   arrivalRange=arrivalHigh-arrivalLow+1;

   serviceRange=serviceHigh-serviceLow+1;

   srand((unsigned)time(NULL));

  }

  Simulation(inttellerNum,intsimuTime,intarrivalLow,intarrivalHigh,intserviceLow,intserviceHigh)

  

  :

tellerNum(tellerNum),simuTime(simuTime),arrivalLow(arrivalLow),arrivalHigh(arrivalHigh),

  

  serviceLow(serviceLow),serviceHigh(serviceHigh),

  arrivalRange(arrivalHigh-arrivalLow+1),serviceRange(serviceHigh-serviceLow+1)

  {srand((unsigned)time(NULL));}

  

  voidInitialize()

  {

   curTime=nextTime=0;

   customerNum=customerTime=0;

   for(inti=1;i<=tellerNum;i++)

   {

  tellers[i].totalCustomerCount=0;

  tellers[i].totalServiceTime=0;

  tellers[i].finishServiceTime=0;

   }

   customer.MakeEmpty();

  }

  

  voidRun()

  {

   Initialize();

   NextArrived();

   #ifdefPRINTPROCESS

  

  cout<

  cout<<"tellerID";

  for(intk=1;k<=tellerNum;k++)cout<<"TELLER"<

  cout<

   #endif

  

   for(curTime=0;curTime<=simuTime;curTime++)

   {

  if(curTime>=nextTime)

  {

   CustomerArrived();

   NextArrived();

  }

  #ifdefPRINTPROCESS

   cout<<"Time:

"<

  #endif

  for(inti=1;i<=tellerNum;i++)

  {

   if(tellers[i].finishServiceTime

   if(tellers[i].finishServiceTime==curTime&&!

customer.IsEmpty())

   {

  intt=NextService();

  #ifdefPRINTPROCESS

   cout<<''<

  #endif

  CustomerDeparture();

  tellers[i].totalCustomerCount++;

  tellers[i].totalServiceTime+=t;

  tellers[i].finishServiceTime+=t;

   }

  

   #ifdefPRINTPROCESS

   elsecout<<"";

   #endif

  }

  #ifdefPRINTPROCESS

   cout<

  #endif

   }

   PrintResult();

  }

  

  voidPtintSimuPara()

  {

   cout<

   cout<<"柜台数量:

"<

"<

   cout<<"两个顾客来到的最小间隔时间:

"<

   cout<<"两个顾客来到的最大间隔时间:

"<

   cout<<"柜台服务最短时间:

"<

   cout<<"柜台服务最长时间:

"<

  }

  

  voidPrintResult()

  {

   inttSN=0;

   longtST=0;

   cout<

   cout<<"-------------模拟结果-------------------";

   cout<

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工作范文 > 其它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1