队列基本操作及银行简易排号的实现

本文介绍队列的基本概念及其实现方式,重点讲解了环形队列的设计原理及其应用,通过示例展示了如何使用环形队列解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

队列是特殊的线性表,只允许在表的前端进行删除操作,在表的后端进行插入操作,进行插入操作的端叫队尾,进行删除操作的端成为队头,当队列中没有元素时,称为空队列。队列是一种先进先出(First In First Out)的结构,采用顺序存储和链式存储方式。


在队列用顺序存储方式保存时,先在内存中申请一片区域用来保存元素,如图:

顺序队列
A  BCD      
 head               tail

第一个元素A进入队列,因为是空队列,元素A排在队头,接着B,C,D进入队列,得到上图队列形式,head指向队头,指示队头元素出队操作,tail指向队尾,指示新增元素进入队列。

当执行出队操作时,元素A将出队,元素B将成为队头,head指向该元素,如图:

顺序队列
 BCD      
      head           tail

队列的基本操作包括:初始化队列,进队,出队,获取队列第一个元素,获取队列长度等。

下面用C语言操作相关函数

首先定义头文件 "SeqQueue.h"

#include<stdio.h>
#define QUEUEAX 15  //设置队列的长度
typedef struct
{
	DATA data[QUEUEMAX]; //队列数据,保存队列中的内容
	int head;
	int tail;
	 
}SeqQueue;
初始化队列:

//初始化队列
SeqQueue *SeqQueueInit()
{
	SeqQueue *q;
	if(q=(SeqQueue *)malloc(sizeof(SeqQueue)))   //申请内存
	{
		q->head=0;    //设置队长 
		q->tail=0;      //设置队尾 
		return q; 
	}else
	  return NULL;   	 
} 

初始化队列,首先先申请内存,申请成功后,设置队头队尾序号都为0,然后返回分配内存的首地址


释放队列:

void SeqQueueFree(SeqQueue *q)
{
	if(q!=NULL)
	    free(q);
}
使用free函数释放对该内存块的使用

获取队列状态:

在入队或出队时,应该先检查队列是状态(空/满)


/队列状态
 int SeqQueueIsEmpty(SeqQueue *q)
 {
 	return (q->head==q->tail);
 }
 
 int SeqQueueIsFull(SeqQueue *q)
 {
 	return (q->tail==QUEUEMAX);   //当队尾序号达到队列长度的最大值表示队列已满
	  
 }
 
 int SeqQueueLen(SeqQueue *q)
 {
 	return (q->tail-q->head);
 }

入队操作:

//入队操作
int SeqQueueIn(SeqQueue *q,DATA data)
{
	if(q->tail==QUEUEMAX)
	{
		printf("队列已满,不能插入");
		return 0; 
	}else{
		q->data(q->tail++)=data;   //修改队尾指针,使数据入队 
		return 1;
	} 
} 
</pre><pre name="code" class="cpp"><pre name="code" class="cpp">//出队操作
int SeqQueueOut(SeqQueue *q)
{
	if(q->head==q->tail);
	{
		printf("队列为空\n");
		return NULL;
	}else{
		return &(q->data[q->head++]);   //返回队头元素指针 
	}

出队就是从队列首部取出队头元素(返回队头元素的指针),然后修改队头head 的序号,使其指向后一个元素。

获取队头元素:


</pre><pre name="code" class="cpp">
<pre name="code" class="cpp">DATA *SeqQueuePeek(SeqQueue *q)
{
	if(SeqQueueIdEmpty(q))     
	{
		printf("\n队列为空");
		return NULL;
	}else{
		return &(q->data[q->head]);
	}
} 
获取队头元素只是把队头的元素取出来查看一下,并不用删除,所以不能修改队头指针。

以上是对队列的简单操作,在判断队列是否满的时候,我们是通过 q->tail==QUEUEMAX来实现的,即当指向tail指向队列末尾时就判断队列已满,不能再入队,看下面的情况:
队列的假溢出
       FGHI  G
                             head                      tail
这样tail指向队列的末尾,虽然队列前面还有5个空位置,但是会显示队列已满,不能入队,这种情况称为队列的假溢出

解决假溢出可以通过在每次出队操作后,使后面的元素往前移动,始终保持队列的第一个位置有元素,每次都从第一个位置进行出队操作,但是当数据量很大时,这种操作效率就比较低。

还有一种方法是构建环形队列,(以下对环形队列的理解转自:http://wenku.baidu.com/view/19ac16b1e009581b6ad9eb42.html




环形队列事实上是数组的线性空间实现的,当数据到了尾部,把它转回到0位置处理,这个转回是通过取模运算执行的,因此环形队列的是逻辑上将数组元素q[0]和q[MAXN-1]连接,形成一个存放队列的环形空间。

在环形队列中,主要需要计算队尾tail和队头head序号,按照取模运算,当队列为空时:q->head==q->tail;当队列为满时:(q->tail+1)%QUEUEMAX=q->head;

循环队列的操作函数: “CycQueue.h”

#define queuemax 15
typedef struct
{
	DATA data[queuemax];  //队列数组
	int head;
	int tail; 
} cycqueue;

cycqueue *cycqueueInit()

{
	cycqueue *q;
	if(q=(cycqueue *)malloc(sizeof(cycqueue)))  //申请内存
	{
		q->head=0;
		q->tail=0;  //设置队头队尾
		return q; 
	} 
	else
	   return NULL; 
}

void cycqueueFree(cycqueue *q)  //释放队列
{
	if(q!=NULL)
	free(q);
} 

int cycqueueIsempty(cycqueue *q)
{
	return (q->head==q->tail);   //队列为空 
}

int cycqueueIsfull(cycqueue *q)
{
	return ((q->tail+1)%queuemax==q->head);  //队列已满 
}

int cycqueueIn(cycqueue *q,DATA data)
{
	if((q->tail+1)%queuemax==q->head)
	{
		printf("队列已满,无法插入");
		return 0; 
	}else{
		q->tail=(q->tail+1)%queuemax;  //求队尾序号 
		q->data[q->tail]=data;  //将元素保存到队列序号为tail处
		return 1;
	}
}

DATA *cycqueueOut(cycqueue *q)
{
	if(q->head==q->tail)  //队列为空
	{
		printf("队列为空\n");
		return NULL;
	} else{
		q->head=(q->head+1)%queuemax;   //获取队首序号
		return &(q->data[q->head]);
	}
}
<pre name="code" class="cpp">DATA *cycqueuePeek(cycqueue *q)  //获取队列中的第一个位置的数据
{
	if(q->head==q->tail)
	{
		printf("队列为空\n");
		return NULL;
	}else{
		return &(q->data[(q->head+1)%queuemax]);
	}
} 

int cycqueueLen(cycqueue *q) //获取队列长度{int n;n=q->tail-q->head;if(n<0) n=queuemax+n;return n;}


实例:利用环形队列实现银行简易排号程序

问题描述:在银行里办理业务时,首先会通过排号机生成一个号码,然后等待工作人员呼叫,办理完业务后,离开,继续下一个。

这就是一个队列的操作,首先创建一个队列,每位顾客来到时将该序号添加到队列中(入队操作),工作人员处理该客户业务后,客户离开,从头部取出该客户序号(出队操作)
 
排号程序:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>

typedef struct
{
	int num;  //队列编号 
	long time;   //进入队列时间 
} DATA;
#include "cycqueue.h"
int num;   //顾客序号
定义类型DATA,表示进入队列的数据。调用上面编写的CycQueue.h函数使用循环队列实现本例操作。

然后编写新增顾客入队函数:
void add(cycqueue *q)  //新增顾客排序
{
	DATA data;
	if(!cycqueueIsfull(q))  //如果队列未满 
	{
		data.num=++num;  //保存顾客序号 
		data.time=time(NULL);  //保存排序时间 
	    cycqueueIn(q,data);   //调用入队函数 
	}else
	   printf("\n排队人数太多,请稍后排队\n");  //显示提示信息 	
} 
如果队列未满,就把队列的编号增加一位,并保存该顾客排序的时间,然后调用入队函数进队

然后是呼叫顾客去办理业务,这时把这位顾客从队列中删除,即出队操作,同时呼叫后还需要提示下一位客户做好准备:

void next(CycQueue *q) //通知下一顾客准备 
{
    DATA *data;
    if(!CycQueueIsEmpty(q)) //若队列不为空 
    {
        data=CycQueueOut(q); //取队列头部的数据 
        printf("\n请编号为%d的顾客办理业务!\n",data->num);
    }
    if(!CycQueueIsEmpty(q)) //若队列不为空 
    {
        data=CycQueuePeek(q);//取队列中指定位置的数据 
        printf("请编号为%d的顾客准备,马上将为您理业务!\n",data->num);      
    }
}

下面是主函数:

int main()
{
    CycQueue *queue1;
    int i,n;
    char select;
    num=0;//顾客序号 
    queue1=CycQueueInit(); //初始化队列 
    if(queue1==NULL)
    {
        printf("创建队列时出错!\n");
        getch();
        return 0;
    }
    do{
        printf("\n请选择具体操作:\n");
        printf("1.新到顾客\n");
        printf("2.下一个顾客\n");
        printf("0.退出\n") ;
        fflush(stdin);
        select=getch();
        switch(select)
        {
            case '1':
                add(queue1);
                printf("\n现在共有%d位顾客在等候!\n",CycQueueLen(queue1));
                break;
            case '2':
                next(queue1);
                printf("\n现在共有%d位顾客在等候!\n",CycQueueLen(queue1));
                break;
            case '0':
                break;
        }        
    }while(select!='0');
    CycQueueFree(queue1); //释放队列
    getch();
    return 0;
}
在菜单提示下输入1,表示有1位新顾客入队,再输入1,表示又有一位新顾客入队,再输入2,表示柜台开始办理业务,呼叫1号办理业务,并提示2号做好准备。














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值