共计 5041 个字符,预计需要花费 13 分钟才能阅读完成。
背景:Heather 银行打算在 Food Heap 超市开设一个 ATM。Food Heap 超市的管理者放心排队期待应用 ATM 的人流会烦扰超市的交通,心愿限度排队期待的人数。Heather 银行心愿对顾客排队等待时间进行估测。
工作:自行设计一个队列的类和顾客的类,顾客达到的工夫随机,并且编写一个程序来模仿顾客和队列的交互。
思路:首先设计队列类,队列是一个尾进头出的构造,若采纳数组来示意队列,删除队列头部元素,须要将整体的元素往前挪动一格,操作较为繁琐,应用链表的数据结构来示意队列,删除队列首元素时,只需针对单个元素,减少队列元素时,只须要扭转地址。链表与数组相比,链表查找元素较为麻烦,增减元素较为不便。
队列类的设计
1、首先设计类的私有接口
- 队列所能包容的项目数应该要有限度,因而应该在类中设计一个 const 属性来存储队列所能存储的最大数目。
- 队列可能创立空队列,所以在队列类的构造函数中该当设置初始化属性的参数
- 可能查看队列是否为满,所以应该设置一个 bool 函数,还想到要有一个类中现有的 items 的属性,通过这个 items 的大小与最大数目来进行比拟从而判断队列是否为满
- 查看队列是否为空,与队列是否为满相相似。
- 须要一个函数成员可能返回以后队列中的数目
- (重点)须要一个插入队列的成员函数和删除队列成员的成员函数,波及到链表的删除和增加,因而在类中应该各设置一个指向头部和指向尾部的指针。
class Quene
{
public:
Quene(int qs);
~Quene();
bool isempty() const;
bool isfull() const;
int countQuene() const;
bool enquene(const Item& item);
bool dequene(Item item);
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++)
bool Quene::enquene(const Item& item)
{if (isfull())
return false;
Node * add = new Node;
add->item = item;
add->next = NULL;
if (front == NULL)
front = add;
else
rear->next = add;
rear = add;
items++
return true;
}
队列的删除办法实现(重点)
思路与减少结点的办法统一
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 上的示例,第一次看的时候感觉很迷糊,所以当初将它记录在思否上。