队列基础篇

目录

一、基本概念

1.定义:

2.特点:

3.相关术语:

4.基本操作:

二、队列的顺序实现

1.结构定义

2.初始化

3.判空

4.入队

5.出队

6.查队头元素

7.相关说明:

三、队列的链式实现

1.链队列定义

2.初始化

3.判空

4.入队

5.出队

6.补充说明


一、基本概念

1.定义:

只允许在一端进行插入(入队),在另一端删除(出队)的线性表;

2.特点:

先进先出(FIFO:First In First Out);

3.相关术语:

(1)空队列:没有元素的队列称为空队列;

(2)队尾:允许插入的一端,队列中最靠近队尾的元素称为队尾元素;

(3)队头:允许删除的一端,队列中最靠近队头的元素称为队头元素;

4.基本操作:

(1)InitQueue(&Q),初始化队列:构造一个空队列Q;
(2)DestroyQueue(&Q),销毁队列:销毁并释放队列Q所占用的内存空间;
(3)EnQueue(&Q,x),入队:若队列Q未满,将x加入,使之成为新的队尾;
(4)DeQueue(&Q,&x),出队:若队列Q非空,删除队头元素,并用x值返回;
(5)GetHead(Q,&x),读队头元素:若队列Q非空,则将队头元素赋值给x;
(6)QueueEmpty(Q),队列判空:若队列Q为空返回true,否则返回false。


二、队列的顺序实现

1.结构定义

#define MaxSize 10			//定义队列中元素的最大个数
typedef struct {
	int data[MaxSize];		//用静态数组存放队列元素
	int front, rear;		//队头指针和队尾指针
}SqQueue;

(1)声明时:SqQueue Q;

(2)front指向队头元素;rear指向队尾元素的后一个位置(下一个应该插入的位置);

(3)这里的front和rear也同样不是数据类型指针,而是功能上类似指针的int型整数,在这里映射的是队头队尾元素的数组下标。

 

2.初始化

(1)InitQueue(&Q),初始化队列:构造一个空队列Q;代码如下:

void InitQueue(SqQueue& Q)
{
	Q.rear = 0;
	Q.front = 0;			
}

(2)初始化时令两个指针都指向0的位置即可。

3.判空

(1)基于初始化函数中给出的构造空队列的方法,则判空条件为front=rear:

bool QueueEmpty(SqQueue Q)
{
	if (Q.front = Q.rear)	//队空条件
		return true;
	else
		return false;
}

4.入队

(1)EnQueue(&Q,x),入队:若队列Q未满,将x加入,使之成为新的队尾;代码如下:

bool EnQueue(SqQueue& Q, int x)
{
	if ((Q.rear + 1) % MaxSize == Q.front)	//队满报错,条件在下面解释中提出
		return false;
	Q.data[Q.rear] = x;
	Q.rear = (Q.rear + 1) % MaxSize;		//A
	return true;
}

(2)对注释中A句的补充解释:

        如果队尾插满后,rear应该指向的是data[9]的下一个位置也就是指向了rear的存储空间,此时rear=10;如果现在队头有元素出队,那么仅凭一个rear=MaxSize并不能判断队列存满;
        这时(尾部满了头部走了),如果还要插入元素的话,就只能在队头插入。所以仅仅让rear自增1的操作不能满足我们在队头插入的需求,于是用到取余运算。
        这样一来,模运算将无限的整数域映射到有限的整数集合上,将存储空间在逻辑上变成了“环状”;用这种方式实现的队列称为“循环队列”(用模运算将存储空间在逻辑上变成了“环状”)。


        再来说队满的条件:按理来说,rear和front指向同一个存储空间时,队列真正存满,但这时会存在一个问题;前面我们在判空函数中说到,rear和front一旦指向同一个位置,就意味着队列判空,所以队满的条件不能这样安排;我们不得已需要牺牲一个存储单元来做到判断队满,条件如下:
        队尾指针的再下一个位置是队头,即(Q.rear+1)%MaxSize==Q.front。

 

 

5.出队

(1)DeQueue(&Q,&x),出队:若队列Q非空,删除队头元素,并用x值返回;代码如下:

bool DeQueue(SqQueue& Q, int& x)
{
	if (Q.front == Q.rear)					//空队列报错
		return false;
	x = Q.data[Q.front];					//将队头指针所指的元素的数据域赋值给x
	Q.front = (Q.front + 1) % MaxSize;		//队头指针后移
	return true;
}

6.查队头元素

(1)GetHead(Q,&x),读队头元素:若队列Q非空,则将队头元素赋值给x;代码如下:

bool GetHead(SqQueue Q, int& x)
{
	if (Q.front == Q.rear)					//空队列报错
		return false;			
	x = Q.data[Q.front];					//将队头指针所指的元素的数据域赋值给x
	return true;	
}

7.相关说明:

(1)队列元素个数计算:(rear+MaxSize-front)%MaxSize;

(2)上面提到了一种判满判空的方法,但是需要浪费一个存储单元,下面给出两种方法可以避免这种浪费:

①初始化时,在队列结构中再定义一个int size来表示当前队列长度,每次插入成功size++,每次删除成功size--;则判满条件为size==MaxSize;判空条件为size==0;

②初始化时,在队列结构中再定义一个int tag来表示最近一次执行的是插入还是删除;每次插入成功后把tag=1,每次删除成功后把tag=0;则判满条件为Q.rear==Q.front&&tag==1;判空条件为Q.rear==Q.front&&tag==0;

(3)有时题目让rear指向当前的队尾元素而不是队尾元素的下一个位置,这时相对应的一些操作改变如下:

①入队:Q.rear=(Q.rear+1)%MaxSize; Q.data[Q.rear]=x;

②初始化:队尾指针指向MaxSize-1,即Q.rear=MaxSize-1;

③判空:(Q.rear+1)%MaxSize==Q.front;

④判满:不加辅助变量的前提下,也就是牺牲一个存储单元,那rear就只能指向front前两个位置,也就是(Q.rear+2)%MaxSize==Q.front。


三、队列的链式实现

1.链队列定义

typedef struct LinkNode {			//链式队列结点
	int data;
	struct LinkNode* next;
}LinkNode;

typedef struct {					//链式队列
	LinkNode* front, * rear;		//队列的队头和队尾指针
}LinkQueue;

2.初始化

(1)带头结点

void InitQueue(LinkQueue& Q)
{
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
	Q.front->next = NULL;
}

(2)无头结点

void Initqueue(LinkQueue& Q)
{
	Q.front = NULL;
	Q.rear = NULL;
}

3.判空

(1)带头结点

bool IsEmpty(LinkQueue Q)
{
	if (Q.front == Q.rear)
		return true;
	else
		return false;
}

(2)无头结点

bool Isempty(LinkQueue Q)
{
	if (Q.front == NULL)
		return true;
	else
		return false;
}

4.入队

(1)带头结点

void EnQueue(LinkQueue& Q, int x)
{
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	Q.rear->next = s;		//新结点插入到rear之后
	Q.rear = s;				//修改表尾指针
}

(2)无头结点

void Enqueue(LinkQueue& Q, int x)
{
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;			
	if (Q.front == NULL)		//在空队列中插入第一个元素
	{
		Q.front = s;			//修改队头队尾指针
		Q.rear = s;				//不带头结点的队列,第一个元素入队需要特殊处理
	}
	else {
		Q.rear->next = s;		//新结点插入到rear结点之后
		Q.rear = s;				//修改rear指针
	}
}

5.出队

(1)带头结点

bool DeQueue(LinkQueue& Q, int& x)
{
	if (Q.front == Q.rear)
		return false;				//空队列
	LinkNode* p = Q.front->next;	//定义p指向要删除的结点,也就是头结点的下一个结点
	x = p->data;					//用变量x返回要删除的元素
	Q.front->next = p->next;		//修改头结点的next指针
	if (Q.rear == p)				//此次是最后一个结点出队
		Q.rear = Q.front;			//修改rear指针
	free(p);
	return true;
 }

(2)无头结点

bool Dequeue(LinkQueue& Q, int& x)
{
	if (Q.front = NULL)
		return false;				//空队,没有可出元素
	LinkNode* p = Q.front;			//定义p指向头指针所指的结点,也就是出队的结点
	x = p->data;					//用变量x返回队头元素
	Q.front = p->next;				//修改front指针
	if (Q.rear == p)				//此次是最后一个结点出队
	{
		Q.front = NULL;
		Q.rear = NULL;				//修改front和rear指针都指向空
	}
	free(p);						//释放结点空间
	return true;
}

6.补充说明

(1)顺序存储时预分配的空间耗尽则队满,而在链式存储时一般不会出现队满的情况,除非内存不足。

(2)双端队列
①概念:允许从两端插入、两端删除的线性表,若只使用一端的插入删除操作,则效果等同于栈;
②输入受限的双端队列:只允许从一端插入、两端删除的线性表;
③输出受限的双端队列:只允许从两端插入、一端删除的线性表。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值