数据结构——队列的详解

上一篇博客:数据结构——栈的详解

队列的定义

 队列和栈相反,队列(queue)是一种先进先出FIFO:first in first out)的线性表。它只允许在表的一端进行插入,而在另一端删除元素。队列和我们日常生活中的排队是一致的,最早进入队列的元素最早离开。在队列中,允许插入的一端叫队尾(tear),允许删除的一端称为队头(front)

队列的结构图

C语言中的队列

队列的基本操作

 队列的基本操作主要有:队列的初始化、入队、出队、取队头元素、判队空、销毁队列、清空队列、求队列中元素的个数以及队列的遍历等等。

队列的初始化

 和上篇说的一样,队列和线性表也类似,队列也有两种表示方法:

  • 用链表表示队列,即链队列
     链队列的操作即为单链表的插入和删除操作的特殊情况,只是尚需修改尾指针和头指针。
    链队列示意图
  • 顺序表示和实现的队列,即循环队列

 循环队列和顺序栈类似,在队列的顺序存储结构中,除了使用一组地址连续的存储单元依次存储从队头到队尾的元素外,还需要附设连个指针 frontrear分别指示队头元素及队尾元素的位置。初始化建造空队列时,令front = rear = 0,每当插入一个新的队列尾元素时,“尾指针增1”;每当删除队列头元素时,“头指针增1”。因此,在非空队列中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置。

头、尾指针和队列中元素的关系
 上图为顺序队列中头、尾指针和队列中元素的关系,假设当前队列可以分配的最大空间为6,则当队列处于(d)状态时不可再继续插入新的队尾元素,这种情况被称为假溢出。如果在当前情况下我们继续插入元素,那么就会因为数组越界而导致程序代码被破坏。然而此时又不宜像顺序栈那样用realloc函数再分配扩大存储空间,因为队列实际可用的存储空间并未占满。一个比较巧妙的方法就是将顺序队列臆造为一个环状的空间,我们称之为循环队列
循环队列示意图
 在循环队列中也存在关系式Q.front = Q.rear,但是我们只凭借这个等式无法判断队列是“空”还是“满”。可以有两种处理方法:

  • 另设一个标志位以区别队列是“空”还是“满”。
    循环队列示意图
  • 少用一个元素存储空间,以队列头指针在队尾指针的下一位置(指环状的下一个位置上)上作为队列满的标志。
    循环队列示意图

我们以下述类型说明作为循环队列的定义:

typedef struct{
	QDataType *base;  //初始化的动态分配存储空间 
	int qFront;	      //队头指针,若队列不空,指向队列头元素 
	int qRear;		  //队尾指针,若队列不空,指向队列尾元素的下一位置 
	int tag = 0;	  //队列是否满的标志位 
}SqQueue;

初始化队列

//初始化队列,构造一个空队列 
Status InitQueue(SqQueue &sq){
  //分配存储空间 
  sq.base = (QDataType *)malloc(MAXQSIZE*sizeof(QDataType));
  if(!sq.base){
  	//如果分配失败则返回error 
  	return ERROR;
  }
  sq.qFront = sq.qRear = 0;  //初始状态下为空队列 
  return OK;
}

入队

 入队首先要判断队列是否已经满了,如果满了则不可以继续入队,否则会发生数组越界。如果没有满则入队,然后将队尾指针加1,再判断一下元素入队后,队列是否满了,如果满了则将标志位设置为1。

Status push(SqQueue &sq,QDataType e){
	if(size(sq)==MAXQSIZE){
		printf("队列已满!\n");
		return ERROR;
	}
	sq.base[sq.qRear] = e;
	//队头、队尾指针加1时从maxsize - 1直接进到 0 ,可用取模(求余)运算实现 
	sq.qRear = (sq.qRear+1)%MAXQSIZE;
	if(sq.qFront == sq.qRear && sq.tag == 0){
		//如果队列满了,则设置标志位为1 
		sq.tag = 1;
	}
	return OK;
}

出队

 出队首先要判断队列是否为空,如果为空,则返回error。不为空则将队头元素出队,然后队头指针加1。出队完成后再判断一下队列是否为空,如果为空,则设置标志位为0。

Status pop(SqQueue &sq,QDataType &e){
	if(size(sq)==0){
		printf("队列已经为空!\n");
		return ERROR;
	}
	e = sq.base[sq.qFront];
	sq.qFront = (sq.qFront+1)%MAXQSIZE;
	sq.tag = 0;
	return e;
}

求队列中的元素的个数

 当我们使用标志位来标志队列是否为满时,我们可以判断一下标志位是否为1,如果为1,则证明此时队列已满,直接返回队列的最大长度即可。否则,使用队尾指针减去队头指针(注意使用取模)即可。

//求队列中元素的个数
Status size(SqQueue &sq){
	if(sq.tag == 1){
		return MAXQSIZE;
	}
	//因为是循环队列,所以需要取模 
	return (sq.qRear - sq.qFront + MAXQSIZE)%MAXQSIZE;
}

取队头元素

&esmp;首先判断队列是否为空,不为空则返回队头元素。

Status front(SqQueue &sq,QDataType &e){
	if(size(sq)==0){
		printf("队列已经为空!\n");
		return ERROR;
	}
	e = sq.base[sq.qFront];
	return e;
}

C语言实现队列的具体代码

#include<stdio.h>
#include<stdlib.h>

#define OK 1
#define ERROR 0
#define MAXQSIZE 10   //最大队列长度 
typedef int QDataType;
typedef int Status;

typedef struct{
	QDataType *base;  //初始化的动态分配存储空间 
	int qFront;	      //队头指针,若队列不空,指向队列头元素 
	int qRear;		  //队尾指针,若队列不空,指向队列尾元素的下一位置 
	int tag = 0;	  //队列是否满的标志位 
}SqQueue;

//初始化队列,构造一个空队列 
Status InitQueue(SqQueue &sq){
	//分配存储空间 
	sq.base = (QDataType *)malloc(MAXQSIZE*sizeof(QDataType));
	if(!sq.base){
		//如果分配失败则返回error 
		return ERROR;
	}
	sq.qFront = sq.qRear = 0;  //初始状态下为空队列 
	return OK;
}

//求队列中元素的个数
Status size(SqQueue &sq){
	if(sq.tag == 1){
		return MAXQSIZE;
	}
	//因为是循环队列,所以需要取模 
	return (sq.qRear - sq.qFront + MAXQSIZE)%MAXQSIZE;
}

//入队
Status push(SqQueue &sq,QDataType e){
	if(size(sq)==MAXQSIZE){
		printf("队列已满!\n");
		return ERROR;
	}
	sq.base[sq.qRear] = e;
	//队头、队尾指针加1时从maxsize - 1直接进到 0 ,可用取模(求余)运算实现 
	sq.qRear = (sq.qRear+1)%MAXQSIZE;
	if(sq.qFront == sq.qRear && sq.tag == 0){
		//如果队列满了,则设置标志位为1 
		sq.tag = 1;
	}
	return OK;
}

//出队
Status pop(SqQueue &sq,QDataType &e){
	if(size(sq)==0){
		printf("队列已经为空!\n");
		return ERROR;
	}
	e = sq.base[sq.qFront];
	sq.qFront = (sq.qFront+1)%MAXQSIZE;
	sq.tag = 0;
	return e;
}

//取队头元素
Status front(SqQueue &sq,QDataType &e){
	if(size(sq)==0){
		printf("队列已经为空!\n");
		return ERROR;
	}
	e = sq.base[sq.qFront];
	return e;
}


int main(){
	SqQueue sq;
	QDataType elem;
	int length;
	
	InitQueue(sq);  //初始化一个队列 
	
	//将0-9入队 
	for(int i=0;i<10;i++){
		push(sq,i);
	}
	//队列满了在入队会返回error 
	push(sq,1); 
	
	//将0-5出队 
	for(int i=0;i<6;i++){
		pop(sq,elem);
		printf("%d ",elem);
	}
	printf("\n");
	
	//取队头元素 
	front(sq,elem);
	printf("当前队头元素为:%d ",elem);
	printf("\n");
	
	//将当前队列中所有的元素出队 
	length = size(sq);
	for(int i=0;i<length;i++){
		pop(sq,elem);
		printf("%d ",elem);
	}
	printf("\n");
	pop(sq,elem);
	
	return 0;
	
} 

C++中的队列

 C++ 对模板(Template)支持得很好,STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。在C++标准中,STL被组织为下面的13个头文件:<algorithm >、<deque >、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>和<utility>。其中的<queue>就是队列。

C++的STL已经将队列的操作都封装成了函数,我们只需要引进#include头文件即可使用。

队列的初始化

 我们可以使用queue<int> q;来创建一个queue对象

入队

入栈

//将0-9入队 
	for(int i=0;i<10;i++){
		q.push(i);
	}

返回对列中的第一个元素

返回队列中的第一个元素

cout<<q.front();

返回队列中的最后一个元素

返回队列中的最后一个元素

cout<<"入队后的队尾元素为:"<<q.back()<<endl;

出队

出队

//将0-5出队 
	cout<<endl;
	for(int i=0;i<6;i++){
		q.pop();	
	}

判断队列是否为空

判空

if(q.empty()){
		cout<<"队列为空!"<<endl;
	}else{
		cout<<"队列不为空!"<<endl; 
	}

求队列中元素的个数

求队列中元素的个数

cout<<"此时队列中元素的个数为:"<<q.size()<<endl;

C++实现队列的完整代码

#include<iostream>
#include<queue>
using namespace std;

int main(){
	queue<int> q;
	
	//将0-9入队 
	for(int i=0;i<10;i++){
		q.push(i);
	}
	cout<<"入队后的队头元素为:"<<q.front()<<endl;
	cout<<"入队后的队尾元素为:"<<q.back()<<endl; 
	
	//判断队列是否为空 
	if(q.empty()){
		cout<<"队列为空!"<<endl;
	}else{
		cout<<"队列不为空!"<<endl; 
	}
	
	//将0-5出队 
	for(int i=0;i<6;i++){
		q.pop();	
	}
	cout<<"将0-5出队后的队头元素为:"<<q.front()<<" "<<endl;

	cout<<"此时队列中元素的个数为:"<<q.size()<<endl; 
	return 0;
}

 以上就是C语言和C++中队列的基本用法了,如果你觉得我的文章对你有用请点个赞支持一下吧,如果喜欢我写的文章那么请点个关注再走呦。如果此文章有错误或者有不同的见解欢迎评论或者私信。
怀疑
我是ACfun:一个成长中的程序猿,感谢大家的支持。

发布了85 篇原创文章 · 获赞 203 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41575507/article/details/105650259
今日推荐