中午在食堂打饭,真是一个使人头疼的事情,去食堂的路上也老是步伐匆匆,为何啊,这还用说,迟一点去,你就会知道什么叫作人山人海了,在食堂排队的时候,相比较学生来讲,打饭阿姨毕竟是少数,在每一个窗口都有人的时候,难免咱们就得等待,直到前面的一个学生打完饭离开,后面排队的人才能够继续向前走,直到轮到本身,别提多费劲了,可是秩序和规则倒是咱们每一个人都应该遵照的,也只能抱怨本身来的迟了前端
这种 “先进先出” 的例子就是咱们所讲的基本数据结构之一 ”队列“node
例子补充:用电脑的时候,有时候机器会处于疑似死机的状态, 鼠标点什么彷佛都没有用,双击任何快捷方式都不动,就当你失去耐心,打算reset的时候,忽然它就像酒醒了同样,把你刚才点击的全部操做所有按照顺序执行了一遍,这实际上是由于操做系统中的多个程序隐须要经过一个通道输出,而按照前后次序排队等待形成的 ——《大话数据结构》ios
定义:队列是一种只容许在一段进行删除操做,在另外一端进行插入操做的线性表c++
容许插入的一段称做队尾 (rear),容许删除的的一端称为队头 (front)数组
队列的数据元素又叫作队列元素,在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队 ,也正是由于队列只容许在一段插入,另外一端删除,因此这也就是咱们前面例子中体现出来的先进先出 (FIFO-first in first out) 的概念微信
补充:除此以外,还有的队列叫作双端队列,也就是能够在表的两边进行插入和删除操做的线性表数据结构
双端队列分类:函数
输出受限的双端队列:删除操做限制在表的一段进行,而插入操做容许早表的两端进行性能
插入操做限制在表的一段进行,而删除操做容许在表的两端进行ui
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include <exception>
using namespace std;
// 用于检查范围的有效性
class outOfRange:public exception {
public:
const char* what()const throw() {
return "ERROR! OUT OF RANGE.\n";
}
};
// 用于检查长度的有效性
class badSize:public exception {
public:
const char* what()const throw() {
return "ERROR! BAD SIZE.\n";
}
};
template <class T> class Queue {
public:
//判队空
virtual bool empty() const = 0;
//清空队列
virtual void clear() = 0;
//求队列长度
virtual int size() const = 0;
//入队
virtual void enQueue(const T &x) = 0;
//出队
virtual T deQueue() = 0;
//读队头元素
virtual T getHead() const = 0;
//虚析构函数
virtual ~Queue(){}
};
#endif
复制代码
队列做为一个特殊的线性表,天然也有着顺序以及链式存储两种方式,咱们先来看看它的顺序存储方式——循环队列
在队列的顺序存储中,咱们除了建立一个具备必定空间的数组空间外,还须要两个指针,分别指向队列的前端和微端,下面的代码中,咱们选择将队头指针指向头元素的前一个位置,队尾指针指向队尾元素(固然这不是惟一的方式,还能够将头指针指向头元素,队尾指针指向队尾元素的后一个位置,原理是基本一致的)
为何要这么作,而且为何这种存储咱们叫作循环队列?
咱们一步步分析一下:
咱们先按照咱们通常的想法画出队列元素进出队的过程,例如队列元素出队
这样的设想,也就是根据咱们前面食堂排队的例子画出来的,可是咱们能够清晰的看到,当a0出队后,a0后的元素所有须要前移,将空位补上,但咱们在计算机中讲究性能二字,如何能够提升出队的性能呢?
循环队列就这样被设计出来了,咱们若是再也不限制队头必定在整个空间的最前面,咱们的元素也就不须要集体移动了
这个时候咱们就须要考虑这样的问题了:
① 如何为了解决只有一个元素的时候,队头和队尾重合使得处理变得麻烦?
可是有一个大问题出现了 !
若是前面有空闲的空间还好说,一旦头元素前面没有空间,咱们的队头指针就指向到了数组以外,也就会出现数组越界问题,这该怎么办呢?
咱们能够看到,虽然咱们的表头已经没有了任何空间,可是表的后半部分还有空余空间,这种现象咱们称做假溢出,打个比方,接近上课你缓缓走进教室,看到只有前排剩下了两个位置,你总不会转身就走吧,固然能够去前排坐,只有实在没座位了,才考虑离开
咱们能够作出这样一种比较可行的方案
咱们刚才也提到了,当表头指针和表尾指针相等的时候就解决了空队列的状况,可是在表满的状况下,你会发现,一样也知足表头表尾指针相等,那么又如何解决这个问题呢?(咱们给出三种可行的解决方案)
咱们重点讲解 C 中的方法
咱们根据这种方法能够总结出几个条件的运算式
队列为满的条件:(rear+1) % MaxSize == front
队列为空的条件:front == rear
队列中元素的个数:(rear- front + maxSize) % MaxSize
入队:rear = (rear + 1) % maxSize
出队:front = (front + 1) % maxSize
#ifndef _SEQQUEUE_H_
#define _SEQQUEUE_H_
#include "Queue.h"
template <class T> class seqQueue:public Queue<T> {
private:
//指向存放元素的数组
T &data;
//队列的大小
int maxSize;
//定义队头和队尾指针
int front, rear;
//扩大队列空间
void resize();
public:
seqQueue(int initSize = 100);
~seqQueue() {delete []data;}
//清空队列
void clear() {front = rear = -1;}
//判空
bool empty() const {return front == rear;}
//判满
bool full() const {return (rear + 1) % maxSize == front;}
//队列长度
int size() const {(rear- front + maxSize) % maxSize;}
//入队
void enQueue(const T &x);
//出队
T deQueue();
//取队首元素
T getHead() const;
};
#endif
复制代码
template <class T> seqQueue<T>::seqQueue(int initSize) {
if (initSize <= 0) throw badSize();
data = new T[initSize];
maxSize = initSize;
front = rear = -1;
}
复制代码
template <class T> void seqQueue<T>::enQueue(const T &x) {
//队满则扩容
if ((rear + 1) % maxSize == front) resize();
//移动队尾指针
rear = (rear + 1) % maxSize;
//x 入队
data[rear] = x;
}
复制代码
template <class T> T seqQueue<T>::deQueue() {
//队列为空则抛出异常
if (empty()) throw outOfRange();
//移动队尾指针
front = (front + 1) % maxSize;
//x入队
return data[front];
}
复制代码
template <class T> T seqQueue<T>::getHead() const {
if (empty()) throw outOfRange();
//返回队首元素,不移动队首指针
return data[(front + 1) % maxSize];
}
复制代码
template <class T> void seqQueue<T>::resize() {
T *p = data;
data = new T[2 *maxSize];
for(int i = 1; i < size(); ++i)
//复制元素
data[i] = p[(front + i) % maxSize];
//设置队首和队尾指针
front = 0; rear = size();
maxSize *= 2;
delete p;
}
复制代码
用链式存储结构表示队列咱们叫作链队列,用无头结点的单链表表示队列,表头为队头,表尾为队尾,须要两个指针分别指向队头元素和队尾元素,这种存储结构的好处之一就是不会出现队列满的状况
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
#include <iostream>
#include "Queue.h"
template <class T> class linkQueue:public Queue<T> {
private:
struct node {
T data;
node *next;
node (const T &x, node *N = NULL) {
data = x;
next = N;
}
node ():next(NULL){}
~node () {}
};
node *front, *rear;
public:
linkQueue(){front = rear = NULL;};
~linkQueue() {clear();}
//清空队列
void clear();
//判空
bool empty() const {return front == NULL;}
//队列长度
int size() const;
//入队
void enQueue(const T &x);
//出队
T deQueue();
//取队首元素
T getHead() const;
};
#endif
复制代码
template <class T> void linkQueue<T>::clear() {
node *p;
//释放队列中全部节点
while(front != NULL) {
p = front;
front = front -> next;
delete p;
}
//修改尾指针
rear = NULL;
}
复制代码
template <class T> int linkQueue<T>::size() const {
node *p = front;
int count = 0;
while(p) {
count++;
p = p -> next;
}
return count;
}
复制代码
template <class T> void linkQueue<T>::enQueue(const T &x) {
if(rear == NULL)
front = rear = new node(x);
else {
rear -> next = new node(x);
rear = rear -> next;
}
}
复制代码
template <class T> T linkQueue<T>::deQueue() {
//队列为空则抛出异常
if (empty()) throw outOfRange();
node *p = front;
//保存队首元素
T value = front -> data;
front = front -> next;
if (front == NULL)
rear = NULL;
delete p;
return value;
}
复制代码
template <class T> T linkQueue<T>::getHead() const {
if (empty()) throw outOfRange();
return front -> data;
}
复制代码
若是文章中有什么不足,或者错误的地方,欢迎你们留言分享想法,感谢朋友们的支持!
若是能帮到你的话,那就来关注我吧!若是您更喜欢微信文章的阅读方式,能够关注个人公众号
在这里的咱们素不相识,却都在为了本身的梦而努力 ❤
一个坚持推送原创开发技术文章的公众号:理想二旬不止