背景:Heather银行打算在Food Heap超市开设一个ATM。Food Heap超市的管理者放心排队期待应用ATM的人流会烦扰超市的交通,心愿限度排队期待的人数。Heather银行心愿对顾客排队等待时间进行估测。
工作:自行设计一个队列的类和顾客的类,顾客达到的工夫随机,并且编写一个程序来模仿顾客和队列的交互。
思路:首先设计队列类,队列是一个尾进头出的构造,若采纳数组来示意队列,删除队列头部元素,须要将整体的元素往前挪动一格,操作较为繁琐,应用链表的数据结构来示意队列,删除队列首元素时,只需针对单个元素,减少队列元素时,只须要扭转地址。链表与数组相比,链表查找元素较为麻烦,增减元素较为不便。
队列类的设计
1、首先设计类的私有接口
- 队列所能包容的项目数应该要有限度,因而应该在类中设计一个const属性来存储队列所能存储的最大数目。
- 队列可能创立空队列,所以在队列类的构造函数中该当设置初始化属性的参数
- 可能查看队列是否为满,所以应该设置一个bool函数,还想到要有一个类中现有的items的属性,通过这个items的大小与最大数目来进行比拟从而判断队列是否为满
- 查看队列是否为空,与队列是否为满相相似。
- 须要一个函数成员可能返回以后队列中的数目
- (重点)须要一个插入队列的成员函数和删除队列成员的成员函数,波及到链表的删除和增加,因而在类中应该各设置一个指向头部和指向尾部的指针。
2、类的公有局部申明
除上述提到的公有局部以外,针对链表数据结构,应该设计一个结点构造,结点构造蕴含数值和指向下一个结点的地址。
struct Node{ Item item; Node* next;}class Quene{public: Node* first; Node* rear; int items; const int Maxitems;
上述代码引出一个问题:是应该在类中申明构造体,还是在全局中进行申明。全局中进行申明的话,作用域为整个文件,若其余头文件也申明了这个结点定义,当一起蕴含时,容易呈现抵触。若在类中申明构造,则其作用域为类,则不必放心抵触问题。若申明在公有局部,则只能类内进行应用,若在私有局部申明,要在类外应用,只需Quene::Node。
最初队列的类私有成员和公有成员申明:
class Quene{private: struct Node { Item item; Node* next; } Node* first; Node* rear; int items; const int Maxitems;public: Quene(int qs); ~Quene(); bool isempty() const; bool isfull() const; int countQuene() const; bool enquene(const Item& item); bool dequene(Item item);
3、类的实现
Quene::Quene(int qs):Maxitems(qs){ first=NULL; rear=NULL; items=0;}
类的有参构造函数须要初始化类中的公有属性,在流程进入函数块中时,程序将会率先为属性分配内存,因而若在函数块中惯例为const常量赋值是不容许的,对于必须在过程进入函数块前,创立常量的同时,初始化常量,因而须要这段代码:Quene(int qs):Maxitems(qs),这种操作被称为成员初始化列表。初始化列表的操作不限于const量,对于个别属性也能够这样操作,然而有一个限度条件为,只能在构造函数中应用。
bool Quene::isempty(){ return (items == 0);}
bool Quene::isfull(){ return(items == Maxitems);}
int Quene::CountQuene(){ return items;}
上述三品种内办法实现较为简单,对于这样简略的类内办法实现,最好应用内联函数的表达形式,即在类内办法申明时,间接在前面加上定义,这样能够晋升整体的程序运行速度,然而会减少内存的应用,所以内联函数更实用于函数体实现代码较少的状况。
队列的插入方法实现(重点)
队列的插入与链表的减少结点的办法统一
graph TD A[查看队列是否满员] -->|曾经满员| C(返回false) A[查看队列是否满员] -->|未满员|B(新建一个结点) B --> D(设置结点的item与next值) D --> E(判断first是否为NULL) E --> |是| I(将first指针指向新结点) E --> |否| F(将上一个结点的next指向新建结点) I --> G F --> G(将rear指针指向这个结点) G --> H(items++)
队列的删除办法实现(重点)
思路与减少结点的办法统一
graph TD A[判断队列是否为空] -->|否| B(将first指向结点的地址赋值给新结点指针) A[判断队列是否为空] -->|是| G(返回false) B --> C{将第二个结点的地址赋给first} C --> D(delete掉刚刚指向的新结点,并将items--) D --> E(判断此时items是否为0) E -->|是| F(将rear指针指向NULL)
bool Quene::dequene(Item& item){ if (isempty()) return false; item = front->item; Node* temp = front; front = front->next; delete temp; items--; if (items == 0) rear = NULL; return true;}
析构函数的实现(重点)
尽管曾经有删除队列的成员函数,然而仍然无奈保障队列过期时,在堆区中所创立的内存全副开释,因而析构函数应该将所有结点一一删除。
Quene::~Quene(){ Node* temp; while (front != NULL) { temp = front; front = front->next; delete temp; }}
4、补充设计
思考该队列类是否能应用默认的复制函数操作,当应用默认的复制函数操作时,会将头指针和尾指针同时复制给新对象,因而在操作新对象时,也会扭转原对象的队列,更为严重的是,原对象的rear指针和first指针不会相应产生扭转,因而在不必须要重载复制函数和深拷贝=函数时,将这两个函数放在公有局部,依附编译器来防止产生谬误。
顾客类的设计
- 可能初始化顾客的属性
- 属性设置为入队工夫以及处理事务工夫
- 可能给顾客的属性赋值
- 类外可能取得顾客的达到工夫属性和解决工夫属性
解决工夫为1~3s,由零碎随机生成
1、顾客类的申明class Customer{private: long arrive; int processtime;public: Customer() { arrive = 0; processtime = 0; } void setTime(long when); long when() const{ return arrive; } int processTime() const{ return processtime; }};
2、顾客类的实现
void Customer::setTime(long when){ arrive = when; processtime = std::rand() % 3 + 1;}
ATM的模仿
本次模仿,程序容许输出三个数:队列的最大长度,程序模仿的工夫(小时),均匀每小时的顾客数,另外程序将应用循环的形式,每循环一次代表过来一分钟,就是相当于该程序的刷新率为1分钟一次,程序须要实现以下工作:
- 须要判断是否有新顾客来。
- 如果没有客户在交易,那么选取队列中的第一个客户,率先确定客户曾经等待的工夫,将wait_time计数器设置为新客户所须要的解决工夫。
- 如果有客户在交易,则将wait_time计数器减1。
- 须要记录取得服务的顾客数目、被回绝的客户数目、排队等待的累计工夫以及累计的队列长度等。
如何判断有新顾客
假如咱们输出的是均匀每小时的顾客数为10,也就是说每6分钟就会有一个顾客,然而为了使模仿更加的实在,咱们想要把这个工夫随机化,也就是说可能两个顾客的间隔时间为3分钟也有可能4分钟5分钟。代码如下
bool newCustomer(int x){ return (rand()*x/RAND_MAX<1);}
rand()为一个返回任意数值的函数,RAND_MAX为rand()所能取的最大值,该式子将会随机返回0~6中的任意一个数字,这个程序相对来说比拟蠢笨,然而可能模仿出新顾客随机呈现的状况。能够通过进步每循环一次,所经验的工夫来进步准确性。
当初编写执行程序代码
1、筹备工作阶段
srand(time(NULL));//随机数种子 cout << "Case Study:Bank of Heather Automatic Teller" << endl; cout << "Enter maximum size of quene: "; int qs;//输出队列所能存储的最大值 cin >> qs; Quene line(qs);//通过有参结构,将输出的最大值赋值给Maxitems cout << "Enter the number if simulation hours:"; int hours; cin >> hours;//输出本次模仿所须要的工夫 long cyclelimit = MIN_PR_HR * hours;//总分钟数 cout << "Enter the average number of customers per hour:"; double perhour; cin >> perhour;//输出均匀每小时的新顾客 double min_per_cust; min_per_cust = MIN_PR_HR / perhour;//min_per_cust为均匀多少分钟来一个客人 Item temp; long turnaways = 0;//因队伍人数满,而被拒绝的人数 long customers = 0;//进入队列的客户总人数 long served = 0;//参加交易的客户总人数 long sum_line = 0;//队伍长度的累加值 int wait_time = 0;//等待时间 long line_wait = 0;//队伍中等待时间的累加
2、循环局部代码
for (int cycle = 0; cycle < cyclelimit; cycle++) { if (newCustomer(min_per_cust))//判断是否有新顾客来 { if (line.isfull())//若队列满了,则给turnways++ turnaways++; else { customers++;//否则给customers++ temp.setTime(cycle);//将顾客达到的工夫赋值给属性arrive line.enquene(temp);//将temp顾客退出到队列中 } } if (wait_time <= 0 && !line.isempty())//判断柜台是否有人在交易 { line.dequene(temp);//选取队列头第一个客户 wait_time = temp.processTime();//将该客户的交易工夫赋给wait_time line_wait += cycle - temp.when();//求出累计的等待时间 served++;//将服务的总客户加1 } if (wait_time > 0)//若wait_time>0,则代表目前还有人在交易 wait_time--; sum_line += line.CountQuene();//将每个工夫里队伍里的人数累加
3、展现局部的代码
展现局部的代码行将各个量打印进去即可。
总结:这是一道在C++primer plus上的示例,第一次看的时候感觉很迷糊,所以当初将它记录在思否上。