数据结构-----循环队列 链式队列

目录

队列的定义

顺序队列

循环队列

循环队列的实现

链式队列

链式队列的实现


 

队列的定义

队列是一种只允许一端进行插入操作,一端只允许进行删除操作的线性表。

        队列是一种先进先出(FIFO)的线性表,它只允许在表的一端进行插入和在表的另一端删除元素。在队列中,允许插入的一段叫做队尾(rear),允许删除的一端叫做队头(front)。假设队列为q = (a1,a2,a3......an)。那么a1即为队头元素,an为队尾元素。如图1所示:

                  

和线性表线性表相似,队列也有顺序存储和链式存储。

顺序队列

       顺序队列通常采用一维数组进行存储。通常设队头指针指向队列的第一个元素,队尾指针指向队尾元素的后一个位置。front和rear的初值在队列初始化时均应置为0;入队时将新元素插入rear所指的位置,然后将rear加1。出队时,删去front所指的元素,然后将front加1并返回被删元素:由此可见,当头尾指针相等时队列为空。在非空队列里,头指针始终指向队头元素,而尾指针始终指向队尾元素的下一个位置。如图2所示:

                           

但是若以这样的形式存储,会出现一个极端状况,如图3所示:

                                                                     

当接着进行入队操作时,发现rear会数组越界,表示不能存储,但是a4前面还有许多空间没有利用,这无疑是对资源的浪费,而这种现象被称之为“假溢出”,为了解决资源浪费,为此设计了循环队列。

循环队列

       所谓的循环队列其实也是采用一维数组进行存储的,但是我们应将之想象为该一维数组首尾相接,如图4所示结构:

                                                             

 

入队:入队时将新元素插入rear所指的位置,然后将rear加1;

出队:删去front所指的元素,然后将front加1并返回被删元素;

假设一种情况:rear指针此时指向下标7的位置,front指针指向下标3的位置,如图5所示:

                                                          

此时添加元素a7,那么rear指针将指向哪里呢?????

       答案时这样的:由于数组0,1,2下标均没有元素,故可以将a7插入下标为7的位置,然后将rear指针指向下标0处,这样便解决了“假溢出”的问题。

那么如何计算front,rear的下标位置呢????

入队:rear = (rear + 1) % MAXSIZE;

出队: front = (front + 1) % MAXSIZE;

前面我们知道,在顺序队列中,当front = rear时,队列为空,如图6所示:

                                                     

此时front = rear,但是此时队列为满,为此我们提出一个约定,约定内容为,整个队列中,空出一个位置作为标记,当rear在front后面并且相邻,则说明队列已满,如图7所示:

                                                       

具体表示方法为:

判空: rear == front;

判满:(rear + 1) % MAXSIZE = front;

获取长度:(rear - front  + MAXSIZE) % MAXSIZE;

循环队列的实现

基本操作:

  • //创建循环队列
    Status InitQueue(SqQueue &Q); 
  • //销毁队列
    Status DestoryQueue(SqQueue &Q);
  • //判断队列是否为满
    bool QueueFull(SqQueue Q);  
  • //判断队列是否为空
    bool QueueEmpty(SqQueue Q);  
  • //清空队列
    Status ClearQueue(SqQueue &Q);
  • //获取队列长度
    int QueueLength(SqQueue Q); 
  • //用e返回队头元素
    Status GetHead(SqQueue Q,QElemType &e);  
  • //插入元素,e为Q的新的队尾元素
    Status EnQueue(SqQueue &Q,QElemType e) ;
  • //删除Q的队头元素,并用e返回其值
    Status DeQueue(SqQueue &Q,QElemType &e) ;
  • //从队尾到队头,依次对Q的每个数据元素调用函数visit().输出队列元素
    Status QueueTraverse(SqQueue Q,Status (*visit)(QElemType e));

代码实现:

#include<stdio.h>
#include<malloc.h>
#define MAXSIZE 100  //最大队列元素

enum Status{OK = 1,ERROR = 0};
typedef int QElemType;

typedef struct
{
	QElemType *base; //初始化的动态分配存储空间
	int front;       //头指针,若队列不空,指向队列头元素
	int rear;        //尾指针,若队列不空,指向队列尾元素的下一位置
}SqQueue;

 //创建循环队列
Status InitQueue(SqQueue &Q)  
{
	//动态开辟一个空队列Q
	Q.base = (QElemType*)malloc(MAXSIZE * sizeof(QElemType));
	if(NULL == Q.base)
		return ERROR;
	Q.front = Q.rear = 0;
	return OK;
}

Status DestoryQueue(SqQueue &Q)
{
	if(Q.base)
		free(Q.base);   //动态释放
	Q.front = Q.rear = 0;
	return OK;
}
//判断队列是否为满
bool QueueFull(SqQueue Q)   
{
	return (Q.rear + 1) % MAXSIZE == Q.front;
}
//判断队列是否为空
bool QueueEmpty(SqQueue Q)    
{
	return Q.rear == Q.front;
}
//清空队列
Status ClearQueue(SqQueue &Q)
{
	Q.front = Q.rear;
	return OK;
}
//获取队列长度
int QueueLength(SqQueue Q)     
{
	return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
//用e返回队头元素
Status GetHead(SqQueue Q,QElemType &e)  
{
	if(QueueEmpty(Q))
		return ERROR;
	e = Q.base[Q.front];
	return OK;
}

//插入元素,e为Q的新的队尾元素
Status EnQueue(SqQueue &Q,QElemType e) 
{	
	if(QueueFull(Q))
		return ERROR;
	Q.base[Q.rear] = e;
	Q.rear = (Q.rear + 1) % MAXSIZE;
	return OK;
}
//删除Q的队头元素,并用e返回其值
Status DeQueue(SqQueue &Q,QElemType &e) 
{
	if(QueueEmpty(Q))
		return ERROR;
	e = Q.base[Q.front];
	Q.front = (Q.front + 1) % MAXSIZE;
	return OK;
}
//定义visit()
Status PrintElement(QElemType e)     
{
	printf("%d ",e);
	return OK;
}
//从队尾到队头,依次对Q的每个数据元素调用函数visit().输出队列元素
Status QueueTraverse(SqQueue Q,Status (*visit)(QElemType e))
{
	while(!QueueEmpty(Q))
	{
		(*visit)(Q.base[Q.front]);
		Q.front = (Q.front + 1) % MAXSIZE;
	}
	printf("\n");
	return OK;
}

main函数:

int main()
{
	SqQueue queue;
	int e;
	InitQueue(queue);
	EnQueue(queue,2);
	EnQueue(queue,4);
	EnQueue(queue,6);
	EnQueue(queue,11);
	QueueTraverse(queue,PrintElement);
	DeQueue(queue,e);
	QueueTraverse(queue,PrintElement);
	printf("len = %d\n",QueueLength(queue));
	ClearQueue(queue);
	QueueTraverse(queue,PrintElement);
	EnQueue(queue,11);
	QueueTraverse(queue,PrintElement);
	GetHead(queue,e);
	printf("head = %d\n",e);
	DestoryQueue(queue);
	return 0;
}

 

链式队列

        所谓链式队列,其实就是一个单链表,只不过对其插入和删除做出了限制;允许插入的一端作为队头(front),允许删除的一端作为队尾(rear);具体结构如图8所示:

                        

此时front指向头结点,rear指向终端节点。

        当队列为空队列时,front和rear均指向头结点。如图9所示:

                                                         

       对于删除则存在一个极端情况:如图10所示:

                                               

对于一般情况,删除队列头元素仅需修改头结点中的指针(front->next = p->next),但是当队列中只有最后一个元素时,这样做便会使rear指针丢失,因此需要对队尾指针重新赋值,即指向头节点(if(rear == p)  rear = front)。

链式队列的实现

基本操作:

//构造一个空队列
Status InitQueue(LinkQueue &Q);
//销毁队列Q,Q不在存在
Status DestroyQueue(LinkQueue &Q);
//将Q清为空队列
Status ClearQueue(LinkQueue &Q);
//若队列为空队列,则返回TRUE,否则返回FALSE
int QueueLength(LinkQueue Q);
//若队列不为空,则用e返回Q的队头元素,并返回OK,否则返回ERROR
Status GetHead(LinkQueue Q,QElemType &e);
//插入元素e为Q的新的队尾元素
Status EnQueue(LinkQueue Q,QElemType e);
//若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回FALSE
Status DeQueue(LinkQueue Q,QElemType &e);

代码实现:

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
enum Status{OK = 1,ERROR = 0,TRUE = 1,FALSE = 0,OVERFLOW = -1};
typedef int QElemType;

typedef struct Node    //结点域
{
	QElemType data;
	struct Node* next;
}Node,*QueuePtr;

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

//构造一个空队列
Status InitQueue(LinkQueue &Q)
{
	Q.front = Q.rear = (QueuePtr)malloc(sizeof(Node));
	if(NULL == Q.front)
		exit(OVERFLOW);
	Q.front->next = NULL;
	return OK;
}
//销毁队列Q,Q不在存在
Status DestroyQueue(LinkQueue &Q)
{
	while(Q.front != NULL)
	{
		Q.rear = Q.front->next;
		free(Q.front);
		Q.front = Q.rear;
	}
	Q.front = NULL;
	return OK;
}
//将Q清为空队列
Status ClearQueue(LinkQueue &Q)
{
	QueuePtr p;
	while(Q.front->next != NULL) 
	{
		p = Q.front->next;
		Q.front->next = p->next;
		free(p);
	}
	Q.rear = Q.front;
	return OK;
}
//若队列为空队列,则返回TRUE,否则返回FALSE
Status QueueEmpty(LinkQueue Q)
{
	if(Q.front == Q.rear)
		return TRUE;
	else
		return FALSE;
}
//获取队列长度
int QueueLength(LinkQueue Q)
{
	int count = 0;    //定义一个计数器
	QueuePtr p = Q.front;
	while(p->next != NULL)
	{
		count++;
		p = p->next;
	}
	return count;
}
//若队列不为空,则用e返回Q的队头元素,并返回OK,否则返回ERROR
Status GetHead(LinkQueue Q,QElemType &e)
{
	if(Q.front == Q.rear)
		return ERROR;
	e = Q.front->next->data;
	return OK;
}
//获取结点
static QueuePtr GetNode(QElemType e)
{
	Node *pNode = (QueuePtr)malloc(sizeof(Node));
	if(NULL == pNode)
		exit(OVERFLOW);
	pNode->data = e;
	pNode->next = NULL;
	return pNode;
}
//插入元素e为Q的新的队尾元素
Status EnQueue(LinkQueue &Q,QElemType e)
{
	Node *pNode = GetNode(e);
	if(NULL == pNode)
		exit(OVERFLOW);
	Q.rear->next = pNode;
	Q.rear = pNode;
	return OK;
}
//若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回FALSE
Status DeQueue(LinkQueue Q,QElemType &e)
{
	if(Q.front == Q.rear)
		return ERROR;        //说明此时队列为空
	QueuePtr p = Q.front->next;
	e = p->data;
	Q.front->next = p->next;
	if(Q.rear == p)
		Q.rear = Q.front;
	free(p);
	return OK;
}
//定义visit()
Status PrintElement(QElemType e)
{
	printf("%d ",e);
	return OK;
}
//从队头到队尾以此对队列Q中的每个元素调用函数visit(),一旦visit失败
Status QueueTraverse(LinkQueue Q,Status (*visit)(QElemType e))
{
	QueuePtr p = Q.front->next;
	while(p != NULL)
	{
		(*visit)(p->data);
		p = p->next;
	}
	printf("\n");
	return OK;
}

main函数:

int main()
{
	LinkQueue queue;
	int e = 0;
	InitQueue(queue);
	EnQueue(queue,2);
	EnQueue(queue,6);
	EnQueue(queue,8);
	EnQueue(queue,12);
	EnQueue(queue,45);
	QueueTraverse(queue,PrintElement);
	DeQueue(queue,e);
	DeQueue(queue,e);
	QueueTraverse(queue,PrintElement);
	EnQueue(queue,25);
	printf("len = %d\n",QueueLength(queue));
	ClearQueue(queue);
	printf("len = %d\n",QueueLength(queue));
	EnQueue(queue,12);
	EnQueue(queue,26);
	QueueTraverse(queue,PrintElement);
	printf("len = %d\n",QueueLength(queue));
	GetHead(queue,e);
	printf("head = %d\n",e);
	DestroyQueue(queue);
	return 0;
}

总结:

             队列其实就是线性表的特例化,它只允许在队头插入元素,在队尾删除元素,其出队入队操作时间复杂度均为O(1)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值