队列是一种特殊的线性表,是一种先进先出(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)队列中的数据元素遵循“先进先出”(First In First Out)的原则,简称FIFO结构;
(2)在队尾添加元素,在队头删除元素。
(3)queue 也没有迭代器。访问元素的唯一方式是遍历容器内容,并移除访问过的每一个元素
(1)队头与队尾: 允许元素插入的一端称为队尾,允许元素删除的一端称为队头;
(2)入队:队列的插入操作;
(3)出队:队列的删除操作。
queue q
;创建一个空队列,名称为q,T为数据类型,后面会有代码介绍#include
queue<int> q;
queue<char> q;
queue<pair<int,int> > q;
queue<node> q;
struct node{...};
push(const T& obj)
:在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。push(T&& obj)
:以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。q.push(x)
;将元素压到队列的末端,即队尾q.pop()
;弹出队列的第一个元素,并不会返回元素的值q.empty()
如果队列为空返回true,否则返回falseq.size()
;返回队列中的元素个数q.front()
;返回队头的元素值,但不删除该元素q.back()
;返回队列尾元素的值,但不删除该元素emplace()
:用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。swap(queue &other_q)
:将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。
- 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
#include
#include
//创建队列对象
//格式: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;
}
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) //销毁一个队列
#include "stdafx.h"
#include
#include
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;
}
判断循环队列是“空”还是“满”,有以下两种处理方法:
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
# include
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;
};
#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;
}