数据结构(7)栈和队列->队列

  • 整理数据结构学习笔记
  • 基于C++

一、队列的相关定义

  • 队列(queue):是限定仅在队尾进行插入,在队头进行删除的线性表。
  • 队头(front):线性表中允许删除的一端
  • 队尾(rear):线性表中允许插入的一端
  • 空队:不含元素的空表
  • 入队(Enqueue):在线性表尾部(队尾)后新增一个元素
  • 出队(Dequeue):把线性表头部(队头)的元素取出并从表中删除
  • 可见,最先入队的元素最先出队,这种特性称为先进先出(FIFO)
    在这里插入图片描述

二、队列的表示和实现

队列通常用链式存储结构实现

1、链队列

(1)定义

  • 链队列是使用单链表结构实现的队列
  • 设置两个指针分别指示队头(头指针)和队尾(尾指针)
  • 为方便起见,在真正队头元素的前一个添加头结点,令头指针指向它

(2)数据结构

//队列(链表)中的一个节点
typedef struct QNode
{
	int data;
	struct QNode *next; 
}QNode,*QueuePtr;

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

(3)基本操作

1、初始化队列 bool InitQueue(LinkQueue &Q)
  • 初始化队列为空队,申请一个结点作为头结点,并让头尾指针指向它即可
  • 注意这时Q.front==Q.rear,这也是空队的判据
    在这里插入图片描述
bool InitQueue(LinkQueue &Q)
{
	Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
	if(!Q.front)
		exit(OVERFLOW);
	Q.front->next=NULL;
	return OK;
}
2、销毁队列 bool DestroyQueue(LinkQueue &Q)
  • 可以利用头尾指针遍历队列,依次释放所有结点空间
bool DestroyQueue(LinkQueue &Q)
{
	while(Q.front)
	{
		Q.rear=Q.front->next;
		free(Q.front);
		Q.front=Q.rear;
	}
	return OK;
}
3、置空 bool ClearQueue(LinkQueue &Q)
  • 注意和栈的区别,链表结点空间难以重复利用,置空时必须把除头结点外的所有队列结点释放空间
  • 设两个指针遍历头结点后的队列(链表),依次释放空间
  • 重设尾指针,令Q.rear=Q.front
  • 特别注意不要忘了Q.rear->next=NULL
bool ClearQueue(LinkQueue &Q)
{
	QueuePtr p,q;
	p=Q.front->next; 
	while(p)
	{
		q=p->next;
		free(p);
		p=q;
	}
	Q.rear=Q.front;
	Q.rear->next=NULL;
	return OK;
}
4、判空 bool QueueEmpty(LinkQueue Q)
  • 判断top和base是否相等
bool QueueEmpty(LinkQueue Q)
{
	if(Q.front==Q.rear)
		return true;
	else
		return false;
}
5、返回队长度 int QueueLength(LinkQueue Q)
  • 设一个指针遍历队列计数
int QueueLength(LinkQueue Q)
{
	int l=0;
	QueuePtr p;
	p=Q.front->next;
	while(p)
	{
		l++;
		p=p->next;
	}
	return l;
}
6、队头元素 bool GetHead(LinkQueue Q,int &e)
  • 先判断队列非空
  • 取头结点后第一个元素
bool GetHead(LinkQueue Q,int &e)
{
	if(QueueEmpty(Q))
		return ERROR;
	QueuePtr p;
	p=Q.front->next;
	e=p->data;
	return OK;
}
7、入队 bool EnQueue(LinkQueue &Q,int e)
  • 大体和链表在尾部插入一个结点的操作相同,只是要注意一下尾指针
  • 申请结点,调整指针顺序,图示为在空表中添加元素x和y
    在这里插入图片描述
bool EnQueue(LinkQueue &Q,int e)
{
	QueuePtr p;
	p=(QueuePtr)malloc(sizeof(QNode));
	p->data=e;
	p->next=NULL;
	Q.rear->next=p;
	Q.rear=p;	//这个别忘了
	return OK;
}
8、出队 bool DeQueue(LinkQueue &Q,int &e)
  • 先判断队列非空
  • 取头结点后第一个元素,移动指针 Q.front->next=Q.front->next
  • 特别注意,如果队中只有一个元素,移动头指针后front指向NULL正确,但尾指针还指向着出队的那个元素,如果现在直接释放结点空间,尾指针就会丢失,所以这里要单独判断一下尾指针的情况,图示为x元素出队的情形
  • 处理好指针问题后,释放出队结点的空间
bool DeQueue(LinkQueue &Q,int &e)
{
	if(QueueEmpty(Q))
		return ERROR;
	QueuePtr p;
	p=Q.front->next;
	e=p->data;
	Q.front->next=p->next;
	//防止只剩一个元素时队尾指针没了 
	if(Q.rear==p)
	Q.rear=Q.front; 
	free(p);
}
9、遍历队列 bool QueueTraverse(LinkQueue Q,bool (*visit)(QNode a))
bool QueueTraverse(LinkQueue Q,bool (*visit)(QNode a))
{
	QNode *p;
	p=Q.front->next;
	while(p)
	{
		if(visit(*p)==ERROR)
			return ERROR;
		p=p->next;
	}
	return OK;
	
}

bool Visit(QNode a)
{
	cout<<a.data<<" ";
	return OK;
}

2、顺序队列(循环队列)

(1)定义

  • 顺序队列是使用一组连续的存储单元实现的队列
  • 设置两个指针分别指示队头(头指针)和队尾(尾指针)。为方便实现,我们规定初始化空队时令front=rear=0,每当尾部有元素入队,rear加一;每当头部有元素出队,front加一
  • 根据以上约定,非空队列中,front指针始终指向头部元素,rear始终指向真正尾部元素再下一个,如图所示
    在这里插入图片描述
  • 很容易发现队列头部的空间无法重复利用,为了解决这一问题,我们通常把顺序队列臆造为环状结构,称之循环队列
    在这里插入图片描述
  • 如上图所示,循环队列中,front==rear时可能为空队也可能为满队,不能再用它作为判空依据了。通常的做法有两个:一是和上图一样牺牲一个存储单元,判空依据为“rear指针的下一个位置为front指向位置”;二是另设一个判空标志,同时判断指针相等和判空标志。
  • 这样处理后,由于指针的问题,无法使用动态申请空间的方法延长队列了,所以一定要事先确定循环队列长度,如果难以确定最好还是使用链式队列。

(2)数据结构

typedef struct 
{
	int rear;	//尾指针
	int front;	//头指针
	int *base;	//队列空间基地址
	int size; 	//队列尺寸(包括用于判空那个元素在内)
}CQueue; 

(3)基本操作

1、初始化队列 bool InitQueue(CQueue &Q,int size)
  • 传入队最大长度,初始化队列为空队
  • 注意多申请一个空间用于区分空队满队
  • 指针front=rear=0
bool InitQueue(CQueue &Q,int size)
{
	Q.size=size+1;	//有一个空间浪费在判空 
	Q.base=(int *)malloc(Q.size*sizeof(int));
	if(!Q.base)
		return ERROR; 
	Q.front=Q.rear=0;
	return OK; 
}
2、置空 void ClearQueue(CQueue &Q)
  • 循环队列用数组实现,类似栈。清空时只要移动指针即可,不需要释放空间
  • 直接设置指针front=rear=0
void ClearQueue(CQueue &Q)
{
	Q.front=Q.rear=0;
}
3、判空 bool QueueEmpty(CQueue Q)
  • 判断top和base是否相等
bool QueueEmpty(CQueue Q)
{
	if(Q.rear==Q.front)
		return true;
	else
		return false;
}
4、判满 bool QueueFull(CQueue Q)
  • 判断rear后移一位后是否和front相等
  • 循环队列中指针p后移一位:p=(p+1)%size
bool QueueFull(CQueue Q)
{
	if((Q.rear+1)%Q.size==Q.front)
		return true;
	else
		return false;
}
5、返回队长度 int QueueLength(CQueue Q)
  • 由于数组特性,可以用指针进行加减运算来求长度
int QueueLength(CQueue Q)
{
	int l=Q.rear-Q.front;
	if(l>0)
		return l;
	else
		return Q.size+l; 
}
6、入队 bool EnQueue(CQueue &Q,int e)
  • 先判断队列未满
  • 在尾指针处赋值,后移尾指针Q.rear=(Q.rear+1)%Q.size
bool EnQueue(CQueue &Q,int e)
{
	if(QueueFull(Q))
		return ERROR;
	Q.base[Q.rear]=e;
	Q.rear=(Q.rear+1)%Q.size;
	return OK;
}
7、出队 bool DeQueue(CQueue &Q,int &e)
  • 先判断队列非空
  • 取指针处值,后移头指针Q.front=(Q.front+1)%Q.size
bool DeQueue(CQueue &Q,int &e)
{
	if(QueueEmpty(Q))
		return ERROR;
	e=Q.base[Q.front];
	Q.front=(Q.front+1)%Q.size;
	return OK; 
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值