请查看原文:
http://www.ibaiyang.org/2012/11/20/queue-list/
在我读严蔚敏版的《数据结构》的时候,看到其中一个例子,让我对数据结构佩服的五体投地,让人把如此的一个问题分析的这么透彻,十分钦佩。也让我明白了一个道理,在设计好的算法之前,一定要设计好的数据结构,当你设计了好的数据结构之后,反而会为你写算法有很大的帮助,这是我深有体会的。
在这里,就将在重复一下这个例子吧,方便以后借鉴,这个例子主要是模拟离散事件的例子。
引言
在日常生活中,我们经常会遇到各种排队的事情,比如乘地铁,去食堂就餐。我们就以去银行办理业务为例,在我们走进银行之后,我们需要到窗口办理自己的业务。
现在需要编制一个软件,模拟银行的这种业务活动并计算一天客户中在银行逗留的平均时间。
在这里,需要满足以下条件:
- 进来的客户是随即的;
- 进来之后的客户选择较短的队列排队;
为了计算客户在一天当中的平均逗留时间,需要知道每个客户在银行的逗留时间,然后其每个客户逗留时间之和除以总人数既是平均逗留时间。在通常情况下,我们知道客户到达和离开银行的时间,自然知道每个客户在银行的逗留时间。所以,我们将客户达到和离开银行这二个时刻发生的事情称为“事件”,整个模拟程序是按照事件发生的先后顺序来进行处理,这样一种模拟程序称作“事件驱动模拟”。
实现
以下描述正是上述银行客户的离散事件驱动模拟程序:
void bank_simulation(int close_time)
{
open_for_day();
while(more_event){
event_drive(occure_time, event_type);
switch(event_type){
case "A": customer_arrived(); break;
case "D": customer_departure(); break;
default: invalid();
}
}
close_for_day();
}
以上算法主要处理对象是“事件”, 事件的主要信息是由事件类型和事件发生的时刻。算法中要处理二类事件:一类是客户达到事件;一类是客户离开事件。前一类事件发生的时刻随客户的打来而自然形成;后一类事件发生则由客户事务所需时间和等待所消耗的时间而定。由于程序驱动是按照事件发生时刻的先后顺序进行,则事件表应该是有序表,其主要操作是插入和删除事件。
模拟程序中的另一种数据结构是表示客户排队的队列,假设银行有4个窗口,则程序需要有4个队列,队列中有关客户的主要信息是客户到达的时刻和客户办理事务所需要的时间。每个队列中的对头客户即为正在窗口办理事务的客户,他办完事务离开队列的时刻就是即将发生客户离开事件的时刻,对每个队列头客户都存在一个将要驱动的客户离开事件。因此,在任何时刻即将发生的事件只有以下5种可能:
- 新的客户达到;
- 1号窗口客户离开;
- 2号窗口客户离开;
- 3号窗口客户离开;
- 4号窗口客户离开;
由以上分析,在这个模拟程序中只需要二种数据结构:有序表和队列。他们的数据元素类型分别定义如下:
typedef struct {
int occure_time;
int event_type;
} Event;
typedef vector< Event> event_list;
typedef struct {
int arrival_time;
int duration;
} QElemType;
给出了数据结构,结合之前的算法框架,就很容易实现程序了,以下给出伪代码
event_list ev; // 事件表
Event en; // 事件
LinkQueue q[5]; // 4个客户队列
QElemType customer; // 客户记录
int total_time, customer_nr = 0;
void open_for_day()
{
total_time = customer_nr = 0;
init_list(ev);
en.occure_time = 0; en.event_type = 0; // 设定第一个客户到达事件
order_insert(ev, en, cmp);
for(i = 1; i < 4; i++){
init_queue(q[i]);
}
}
void customer_arrived()
{
customer_nr ++;
random(durtime, intertime); // 产生随机数
t = en.occure_time + intertime; // 下一个客户到达事件
if( t < close_time ){
order_insert(ev, (t, 0), cmp);
}
i = minimum(q);
enqueue(q[i], (en.occure_time, durtime));
if( queue_len(q[i]) == 1){
order_insert(ev, (en.occure_time + durtime, i), cmp);
}
}
void customer_departure()
{
i = en.event_type; del_queue(q[i], customer); // 删除队列头的客户
total_time += en.occure_time - customer.arrival_time;
if( !queue_empty() ){ // 设定第i队列的一个离开事件并插入事件表
get_head(q[i], customer);
order_insert(ev, (en.occure_time + customer.duration, i)), cmp);
}
}
我觉得写到这里,读者应该仔细去体会“驱动”二字,请分别看以上函数,举customer_departure函数为例,当该i队列产生客户离开事件后,并产生一个新的离开事件,如果该i队列不为空的话,为何会产生这个离开事件呢,因为事件表中产生了一个事件,表明第i个队列的头客户已经离开,故会产生另一个客户排在队头,当然会产生一个离开事件了。
联想
在慢慢的品味这个算法时,让我联想到了多线程中,生产—消费模型,貌似也是一个事件驱动模型,不知道对不对,不过我认为是吧,可以知道生产和消费为其中的二个事件。给出伪代码。
void produce()
{
// 创建临界区域
lock();
while( v.len() == max_len ){
condition_wait();
}
v.push_back( t );
unlock();
if( v.len() == 1){
notify_all();
}
}
void consume()
{
lock();
while ( v.len() == 0 ){
condition_wait();
}
t = v.pop();
unlock();
if( v.len() == 0){
notify_all();
}
}
为什么我说这是事件驱动模型呢,因为我觉得,当生产出来了资源,即v不为空时,自然会唤醒consume线程去消费它,相反,当v为空时,又会唤醒produce生产资源,其中的唤醒类似驱动原理。
【注】如有理解偏差,实属能力问题,本人还在努力之中,共同进步。
-----------------打造高质量的文章 更多关注 把酒泯恩仇---------------
为了打造高质量的文章,请 推荐 一下吧。。。。谢谢了,请关注我后续的文章,会更精彩哦
请关注sina微博:http://weibo.com/baiyang26
把酒泯恩仇官方博客:http://www.ibaiyang.org 【推荐用google reader订阅】
把酒泯恩仇官方豆瓣:http://www.douban.com/people/baiyang26/
如果您想转载本博客,请注明出处
如果您对本文有意见或者建议,欢迎留言
分享到:
相关推荐
相比之下,链表实现的队列更加灵活,可以动态地增长和缩减,不受固定空间大小的限制。然而,链表实现可能会引入额外的内存开销。 ### 队列的应用 队列在现实世界中的应用无处不在,从简单的日常生活到复杂的计算机...
链表是计算机科学中一种非常...在实际工作中,链表的运用无处不在,无论是系统编程、数据库管理还是算法设计,都会频繁地用到链表。因此,深入理解并熟练掌握链表及其相关指针操作,对于任何IT从业者都是必要的技能。
8. **数据结构**:如栈、队列、链表、树(二叉树、平衡树如AVL和红黑树)、哈希表等,它们是实现算法的基础。 9. **概率算法和随机化算法**:在不确定性和概率环境中寻找解决方案,如Monte Carlo方法和Las Vegas...
Java中常见的数据结构有数组、链表、栈、队列、集合、映射(哈希表)、树(二叉树、平衡树如AVL和红黑树)和图等。数组是最基本的数据结构,而链表解决了数组插入删除的效率问题;栈和队列分别用于后进先出(LIFO)...
4. **高效的数据结构实现**:包括但不限于栈、队列、链表、树(如二叉搜索树)、散列表等。这些数据结构的实现不仅考虑到了JavaScript的特性,还考虑到了它们在实际应用场景中的效率。 5. **排序算法**:介绍了快速...
数据结构与算法是计算机科学领域的两大基石,它们几乎无处不在地影响着我们的日常生活和工作。尽管很多人可能会有这样的误解,认为数据结构和算法是高深且脱离实际工作的理论知识,只在面试或者特定情况下才会用到。...
1. **数据结构**:书中的数据结构部分涵盖了数组、链表、栈、队列、树、图、哈希表、堆和队列等多种类型。理解这些数据结构的特性和操作,能有效提高代码效率,优化问题解决方案。例如,栈和队列作为线性结构,是很...
在实际编程工作中,数据结构和算法的应用无处不在。例如,搜索引擎的索引构建依赖于高效的哈希表和倒排索引;推荐系统中的协同过滤算法基于用户行为数据进行分析;数据库管理系统中的查询优化则需要对各种查询算法有...
在实际编程中,数据结构和算法的应用无处不在。例如,数据库管理系统利用索引来提高查询效率,搜索引擎使用图算法进行网页排名,而推荐系统则可能依赖于协同过滤算法。因此,学习哈工大课程的PPT,将有助于你掌握...
在Java中,数据结构和算法的应用无处不在,从小型的业务逻辑处理到大型的系统设计,它们都起着基石的作用。 首先,我们来看数据结构。数据结构包括数组、链表、栈、队列、集合、映射、树和图等。数组是最基础的数据...
在实际应用中,我们还常常利用数据结构,如数组、链表、栈、队列、树、图等,来辅助算法设计。同时,算法的优化技巧,如**代码优化**、**并行计算**和**分布式计算**,也是提升算法性能的关键。 《算法设计与分析...
+ [块状链表](#块状链表) + [动态树](#动态树) + [左偏树](#左偏树) + [跳表](#跳表) + [SBT](#sbt) + [线段树](#线段树) + [单调队列](#单调队列) + [哈希表](#哈希表) + [Splay](#splay) * [图论](#图论...
熟练掌握单链表反转能够加深对链表这种基础数据结构的理解,并在解决更复杂的数据结构和算法问题时提供坚实的基础。因此,无论作为初学者还是资深开发者,深入理解单链表及其反转算法,都是不断进步的必经之路。
- **数据结构分类**:介绍了不同类型的数据结构,如数组、链表、栈、队列、树和图等,并讨论了它们的特点和适用场景。 ### 四、学习资源与支持 - **源代码仓库**: 本书的所有代码均托管在GitHub仓库([github....
在Java编程中,数据结构和算法的应用无处不在,从小型的业务逻辑处理到大型的系统设计,都需要依赖它们来提高程序的效率和性能。 首先,我们要了解一些基本的数据结构,如数组、链表、栈、队列、集合、映射(哈希表...
7. 数据结构:链表、栈、队列、堆、树和图等数据结构的应用也是常见题目,例如,如何有效地使用二叉搜索树进行查找和插入操作。 课后答案提供了参考思路和解决方案,可以帮助读者检查自己的理解是否准确,也可以...
3. **数据结构**:数据结构是算法的基石,包括数组、链表、栈、队列、树、图等。每种数据结构都有其特定的操作和用途,例如,二叉搜索树适合快速查找,哈希表提供近似恒定时间的查找。掌握数据结构能有效提升算法的...
《Java数据结构和算法(第二版)》是深入学习编程基础和优化问题解决能力的重要教材。本书涵盖了数据结构和算法的广泛主题,旨在帮助Java程序员理解如何有效地存储、组织和处理数据,以及如何通过高效的算法解决复杂...
链表作为基础数据结构,课程会深入讲解单向链表、双向链表和循环链表,并通过实际题目,如反转链表、合并两个有序链表和删除倒数第N个节点,让学员熟悉链表的操作。 哈希表是无处不在的数据结构,课程将讨论其Java...