C/C++语言数据结构快速入门(代码解析+内容解析)队列

Posted 蓝盒子bluebox

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++语言数据结构快速入门(代码解析+内容解析)队列相关的知识,希望对你有一定的参考价值。

一、队列的基本概念

注:数据结构三要素–逻辑结构,数据的运算,存储结构(物理结构)
存储结构不同,运算的实现方式也不同

1、队列的定义

线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n=0的时候线性表是一个空表。若用L命名线性表,则其一般表示为

栈(Stack)是只允许在一端进行插入(进栈)或删除(出栈)操作的线性表

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

队列(Queue)是只允许在一端进行插入,在另外一端删除的线性表

特点:先进入队列的元素先出对
主要术语:
队头、队尾、空队列

2、队列的基本操作

InitQueue(&Q);初始化队列,构造一个空队列Q。(创)

DestoryQueue(&Q);销毁队列。销毁并释放队列Q。(销)

EnQueue(&Q,x);入队,若队列Q未满,将x加入,使只成为新的队尾。(增)

(删除队头元素)DeQueue(&Q,&x);出队,若队列Q非空,删除对头元素,并用x返回。(删)

(不删除对头元素)GetHead(Q,&x);读队头元素,若队列Q非空,则将对头元素赋值给x。(查:队列的使用场景当中大多数只能访问对头元素)

二、队列的操作

1、队列的顺序实现

#define MaxSize 10			//定义队列中元素的最大值
typedef struct {
	ElemType data[MaxSize];	//用静态数组存放队列元素
	int front,rear;
}SqQuenue;

Sq:sequence—顺序

连续的存储空间,大小MaxSize*sizeof(ElemType)

void testQueue(){
	SqQueue Q;//声明一个队列(顺序存储)
	//...后续操作...
}


2、初始化操作

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

//初始化队列
void InitQueue(SqQueue &Q){
	//初始时 对头 队尾指针指向Q
	Q.rear = Q.front = 0; 
} 
void testQueue(){
	//声明一个队列(顺序存储)
	SqQueue Q;
	InitQueue(Q);
	//...后续操作... 
}
//判断队列是否为空
bool QueueEmpty(SqQueue Q){
	if(Q.rear == Q.front) //队空条件
		return true;
	else 
		return false;
}

3、入队操作(只能从队尾入队)(插入)

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int front,rear;
}SqQueue;
//入队
bool EnQueue(SqQueue &Q,ElemType x){
	if(队列已满)
		return false;//队满则报错
	Q.data[Q.rear] = x;//将x插入队尾
	Q.rear=Q.rear + 1;//队尾指针后移
	return true;
}
(1)插入一个元素


插入多个元素

当队尾rear==MaxSize???的时候,此时队列并没有存满
因为front端可能会出队

当rear指向最后一个位置的元素的时候

如果此时front不在头结点的位置,

rear会回到头结点报错新插入的元素到头结点

(2)入队操作的改进版
#include<stdio.h>
#define MaxSize 10			//定义队列中元素的最大个数 
typedef struct{
	ElemType data[MaxSize];	//用静态数组存放队列元素 
	int front,rear;			//对头指针和队尾指针 
}SqQueue; 

//入队
bool EnQueue(SqQueue &Q,ElemType x){
	if(队列已满)
		return false;
	Q.data[Q.rear] = x;
	Q.rear = (Q.rear + 1) % MaxSize;//如果rear为9的时候  1+9=10  10/10 = 0  ==>rear会回到头结点 
	return true; 
} 
 


取模运算,即取余运算。两个整数a,b,a%b == a除以b的余数

{0,1,2,3,…,MaxSize-1}将存储空间在逻辑上变成了“环状”

模运算将无线限的整数域映射到有限的整数集合{0,1,2,3,…,b-1}上

4、循环队列(用模运算将存储空间在逻辑上变成了“环状”)

(1)入队操作
Q.data[Q.rear] = x;				//新元素插入队尾
Q.rear = (Q.rear + 1) % MaxSize;//队尾指针加1取模




队列已满的条件:对尾指针的再下一个位置是队头,既(Q.rear+1)%MaxSize==Q.front

代价:牺牲一个存储单元

//判断队列是否为空
bool QueueEmpty(SqQueue Q){
	if(Q.rear == Q.front)	//判断队空(对空条件) 
		return;
	else
		return false;
} 
//入队
bool EnQueue(SqQueue &Q,ElemType x){
	if((Q.rear+1) % MaxSize == Q.front )	//判断队满 
		return false;						//队满则报错 
	Q.data[Q.rear] = x;						//新元素插入队尾 
	Q.rear=(Q.rear + 1)%MaxSize;			//队尾指针加1取模(用模运算将存储空间在逻辑上变成了“环状”) 
	return true;
}
(2)出队操作(只能让队头元素出队)
//出队(删除一个队头元素,并用x返回)
bool DeQueue(SqQueue &Q,ElemType &x){
	if(Q.rear == Q.front)//判断队空
		return false;//队空则报错
	x=Q.data[Q.front];
	Q.front=(Q.front+1)%MaxSize;//队头指针后移
	return true;
}


获得对头元素的值,用x返回

bool DeQueue(SqQueue &Q,ElemType &x){
	if(Q.rear == Q.front)//判断队空
		return false;//队空则报错
	x=Q.data[Q.front];
	return true;
}
(3)方案一:判断队列已满/已空
#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int front,rear;//初始化rear=front=0
}SqQueue;

队列元素个数
(rear+MaxSize-front)%MaxSize;

当前上述左边的情况下的时候
(2+10-3)%10 = 9 % 10 = 9

(4)方案二:判断队列已满/已空(不浪费那片存储空间)
#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int front,rear;//初始化rear=front=0
	int size;//队列当前的长度(插入成功size++,删除成功size--) (初始化的时候rear=front=0 size=0)
}SqQueue;

插入元素

删除元素

(5)方案三:判断队列已满/已空(不浪费那片存储空间)
#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int front,rear;//初始化rear=front=0
	int tag;//最近进行的是删除/插入
}SqQueue;

每次删除操作成功的时候,都令tag=0
每次插入操作成功的时候,都令tag=1

只有删除操作,才可能导致队空
只有插入操作,才可能导致队满


(6)其他出题方法



判断队列是否为空
(Q.rear+1)%MaxSize == Q.front

判断队列是否已满
方案一:牺牲一个存储单元
方案二:增加辅助变量
在这里插入图片描述

(7)知识回顾与重要考点

5、队列的链式的实现

(1)知识总览

(2)队列的链式
#include<stdio.h>
typedef struct LinkNode{		//链式队列结点 
	ElemType data;
	struct LinkNode *next;
}LinkNode; 

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


链队列–链式存储实现的队列

(3)初始化(带头结点)
#include<stdio.h>
typedef struct LinkNode{		//链式队列结点 
	ElemType data;
	struct LinkNode *next;
}LinkNode;
typedef struct {			//链式队列 
	LinkNode *front,*rear;	//队列的对头和队尾指针 
}LinkQueue;
//初始化队列(带头结点)
void InitQueue(LinkQueue &Q){
	//初始化front rear 都指向头结点
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));//并分配内存空间 
	Q.front->next = NULL;//头结点的指针指向空 
}
//判断队列是否为空
bool IsEmpty(LinkQueue &Q){
	if(Q.front == Q.rear)
		return true;
	else
		return false;
}
void testLinkQueue(){
	LinkQueue Q;//声明一个队列 
	InitQueue(Q);//初始化
	//...后续操作... 
}

(4)初始化(不带头结点)
//初始化队列(不带头结点)
void InitQueue(LinkQueue &Q){
	//初始时front ,rear都指向NULL
	Q.front = NULL;
	Q.rear = NULL; 
} 
//判断队列是否为空(不带头结点)
bool IsEmpty(LinkQueue Q){
	if(Q.front == NULL)
		return true;
	else 
		return false;
}

rear–>NULL
front–>NULL

不带头结点的空队列

(5)入队(带头结点)
//入队(带头结点)
//新元素入队(带头结点)
void EnQueue(LinkQueue &Q,ElemType x){
	LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));//对链表分配内存空间 
	s->data = x;
	s->next = NULL;
	Q.rear->next = s;		//新结点插入到rear之后 
	Q.rear = s;				//修改表尾指针 
}

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

(7)出队(带头结点)
//队头元素出队(不带头结点)
bool DeQueue(LinkQueue &Q,ElemType &x){
	if(Q.front == Q.rear)
		return false;//队空
	LinkNode *p = Q.front->next;
	x=p->data;//用变量x返回队头元素
	Q.front->next = p->next;//修改头结点的next指针
	if(Q.rear == p)			//此次是最后一个结点出队 
		Q.rear = Q.front;	//修改rear指针 
	free(p);			//释放结点空间 
	return true; 
} 


(8)出队(不带头结点)
//队头元素出队(不带头结点)
bool DeQueue(LinkQueue &Q,ElemType &x) {//传入参数是队列和要出队的元素的地址值
	if(Q.front == NULL)//判断传入队列的首地址值是否为空,如果为空则直接返回false
		return false;	//空队 
	LinkNode *p = Q.front;//p指向此次出栈的头结点,声明队列类型的指针指向队列的头位置
	x=p->data;			//用变量x返回队头元素
	Q.front = p->next; //修改front指针
	if(Q.rear == p){		//修改front指针 
		Q.front = NULL;	//front指向NULL 
		Q.rear = NULL;//rear 指向NULL 
	} 
	free(p);	//释放结点空间 
	return true;
}

(9)队列满的条件



三、双端队列

1、双端队列是运行两端插入,两端删除的线性表


如果只使用其中一端的插入,删除操作,则效果等同于栈。

2、输入受限的双端队列:只允许从一端插入,两端删除的线性表

3、输出受限的双端队列:只允许从两端插入,一端删除的线性表

4、考点:判断输出序列合法性:若数据元素输入序列为1,2,3,4,则那些输出序列是合法的,那些是违法的?

(1)栈

以下红色为非法的输出序列

(2)输入受限的双端队列

(3)输出受限的双端队列

6、知识点回顾

以上是关于C/C++语言数据结构快速入门(代码解析+内容解析)队列的主要内容,如果未能解决你的问题,请参考以下文章

C/C++语言数据结构快速入门(代码解析+内容解析)栈的应用

C/C++语言数据结构快速入门(代码解析+内容解析)栈

C/C++语言数据结构快速入门(代码解析+内容解析)栈

C/C++语言数据结构快速入门(代码解析+内容解析)队列的应用

C/C++语言数据结构快速入门(代码解析+内容解析)特殊矩阵

C/C++语言数据结构快速入门(代码解析+内容解析)队列