离散事件模型通常需要用到队列和线性表。
典型的例子是银行业务的模拟。
本文参考的是严蔚敏的《数据结构》。
过程如下:用四个队列表示银行的四个窗口,用一个有序链表存储到达事件和离开事件。在初始化函数里面先初始化四个队列和一个链表,并且产生一个到达事件,插在有序链表中。

遍历有序链表,取出头结点,当头结点是到达事件时,随机产生客户的停留时间以及下一客户的到达时间,将下一客户的到达事件插入到有序链表中,将此客户元素插入到最短的队列中,如果队列长度是1,那么为此客户设置一个离开事件。当头结点是离开事件时,删除此队列的头结点,如果此队列不为空,那么给此队列的下一个节点设置离开事件。直到银行工作时间结束。

不过,如果一个客户选择最短队列之后是不能换到其他队列的。

此程序使用vs2015编写的。

#include "stdafx.h"
#include <windows.h>
#include "stdlib.h"

#define Qu 4    // 客户队列数 
#define v110 5  // 两相邻到达的客户的时间间隔最大值
#define v120 30 // 每个客户办理业务的时间最大值 

typedef struct 
{
    int OccurTime;  // 事件发生时刻 
    int EventType;  // 事件类型,Qu表示到达事件,0至Qu-1表示Qu个窗口的离开事件 
}Event, ElemType;   //事件类型,有序链表LinkList的数据元素类型 

typedef struct
{
    int ArrivalTime; // 到达时刻
    int Duration;    // 办理事务所需时间 
}QElemType;          // 定义QElemType(队列的数据元素类型)为结构体类型;  


typedef struct QNODE
{
    QElemType data;
    struct QNODE *next;
}QNODE, *PQNODE;

typedef struct
{
    PQNODE Front, Rear; // 队头、队尾指针  
}LinkQueue;


typedef struct Node // 结点 
{
    ElemType Data;
    struct Node* Flink;
}NODE, *PNODE;


typedef struct LinkList // 链表类型  
{
    PNODE Head, Tail;  // 分别指向线性链表中的头结点和最后一个结点  
    int Len;           // 指示线性链表中数据元素的个数  
}LinkList;


void Bank_Simulation();
void OpenForDay();
BOOL InitList(LinkList *L);
BOOL OrderInsert(LinkList *L, ElemType e, int(*comp)(ElemType, ElemType));
int Cmp(Event a, Event b);
BOOL ListEmpty(LinkList L);
BOOL DeleteFirst(LinkList *L, PNODE h, PNODE *q);
PNODE GetListHead(LinkList L);
ElemType GetCurElem(PNODE p);
VOID CustomerArrived();
VOID CustomerDeparture();
int Minimum(LinkQueue Q[]);
BOOL DeQueue(LinkQueue *Q, QElemType *e);
int QueueLength(LinkQueue Q);
BOOL EnQueue(LinkQueue *Q, QElemType e);
BOOL QueueEmpty(LinkQueue Q);
BOOL InitQueue(LinkQueue *Q);
BOOL GetHead_Q(LinkQueue Q, QElemType *e);
void Random(int *d, int *i);

int g_CloseTime;        //银行营业时间,单位是分
LinkList g_EventList;   // 事件表 
Event g_event,g_et;
LinkQueue g_queue[Qu];  // Qu个客户队列
QElemType customer;     // 客户记录  
int TotalTime = 0, CustomerNum = 0; // 累计客户逗留时间,客户数(初值为0) 

int main()
{
    printf("请输入银行营业时间长度(单位:分)\n");
    scanf("%d", &g_CloseTime);
    Bank_Simulation();

    return 0;
}

void Bank_Simulation()
{
    PNODE p;
    OpenForDay(); // 初始化  
    while (!ListEmpty(g_EventList))
    {
        DeleteFirst(&g_EventList, GetListHead(g_EventList), &p);
        g_event.OccurTime = GetCurElem(p).OccurTime;
        g_event.EventType = GetCurElem(p).EventType;
        if (g_event.EventType == Qu)
            CustomerArrived();   // 处理客户到达事件  
        else
            CustomerDeparture(); // 处理客户离开事件  
    } // 计算并输出平均逗留时间  
    printf("顾客总数:%d, 所有顾客共耗时:%d分钟, 平均每人耗时: %d分钟\n", CustomerNum, TotalTime, TotalTime / CustomerNum);
}


void OpenForDay()
{ // 初始化操作  
    int i;
    InitList(&g_EventList); // 初始化事件链表为空  
    g_event.OccurTime = 0; // 设定第一个客户到达事件  
    g_event.EventType = Qu; // 到达  
    OrderInsert(&g_EventList, g_event, Cmp); // 插入事件表  
    for (i = 0; i<Qu; ++i) // 置空队列  
        InitQueue(&g_queue[i]);
}


BOOL InitQueue(LinkQueue *Q)
{ 
    // 构造一个空队列Q  
    (*Q).Front = (*Q).Rear = (PQNODE)malloc(sizeof(QNODE));
    if (!(*Q).Front)
    {
        return FALSE;
    }
    (*Q).Front->next = NULL;
    return TRUE;
}




BOOL InitList(LinkList *L)
{
    // 构造一个空的线性链表  
    PNODE p;
    p = (PNODE)malloc(sizeof(Node)); // 生成头结点  
    if (p!=NULL)
    {
        p->Flink = NULL;
        (*L).Head = (*L).Tail = p;
        (*L).Len = 0;
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}



BOOL OrderInsert(LinkList *L, ElemType e, int(*comp)(ElemType, ElemType))
{ // 已知L为有序线性链表,将元素e按非降序插入在L中。(用于一元多项式)  
    PNODE v1, v2, v3;
    v3 = (*L).Head;
    v2 = v3->Flink;
    while (v2 != NULL&&comp(v2->Data, e)<0) // v2不是表尾且元素值小于e  
    {
        v3 = v2;
        v2 = v2->Flink;
    }
    v1 = (PNODE)malloc(sizeof(Node)); // 生成结点  
    v1->Data = e;   // 赋值  
    v3->Flink = v1; // 插入  
    v1->Flink = v2;
    (*L).Len++;     // 表长加1  
    if (!v2)        // 插在表尾  
        (*L).Tail = v1; // 修改尾结点  
    return TRUE;
}

int Cmp(Event a, Event b)
{ // 依事件a的发生时刻<、=或>事件b的发生时刻分别返回-1、0或1  
    if (a.OccurTime == b.OccurTime)
        return 0;
    else
        return (a.OccurTime - b.OccurTime) / abs(a.OccurTime - b.OccurTime);
        //abs函数是求绝对值
}


BOOL ListEmpty(LinkList L)
{ // 若线性链表L为空表,则返回TRUE,否则返回FALSE  
    if (L.Len)
        return FALSE;
    else
        return TRUE;
}


BOOL DeleteFirst(LinkList *L, PNODE h, PNODE *q) 
{ // h指向L的一个结点,把h当做头结点,删除链表中的第一个结点并以q返回。  
  // 若链表为空(h指向尾结点),q=NULL,返回FALSE  
    *q = h->Flink;
    if (*q) // 链表非空  
    {
        h->Flink = (*q)->Flink;
        if (!h->Flink)      // 删除尾结点  
            (*L).Tail = h;  // 修改尾指针  
        (*L).Len--;
        return TRUE;
    }
    else
        return FALSE; // 链表空  
}


PNODE GetListHead(LinkList L)
{  
    return L.Head;
}


ElemType GetCurElem(PNODE p)
{ // 已知p指向线性链表中的一个结点,返回p所指结点中数据元素的值  
    return p->Data;
}



VOID CustomerArrived()
{ // 处理客户到达事件 
  // 生成该客户的停留时间以及下一客户的到达时刻,将下一客户到达事件插入事件表中,
  // 并将此客户插入最短队列中,如果此队列的长度是1,那么设定一个离开事件并插入事件表中。
    QElemType f;
    int durtime, intertime, i;
    ++CustomerNum;
    Random(&durtime, &intertime); // 生成随机数  
    g_et.OccurTime = g_event.OccurTime + intertime; // 下一客户到达时刻  
    g_et.EventType = Qu; // 队列中只有一个客户到达事件  
    if (g_et.OccurTime<g_CloseTime) // 银行尚未关门,插入事件表  
        OrderInsert(&g_EventList, g_et, Cmp);
    i = Minimum(g_queue); // 求长度最短队列的序号,等长为最小的序号  
    f.ArrivalTime = g_event.OccurTime;
    f.Duration = durtime;
    EnQueue(&g_queue[i], f);
    if (QueueLength(g_queue[i]) == 1)
    {
        g_et.OccurTime = g_event.OccurTime + durtime;
        g_et.EventType = i;
        OrderInsert(&g_EventList, g_et, Cmp); // 设定第i队列的一个离开事件并插入事件表  
    }
}

VOID CustomerDeparture()
{ // 处理客户离开事件 
  // 删除第i队列的排头客户,在第i队列不为空的情况下,设定第i队列的下一个离开事件
    int i;
    i = g_event.EventType;
    DeQueue(&g_queue[i], &customer); // 删除第i队列的排头客户  
    TotalTime += g_event.OccurTime - customer.ArrivalTime; // 累计客户逗留时间  
    if (!QueueEmpty(g_queue[i]))
    { // 设定第i队列的一个离开事件并插入事件表  
        GetHead_Q(g_queue[i], &customer);
        g_et.OccurTime = g_event.OccurTime + customer.Duration;
        g_et.EventType= i;
        OrderInsert(&g_EventList, g_et, Cmp);
    }
}


int Minimum(LinkQueue Q[]) // 返回最短队列的序号  
{
    int l[Qu];
    int i, k;
    for (i = 0; i<Qu; i++)
        l[i] = QueueLength(Q[i]);
    k = 0;
    for (i = 1; i<Qu; i++)
        if (l[i]<l[0])
        {
            l[0] = l[i];
            k = i;
        }
    return k;
}



BOOL DeQueue(LinkQueue *Q, QElemType *e)
{ // 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR  
    PQNODE p;
    if ((*Q).Front == (*Q).Rear)
        return FALSE;
    p = (*Q).Front->next;
    *e = p->data;
    (*Q).Front->next = p->next;
    if ((*Q).Rear == p)
        (*Q).Rear = (*Q).Front;
    free(p);
    return TRUE;
}


BOOL EnQueue(LinkQueue *Q, QElemType e)
{ // 插入元素e为Q的新的队尾元素  
    PQNODE p = (PQNODE)malloc(sizeof(QNODE));
    if (!p) // 存储分配失败
    {
        return FALSE;
    }
    p->data = e;
    p->next = NULL;
    (*Q).Rear->next = p;
    (*Q).Rear = p;
    return TRUE;
}

int QueueLength(LinkQueue Q)
{ // 求队列的长度  
    int i = 0;
    PQNODE p;
    p = Q.Front;
    while (Q.Rear != p)
    {
        i++;
        p = p->next;
    }
    return i;
}

BOOL QueueEmpty(LinkQueue Q)
{ // 若Q为空队列,则返回TRUE,否则返回FALSE  
    if (Q.Front == Q.Rear)
        return TRUE;
    else
        return FALSE;
}


BOOL GetHead_Q(LinkQueue Q, QElemType *e) 
{ // 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR  
    PQNODE p;
    if (Q.Front == Q.Rear)
        return FALSE;
    p = Q.Front->next;
    *e = p->data;
    return TRUE;
}

void Random(int *d, int *i)
{
    *d = rand() % v120 + 1; // 1到v120之间的随机数  
    *i = rand() % v110 + 1; // 1到v110之间的随机数  
}

java 离散事件模拟 离散事件例子_结点