C++Prime Plus队列与ATM模拟

66.队列(1)—存储实现

队列:一种特殊的线性表,插入在表尾,删除在表头。
fig1
队列类的特征
fig2
假设我们有一个Queue类的声明:
fig3
对于Queue的存储设计,可以基于数组,也可以基于链表:
fig4
应当补充以下声明:
fig5
链表的节点Node有两个元素:一个是Item,一个是指向Node结构类型的指针;
Queue类里面还需要声明链表的尾部指针rear和头部指针front。

这样定义符合队列的行为:从队头front删除元素,从队尾rear加入元素;
1.front指针移动到当前front指向的下一个Node;
2.rear从指向NULL改变成指向一个新的Node,并且让该Node的指针指向NULL,作为新的rear指针。

注意成员int items是队列的当前元素个数,这便于直接查询修改队列的长度,免得我们遍历,属于用空间换时间的做法(开辟额外变量省去遍历队列的时间)

67.队列(2) —成员函数的实现

队列类的声明如下:

#pragma once
//queue.h -- Queue class interface
//version 00

#include <iostream>

using namespace std;

class Item {
    
    
	//Item根据我们的应用进行自定义
};

class Queue
{
    
    
private:
	enum {
    
    Q_SIZE=10}; //57.类作用域常量: 用枚举型常量定义
	struct Node {
    
     Item item; Node* next; };
	Node* front;
	Node* rear;
	int items;
	const int qsize;
public:
	Queue(int qs = Q_SIZE);
	~Queue();
	bool isempty() const;
	bool isfull() const;
	int queuecount() const;
	bool enqueue(const Item& item);
	bool dequeue(Item& item);
};

对于构造函数,不能写成左边形式,要写成右边形式(用构造函数的初始化列表):
fig6
因为qsize是常量成员,常量只能初始化,不能赋值;


构造函数初始化列表:
比如:
fig7

定义构造函数时并没有在函数体中对成员变量一一赋值,其函数体为空(当然也可以有其他语句),而是在函数首部与函数体之间添加了一个冒号:,后面紧跟m_name(name), m_age(age), m_score(score)语句,这个语句的意思相当于函数体内部的m_name = name; m_age = age; m_score = score;语句,也是赋值的意思。

构造函数初始化列表还有一个很重要的作用,就是初始化 const 成员变量。初始化 const 成员变量的唯一方法就是使用初始化列表


队列的实现如下:

扫描二维码关注公众号,回复: 14263905 查看本文章
//queue.cpp -- implementing the Queue class
//version 00

#include "queue.h"

Queue::Queue(int qs):qsize(qs)
{
    
    
	front = rear = NULL;
	items = 0;
}

//入队:从队尾rear加入元素
bool Queue::enqueue(const Item& item)
{
    
    
	//如果队列已满,则不能入队
	if (isfull())
		return false;
	Node* add = new Node; //创建一个node

	add->item = item;
	//add会作为rear(rear=add),所以其next=NULL
	add->next = NULL;
	items++;

	//队列为空时,加入node后,front和rear都指向该node:front = add和rear = add;
	if (front == NULL)
		front = add;
	//队列已经有元素,原始rear指向add,再将add赋值到rear
	else
		rear->next = add;
	rear = add;
	return true;
}

//出队:从队头front删除元素
bool Queue::dequeue(Item& item)
{
    
    
	//如果队列为空,则不能出队
	if (front == NULL)
		return false;
	//将原始front的item保存到形参item
	item = front->item;
	items--;

	//创建temp并指向front所指的node
	Node* temp = front;
	//front更新指向下一个node
	front = front->next;
	//销毁原始front所指的内存
	delete temp;
	//如果此时队列为空,更新rear也指向NULL
	if (items == 0)
		rear = NULL;
	return true;
}

bool Queue::isempty() const {
    
     return items == 0; }

bool Queue::isfull() const {
    
     return items == qsize; }

int Queue::queuecount() const {
    
     return items; }

Queue::~Queue()
{
    
    
	Node* tmp;
	//依次顺着next所指node销毁内存
	while (front != NULL)
	{
    
    
		tmp = front;
		front = front->next;
		delete tmp;
	}
}

68.队列(3) —ATM排队模拟

问题描述
模拟ATM机的活动,统计在某段时间中的数据:

  • 获得服务的客户数目;
  • 被拒绝的客户数目;
  • 排队等候的累积时间;
  • 累积的队列长度;

ATM机运行的信息:

  • 队列的最大长度
  • 程序模拟的持续时间(单位为小时)
  • 平均每小时的客户数

设计过程
模拟过程:模拟每一分钟发生的事情

  • 判断是否来了新的客户,如果来了,并且此时队列未满,则将它添加到队列中,否则拒绝客户入队;
  • 如果没有客户在进行交易,则选取队列的第一个客户。确定该客户的已等候时间,并将wait_time计数器设置为新客户所需的处理时间;
  • 如果客户正在处理中,则将该客户剩余处理时间wait_time计数器减1;
  • 记录其他数据;

问题:

  • 如何表示排队:队列类对象;
  • 每个客户的信息:到达时间,服务时间;
  • 客户的服务时间有多长:随机数;
  • 如何获知某一时刻有新客户到达:随机数;

客户类型定义(即Item类):

#pragma once
//queue.h -- Queue class interface
//version 00

#include <iostream>

using namespace std;

typedef class Customer
{
    
    
private:
	long arrive; //顾客的到达时间
	int processtime; //顾客的处理时间
public:
	Customer():arrive(0),processtime(0){
    
    }//构造函数的初始化列表
	void set(long when);
	long when() const {
    
     return arrive; }
	int ptime() const {
    
     return processtime; }
}Item;

class Queue
{
    
    
private:
	enum {
    
    Q_SIZE=10}; //57.类作用域常量: 用枚举型常量定义
	struct Node {
    
     Item item; Node* next; };
	Node* front;
	Node* rear;
	int items;
	const int qsize;
public:
	Queue(int qs = Q_SIZE);
	~Queue();
	bool isempty() const;
	bool isfull() const;
	int queuecount() const;
	bool enqueue(const Item& item);
	bool dequeue(Item& item);
};

注意到:typedef class Customer,关键字typedef为自定义数据类型定义简洁的类型名称;

typedef struct tagPoint
{
    
    
    double x;
    double y;
    double z;
} Point;

等价于:

struct tagPoint
{
    
    
    double x;
    double y;
    double z;
} ;

//使用 typedef 为这个新的结构起了一个别名,叫 Point
typedef struct tagPoint Point

关于随机数,我们可以使用随机数函数rand(),在<cstdlib>中,每次调用会产生一个0RAND_MAX(定义于头文件 <cstdlib>)之间的整数,而且每个整数出现的概率是相等的。

程序第一次调用rand()之前需要对rand()初始化:std::srand(std::time(0))

如果客户的服务时间processtime在1-3分钟之间均匀分布,则有:

void Customer::set(long when)
{
    
    
	arrive = when;
	processtime = std::rand() % 3 + 1;
}

模拟某一分钟是否有顾客到达,假设平均每 min_per_cust 来一个顾客,则有以下bool类型表达式:

std::rand()*min_per_cust/RAND_MAX<1

ATM模拟的具体实现如下:

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "queue.h" //包含类的头文件

using namespace std;

const int MIN_PER_HR = 60;

bool newcustomer(double x)
{
    
    
    //假设平均每x分钟来一个顾客,下面表达式用于模拟每分钟有没有顾客到达
    return (rand() * x / RAND_MAX < 1);
}

int main()
{
    
    
    srand(time(0));
    cout << "Case Study: Bank of Heather Automatic Teller\n";
    cout << "Enter maximum size of queue:";
    int qs;
    cin >> qs;
    Queue line(qs);

    cout << "Enter the number of simulation hours:";
    int hours;
    cin >> hours;
    long cyclelimit = MIN_PER_HR * hours; //模拟的分钟数

    cout << "Enter the average number of customers per hour:";
    double perhour; //每小时到达的顾客平均数
    cin >> perhour;
    double min_per_cust; //每min_per_cust到达一个顾客
    min_per_cust = MIN_PER_HR / perhour;

    Item temp; //新顾客data
    long turnaways = 0; //离开的顾客数
    long customers = 0; //加入排队的人数
    long served = 0; //加入排队并且得到服务的人数
    long sum_line = 0; //整个过程下,每分钟求和一次队列长度
    int wait_time = 0; //当前正在服务的人还需要服务多少分钟结束
    long line_wait = 0; //得到服务的顾客的等待时间之和(等待时间:从到达直到服务结束)

    //模拟
    for (int cycle = 0; cycle < cyclelimit; cycle++)
    {
    
    
        if (newcustomer(min_per_cust)) //模拟每分钟是否有顾客到达
        {
    
    
            if (line.isfull()) //如果队列已满
                turnaways++; //离开的顾客数+1
            else //如果队列还没有满
            {
    
    
                customers++; //加入排队的人数+1
                temp.set(cycle); //cycle即当前顾客的到达时间,set同时生成随机的处理时间processtime
                line.enqueue(temp); //当前顾客入队
            }
        }
        if (wait_time <= 0 && !line.isempty()) //当前顾客服务完并且队列还有顾客
        {
    
    
            line.dequeue(temp); //当前顾客出队
            wait_time = temp.ptime(); //返回当前顾客的处理时间processtime,作为下一个被服务顾客的等待时间
            line_wait += cycle - temp.when(); //统计当前结束服务的顾客从到达直到服务结束的时间,并与前面已经服务过的顾客等待时间求和
            served++; //已服务过的人数+1
        }
        if (wait_time > 0)
            wait_time--; //模拟每分钟的服务过程
        sum_line += line.queuecount(); //每分钟求和一次队列长度
    }

    if (customers > 0)
    {
    
    
        cout << "customers accepted:" << customers << endl;
        cout << "customers served:" << served << endl;
        cout << "turnaways:" << turnaways << endl;
        cout << "average queue size:";

        cout.precision(2);
        cout.setf(ios_base::fixed, ios_base::floatfield);

        cout << (double)sum_line / cyclelimit << endl; //每分钟的平均队列长度
        cout << "average wait time:" << (double)line_wait / served << " minutes\n"; //队列内顾客的平均等待时间
    }
    else
        cout << "No customers!" << endl;
    cout << "Done!" << endl;

    return 0;
}

我们输入信息后得到模拟的统计结果:
fig7

猜你喜欢

转载自blog.csdn.net/qq_40943760/article/details/125032076