queue(队列)总结


queue

队列是一种特殊的线性表,是一种先进先出(FIFO)的数据结构。它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列

一.队列简介

在队列这种数据结构中,最先插入在元素将是最先被删除;反之最后插入的元素将最后被删除,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。
队列空的条件:front=rear
队列满的条件: rear = MAXSIZE
队列可以用数组Q[1…m]来存储,数组的上界m即是队列所容许的最大容量。在队列的运算中需设两个指针:head,队头指针,指向实际队头元素的前一个位置;tail,队尾指针,指向实际队尾元素所在的位置。一般情况下,两个指针的初值设为0,这时队列为空,没有元素。图1 ( a)画出了一个由6个元素构成的队列,数组定义Q[1…10]。Q(i) i=3,4,5,6,7,8头指针head=2,尾指针tail=8。队列中拥有的元素个数为:L=tail-head现要让排头的元素出队,则需将头指针加1。即head=head+1这时头指针向上移动一个位置,指向Q(3),表示Q(3)已出队。见图1 (b)。如果想让一个新元素入队,则需尾指针向上移动一个位置。即tail=tail+1这时Q(9)入队。当队尾已经处理在最上面时,即tail=10,如果还要执行入队操作,则要发生"上溢",但实际上队列中还有三个空位置,所以这种溢出称为"假溢出"。
克服假溢出的方法有两种。一种是将队列中的所有元素均向低地址区移动,显然这种方法是很浪费时间的;另一种方法是将数组存储区看成是一个首尾相接的环形区域。当存放到n地址后,下一个地址就"翻转"为1。在结构上采用这种技巧来存储的队列称为循环队列。
队列和栈一样只允许在端点(前端或者后端)处插入和删除元素。
循环队的入队算法如下:
1、tail=tail+1;
2、若tail=n+1,则tail=1;
3、若head=tail尾指针与头指针重合了,表示元素已装满队列,则作上溢出错处理;
4、否则,Q(tail)=X,结束(X为新入出元素)。
队列和栈一样,有着非常广泛的应用

二.队列的特性

1.特点

(1)队列中的数据元素遵循“先进先出”(First In First Out)的原则,简称FIFO结构;

(2)在队尾添加元素,在队头删除元素。
(3)queue 也没有迭代器。访问元素的唯一方式是遍历容器内容,并移除访问过的每一个元素

2.相关概念

(1)队头与队尾: 允许元素插入的一端称为队尾,允许元素删除的一端称为队头;

(2)入队:队列的插入操作;

(3)出队:队列的删除操作。

二.基本函数实现

使用前添加#include<queue>
在这里插入图片描述

1.构造函数

  • queue<T> q;创建一个空队列,名称为q,T为数据类型,后面会有代码介绍
#include<queue>

queue<int> q;

queue<char> q;

queue<pair<int,int> > q;

queue<node> q;

struct node{...};

2.添加函数

  • push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
  • push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
  • q.push(x);将元素压到队列的末端,即队尾

3.删除函数

  • q.pop();弹出队列的第一个元素,并不会返回元素的值

4.判断函数

  • q.empty()如果队列为空返回true,否则返回false

5.大小函数

  • q.size();返回队列中的元素个数

6.其他函数

  • q.front();返回队头的元素值,但不删除该元素
  • q.back();返回队列尾元素的值,但不删除该元素
  • emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
  • swap(queue<T> &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。

7.总结

  • front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
  • push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
    pop():删除 queue 中的第一个元素。
    size():返回 queue 中元素的个数。
    empty():如果 queue 中没有元素的话,返回 true。
    emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
  • swap(queue &other_q):将当前 queue 中的元素和参数 queue
    中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。
用法作用
q.front(),q.back()返回queue的首、尾元素
q.push()从queue末尾加入一个元素
q.size()返回queue当前的长度(大小)
q.pop()从queue末尾删除一个元素
q.empty()返回queue是否为空,1为空、0不为空

注意,虽然vector和queue是两种最基本的STL容器,但请记住它们两个不是完全一样的。就从使用方法来讲:
queue不支持随机访问,即不能像数组一样地任意取值。并且,queue并不支持全部的vector的内置函数。比如queue不可以用clear()函数清空,清空queue必须一个一个弹出。同样,queue也并不支持遍历,无论是数组型遍历还是迭代器型遍历统统不支持,所以没有begin(),end();函数,使用的时候一定要清楚异同!

代码介绍

#include<iostream>
#include <queue>
#include<string> 

//创建队列对象
//格式:std::queue<数据类型> 对象名;

int main() {
    std::queue<std::string> q;  //创建队列对象 
    std::string str;
    str = "李明";
    q.push(str);   //在末尾加入一个元素
    bool b = q.empty();  //判断是否为空
    //如果queue中没有元素的话,返回 true
    std::string str1,str2;
    str1 = q.front();  //返回队首元素,但不删除
    str2= q.back();   //返回队尾元素,但不删除
    q.pop();  //删除队首元素
    q.push(str);
    str = "张三";
    q.push(str);
    str = "李四";
    q.push(str);
    str = "王五";
    q.push(str);
    int n = q.size();  //返回 queue 中元素的个数


    std::cout << n << std::endl;

}

二.两种队列实现

具体图片介绍可参考 传送门
在这里插入图片描述

链式队列的C++实现

1.数据结构

struct QNode    //定义队列结点的数据结构
{
	QNode *next; //指针域,指向下一个结点
	double data;    //数据域,存储队列信息
};

struct LinkQueue    //定义队列的数据结构
{
	QNode *front;      //队首指针,指向QNode类型的指针
	QNode *rear;       //队尾指针
};
void InitQueue(LinkQueue &Q)     //构造一个空的队列
int IsEmpty(LinkQueue &Q)    //判断队列是否为空
void EnQueue(LinkQueue &Q,double e)     //从队列尾部插入一个结点
void DeQueue(LinkQueue &Q, double &e)   //从队列首部删除一个结点
void DestoryQueue(LinkQueue &Q)       //销毁一个队列

2.完整代码

#include "stdafx.h"
 
#include <iostream>
#include <cstdlib>
using namespace std;
 
struct QNode    //定义队列结点的数据结构
{
	QNode *next; //指针域,指向下一个结点
	double data;    //数据域,存储队列信息
};
 
struct LinkQueue    //定义队列的数据结构
{
	QNode *front;      //队首指针,指向QNode类型的指针
	QNode *rear;       //队尾指针
};
 
void InitQueue(LinkQueue &Q)     //构造一个空的队列
{
	QNode *q;
	q = new QNode;    //申请一个结点的空间
	q->next = NULL;   //当作头结点
	//队首与队尾指针都指向这个结点,指针域为NULL
	Q.front = q;
	Q.rear = q;
}
 
int IsEmpty(LinkQueue &Q)    //判断队列是否为空
{
	if (Q.rear == Q.front)
		return 0;
	else
		return 1;
}
 
void EnQueue(LinkQueue &Q,double e)     //从队列尾部插入元素
{
	QNode *p;    //新创建一个结点
	p = new QNode;
	p->next = NULL;
	p->data = e;  //输入数据信息
	//将新结点插入队列尾部
	Q.rear->next = p;     
	Q.rear = p;       //设置新的尾结点
}
 
void DeQueue(LinkQueue &Q, double &e)   //从队列首部删除一个结点
{
	QNode *p;
	p = Q.front->next;
	e = p->data;    //保存要出队列的数据
	Q.front->next = p->next;       //将下一个结点当作头结点后面链接的第一个结点
	if (Q.rear == p)    //如果要删除的元素即为尾结点,则将头指针赋予尾指针,一同指向头结点,表示队列为空
		Q.rear = Q.front;
	delete p;
}
 
void DestoryQueue(LinkQueue &Q)       //销毁一个队列
{
	while (Q.front)
	{
		Q.rear = Q.front;    //从头节点开始,一个一个删除队列结点,释放空间
		delete Q.front;
		Q.front = Q.rear;
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
	LinkQueue *Q;  //定义一个队列Q
	Q = new LinkQueue;  
	InitQueue(*Q);
	cout << "开始往队列里输入数据,以-1作为结束符" << endl;
	cout << "请输入一个数:" << endl;
	double a, x;
	cin >> a;
	while (a != -1)
	{
		EnQueue(*Q, a);
		cout << "请输入一个数:" << endl;
		cin >> a;
	}
	//输出队列元素,队首->队尾
		QNode *p;
		p = Q->front->next;
		if (p == NULL)     //如果为空表,直接退出
		{
			cout << "队列为空!" << endl;
			return 0;
		}
		cout << "队列数据依次为:" << endl;
		while (p!=NULL)
		{
			cout << p->data << " ";
			p = p->next;
		}
		cout << endl;
	//删除队列元素
	while (!IsEmpty(*Q))
	{
		DeQueue(*Q, x);
		cout << x << " ";
	}
	//释放内存空间
	delete Q->front;
	delete Q;
	return 0;
}

3.实验截图

在这里插入图片描述

循环队列的实现

判断循环队列是“空”还是“满”,有以下两种处理方法:
1》设置状态标志位以区别空还是满
2》少用一个元素,约定“队头front在队尾rear的下一个位置(指的是环的下一个位置)”作为“满”的标志

注意以下几点,循环队列迎刃而解: 1》求元素的个数:(rear - front + MAXSIZE) % MAXSIZE
2》front/rear指向逻辑的下一个空间 front =(front+1)%MAXSIZE rear =
(rear+1)%MAXSIZE 3》判空:front == rear 4》判满:(rear+1+MAXSZIE) == front

1. 定义数据结构和类

# include <iostream>
using namespace std;
 
#define MAX_QUEUE_SIZE 100
typedef int ElemType;
 
typedef struct QNode
{
	ElemType data;
	QNode *next;
 
}QNode, *QueuePtr;      //节点
 
//循环队列
 typedef struct{
	ElemType *base;
	int front;
	int rear;
}SqQueue;
 
class CircularQueue
 {
 public:
	 void InitQueue();			//初始化队列
	 void DestroyQueue();		//销毁队列
	 void ClearQueue();			//清空队列
	 bool QueueEmpty();			//队列是否为空
	 int QueueLength();			//队列长度
	 void Enqueue(ElemType val);	//在队尾插入数据
	 void DeQueue(ElemType & val);	//删除队头
	 void Print();					//从头到尾打印
 
 private:
	 SqQueue q;
 };
 

2.具体实现

#include "Queue.h"
 
//初始化队列
void CircularQueue::InitQueue()
{
	q.base = (ElemType *)malloc(sizeof(ElemType) * MAX_QUEUE_SIZE);
	if(!q.base)
	{
		//如果分配失败
		cout <<"初始化失败"<<endl;
		return;
	}
	q.front = q.rear = 0;
}	
 
//销毁队列
void CircularQueue::DestroyQueue()
{
	free (q.base);
	q.front = q.rear = 0;
}
//在队尾插入数据
void CircularQueue::Enqueue(ElemType val)
{
	if((q.rear + 1)%MAX_QUEUE_SIZE == q.front)
	{
		cout << "队列已满!" << endl;
		return;
	}
	q.base[q.rear] = val;
	q.rear = (q.rear+1)%MAX_QUEUE_SIZE;
	return;
}
//删除队头,并返回当前队头的值
void CircularQueue::DeQueue(ElemType & val)
{
	if(q.front == q.rear)
	{
		cout<<"队列为空!"<<endl;
		return;
	}
	val = q.base[q.front];
	q.front = (q.front+1)%MAX_QUEUE_SIZE;
	return;
}
 
//打印
void CircularQueue::Print()
{
	if(q.front == q.rear)
	{
		cout<< "队列为空" << endl;
		return;
	}
	for(int i = q.front; i < q.rear;i++)
		cout<< q.base[i]<<endl;
	return;
}
 
//清空队列
void CircularQueue::ClearQueue()
{
	DestroyQueue();
	InitQueue();
}
 
 
//队列是否为空
bool CircularQueue::QueueEmpty()
{
	if(q.front == q.rear)
		return true;
	else
		return false;
}
//队列长度
int CircularQueue:: QueueLength()
{
	return (q.rear - q.front + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE;
}
  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值