大话数据结构之队列

队列(Queue):一种先进先出(first in first out)的线性表。只允许在尾部(rear)插入,头部(front)删除。


上图可以形象的描述队列的特点。数据元素排队进入队列,按顺序排队出队列。

实现顺序队列时,可以采用一组地址连续的地址单元,例如数组。队列头(front)与队尾(rear)在初始化时,指向数组的"0"地址。下面为插入新的元素时的操作:


入队操作:不需要移动任何元素,在队尾追加一个元素。故时间复杂度为O(1);

 

出对列操作

 

出对列操作:front指向数组0地址。即队列头部始终在0地址。删除其中元素后,为保证头部单元“内容”不为空。需要将后面的元素往前移动。这样,需要的时间复杂度为:O(n);

那么为了避免繁琐的将后面的元素向前移动,可以规定front不必非得指向数组中下标为0的位置。如下图:


 front指向下一个元素。删除一个,移动一次。

 


 

当fornt ==rear 时,可以是空队列,也可以是"只剩下一个元素的队列”

故为了避免这种情况,

规定:front指针指向对头元素,rear指针指向最后元素的下一位置(队尾元素的下一位置)。这样:front == rear 是为空队列。

 

下面为长度为5的数组,初始状态 front == rear。依次插入新元素。元素a1,a2出队列后,在插入a5,情况如图:


rear所指超出界限。但是下标0,1出为空。


难道不利用了吗?还要将增加数组的长度吗?那多大的数组才合适呢?

前面的空单元没有利用。此现象称之为"假溢出"。为了解决这种问题。循环队列就呼之欲出了。

循环队列定义:首尾相接的顺序存储结构。

那么当上面的情况出现在循环队列里时,如图:rear指向下标0处。


 为了避免rear == front 判断是空,还是满队列时。

常用的方法是规定:

(1)    空队列:front == rear;

 

(2)队列满:保留一个元素空间,即有一个单元为空。

 

 

故队列满时:(rear + 1 + QueueSize) % QueueSize == front  /*QueueSize 为队列的分配空间*/

 

(3)队列长度,即队列元素个数:(rear -front + QueueSize) % QueueSize

 

(4)插入新的元素后:rear = (rear+ 1) % QueueSize

 

(5)删除元素后: front ==(front + 1) % QueueSize

 

有了上述公式后,循环队列的顺序结构实现就清晰明了了。

代码:

typedef int Status;
typedef int QElemType;

typedef struct Queue
{
	QElemType data[MAXSIZE];
	int front;
	int rear;
}SQueue;

Status Visit(QElemType q)
{
	printf("%3d",q);
	return OK;
}

/*
****操作结果:初始化队列
*/
Status InitQueue(SQueue *Q)
{
	Q->front = 0;
	Q->rear = 0;
	return OK;
}

/*
****操作结果:将非空队列清空
*/
Status ClearQueue(SQueue *Q)
{
	Q->front = Q->rear = 0;
	return OK;
}

/*
****操作结果:若队列空,则返回TRUE,否则返回FALSE
*/
Status EmptyQueue(SQueue Q)
{
	if(Q.front == Q.rear)
		return TRUE;
	else
		return ERROR;
}

/*
****操作结果:返回队列元素个数
*/
int LengthQueue(SQueue Q)
{
	return (Q.rear - Q.front +MAXSIZE)%MAXSIZE;
}

/*
****操作结果:向未满的队列尾部插入新的数据元素
*/
Status InsertQueue(SQueue *Q,QElemType e)
{
	if((Q->rear + 1+ MAXSIZE)%MAXSIZE == Q->front)   //队列满
		return ERROR;

	Q->data[Q->rear] = e;
	printf("插入新的数据元素:%d\n",e);
	Q->rear = (Q->rear + 1)%MAXSIZE;
	return OK;
}

/*
****操作结果:若队列非空,则删除队列头部元素,并用e返回,成功OK,失败ERROR
*/
Status DeleteQueue(SQueue *Q,QElemType *e)
{
	if(Q->front == Q->rear)          //队列空
		return ERROR;
	
	*e = Q->data[Q->front];
	printf("删除的队列头部元素为:%d\n",*e);
	Q->front = (Q->front + 1)%MAXSIZE;
	return OK;
}

/*
****操作结果:得到非空队列的头部元素
*/
Status GetHead(SQueue Q,QElemType *e)
{
	if(Q.front == Q.rear)
		return ERROR;

	*e = Q.data[Q.front];
	printf("得到的队列头元素为:%d\n",*e);
	return OK;
}
/*
****操作结果:从对列头到队列尾一次输出队列数据元素
*/
Status Traverse(SQueue Q)
{
	int i ;
	i = Q.front;
	printf("将队列元素输出:\n");
	
	while((i + Q.front) != Q.rear)
	{
		Visit(Q.data[i]);
		i = (i + 1)%MAXSIZE;
	}
	printf("\n");
	return OK;
}

下面为队列链式实现:

typedef struct QNode
{
	QElemType data;
	struct QNode *next;
}QNode ,*QueuePtr;              //*QueuePtr  指针类型的结点

typedef struct 
{
	QueuePtr front;            //头指针
	QueuePtr rear;             //尾指针
}LinkQueue;

Status Visit(QElemType q)
{
	printf("%3d",q);
	return OK;
}

/*
****操作结果:初始化 一个带头结点的队列
*/
Status InitQueue(LinkQueue *Q)
{
	Q->front = Q->rear = (QueuePtr)malloc(MAXSIZE*sizeof(QNode)); 
	if(!Q->front)
		exit(OVERFLOW);

	Q->front->next = NULL;
	return OK;
}

/*
****操作结果:判断队列是否为空,空 返回TRUE,否则返回FALSE
*/
Status EmptyQueue(LinkQueue Q)
{
	if(Q.front == Q.rear)
		return TRUE;
	else
		return FALSE;
}

/*
**** 操作结果:销毁队列
*/
Status DestroyQueue(LinkQueue *Q)
{
	while(Q->front)
	{
		Q->rear = Q->front;
		free(Q->front);
		Q->front = Q->rear;
	}
	return OK;
}

/*
****操作结果:清空队列
*/
Status ClearQueue(LinkQueue *Q)
{
	QueuePtr p,q;
	p = Q->front->next;

	while(p)
	{
		q = p->next;
		free(p);
		p = q;
	}
	Q->rear = Q->front;
	Q->front->next = NULL;
	return OK;
}

/*
****返回队列元素个数
*/
int LengthQueue(LinkQueue Q)
{
	int i = 0;
	QueuePtr p;
	p = Q.front;
	while(p != Q.rear)           //不存在队列满情况
	{
		p = p->next;
		i++;
	}
	return i;
}

/*
****操作结果:向队尾插入的新的数据元素
*/
Status InsertQueue(LinkQueue *Q,QElemType e)//不存在队列满,故不用判断
{
	QueuePtr p;
	p = (QueuePtr)malloc(sizeof(QNode));
	if(!p)
		exit(OVERFLOW);
	
	p->data = e;
	p->next = NULL;
	Q->rear->next = p;
	Q->rear = p;
	return OK;
}

/*
****操作结果:若队列非空,删除对头元素
*/
Status DeleteQueue(LinkQueue *Q,QElemType *e)
{
	QueuePtr p;

	if(Q->front == Q->rear)        //队列空
		return ERROR;
	p = Q->front->next;            //p指向队列第一个结点,Q->front 代表头结点
	*e = p->data;
	Q->front->next = p->next;

	if(Q->rear == p)              /*若队列只有一个结点,即此时 p 与 rear指向同一结点。
		                          **** 删除p 结点后,应该将rear 指向fornt.*/
		Q->rear = Q->front;
	free(p);
	return OK;
}

/*
****操作结果:若队列非空,用e返回对头的数据元素
*/
Status GetHead(LinkQueue Q,QElemType *e)
{
	QueuePtr p;
	if(Q.front == Q.rear)
		return ERROR;

	p = Q.front->next;
	*e = p->data;
	return OK;
}

/*
****操作结果:从对头至队尾一次输出队列元素
*/
Status Traverse(LinkQueue Q)
{
	QueuePtr p;
	if(Q.front == Q.rear)
		return ERROR;

	p = Q.front->next;
	while(p)
	{
		Visit(p->data);
		p = p->next;
	}
	printf("\n\n");
	return OK;
}

参考了《大话数据结构》一书,作者讲解的清晰明了。适合入门,以及知识点回顾。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值