队列

---------------------siwuxie095

   

   

   

   

   

   

   

   

   

队列

   

   

所谓 队列,从特点上来讲,是一个先入先出的数学模型,

First In First Out,简称为 FIFO

   

   

   

   

如:在排队买票时,先来的,就排在队列的前面,后来的,

就排在队列的后面,于是就形成了队列

   

   

   

队列排在最前面的称之为 队头,队列排在最后面的称之为 队尾

也叫 队列头 队列尾

   

最关键的是,售票员一定是从队列头开始卖票的

   

   

   

当买票的人买到票之后,就会逐个的离开这个队列

   

   

   

显然,先到的人一定是先买到票的,然后离开,后到的人一定是

后买到票的,然后离开,也即 先入先出,这个模型在数据结构上

就称之为 队列

   

   

   

从队列的形式上来说,分为两种:一种是普通队列,一种是环形队列

   

   

   

   

   

   

   

   

普通队列

   

   

假如左方是售票员所在的窗口,右方是排队买票的人的队列:

   

   

   

当只有一个人买票时,他肯定排在最前面,这时,他既是 队列头,

也是 队列尾

   

当再来人买票时,就排在前面那个人的后面,再来人再来人

   

一直排总之,在队列的最后一个就是 队列尾

   

显然,队列的长度肯定是有一个限度的,如:排到一定程度就没法

排队了,对应到计算机中,就是排到一定程度,后面的内存就无法

再分配了

   

   

对于处理来说:

   

前面的售票员肯定是会给第一个来排队的人卖票,卖给他票之后,

他就离开了,于是给第二个人卖票,他拿到票之后,也离开了

   

   

   

于是就产生了两种情况:

   

1)在队列头的那个人买完票离开后,后面的人往前依次进一位,

第二个人就变成了队列头,他买完票离开后,所有人再往前进一

   

2)售票员拿着票,给第一个位置上的人卖票,卖给他票之后,他

离开了,第二个位置上的人就是队列头。售票员走动,卖票给第二

个位置上的人之后,他也离开了,售票员接着走动,卖票给第三个

位置上的人售票员一直走到队列尾,最后一个人拿票离开了

此时,如果还有人过来排队,排的就是后面的位置

   

   

   

对于普通队列来说,存在着这样一些缺点

   

如果是第二种情况,就显得浪费了前面的位置,因为这些位置一旦

处理完之后,人一离开,剩下的人都只能往后排,前面的位置就被

浪费了,对应到计算机中,这些位置就是一个个的内存,即 这些内

存就被浪费了

   

如果是第一种情况,虽然内存没有浪费,每次处理完一个,后面的

都需要往前移动一位,处理起来效率就会低,速度就会慢

   

   

如果想要将普通队列的缺点弥补,怎么办呢?就可以采用 环形队列

   

   

   

   

   

   

   

环形队列

   

   

所谓 环形队列,即 队列成一个环,它的好处就是屏蔽掉了

普通队列的缺点,如下:

   

   

   

   

对于环形队列来说,它仍然是有一个 队列头 和一个 队列尾

   

当队列中只有一个元素时,它既是 队列头,也是 队列尾

   

当再有元素进来,新元素就是 队列尾,再有元素进来

   

   

   

对于环形队列是有 顺时针 逆时针 之分的,上图中采用的是

顺时针

   

当顺时针去看这个结构时,发现:对于整个队列空间来说,还

有两个空间没被占据,如果再有两个元素把两个空间都占满了,

那么队列头和队列尾就顶到了一起,此时,如果再想排队的元

素就没法进来了

   

显然,环形队列中的所有内存,在使用上是非常高效的,而且

在处理时,对于队列头,处理一个,队列头就变到了第二个位

置,再处理一个,队列头就变到了第三个位置如果队列尾后

再有元素来排队,就可以使用前面腾开的位置往后排

   

可见:使用环形队列是可以充分的利用每一个内存空间的

   

   

   

   

   

   

   

队列的用途

   

   

从现实生活中来说,队列的用途是非常广泛的,最为典型的

就是 自动排号机

   

   

   

大家到银行、移动营业厅等地方都能遇到,假设我们去打一个单子,

其中:编号 XXX 肯定就是指你是今天的第几位,用户类型 表明

你是普通队,还是 VIP 队,取号时间 即 你什么时间取的号 …

   

   

   

   

   

   

程序 1:

   

MyQueue.h:

   

#ifndef MYQUEUE_H

#define MYQUEUE_H

   

//环形队列

class MyQueue

{

public:

MyQueue(int queueCapacity); //创建队列

virtual ~MyQueue(); //销毁队列

void ClearQueue(); //清空队列

bool QueueEmpty() const; //判空队列

bool QueueFull() const; //判满队列

int QueueLength() const; //队列长度

bool EnQueue(int element); //新元素入队

bool DeQueue(int &element); //首元素出队

void QueueTraverse(); //遍历队列

   

private:

int *m_pQueue; //队列数组指针

int m_iQueueLen; //队列元素个数队列长度

int m_iQueueCapacity; //队列数组容量

int m_iHead; //队头

int m_iTail; //队尾

};

   

//使用 C++ 实现队列和使用 C 语言实现队列,略有不同

//但思想上是大同小异的

   

#endif

   

   

   

MyQueue.cpp:

   

#include "MyQueue.h"

#include "stdlib.h"

#include <iostream>

using namespace std;

   

   

//构造函数,创建一个队列

MyQueue::MyQueue(int queueCapacity)

{

//队列容量

m_iQueueCapacity = queueCapacity;

//初始化时,队头和队尾都为 0

m_iHead = 0;

m_iTail = 0;

//初始化时,队列元素个数队列长度为 0

m_iQueueLen = 0;

//用数组来存放该队列,从堆中申请内存有可能失败,暂不做处理

m_pQueue = new int[m_iQueueCapacity];

}

   

   

//析构函数,销毁整个队列

MyQueue::~MyQueue()

{

//回收内存

delete[]m_pQueue;

//将指针置于安全状态

m_pQueue = NULL;

}

   

   

//清空队列,即让队列还原成初始状态,

//也就是执行构造函数时创建队列的那个样子,

//只不过清空队列后,它的内存还保持着,元素不在了

//

//只需将队头、队尾、队列长度置为 0 即可

//不必重置容量和也不必对已经分配到的内存做相应的处理

//

//显然,ClearQueue() 中的代码和构造函数中部分重复

//可以将构造函数中相应的代码替换为 ClearQueue() 的调用

void MyQueue::ClearQueue()

{

m_iHead = 0;

m_iTail = 0;

m_iQueueLen = 0;

}

   

   

//判断队列是否为空

bool MyQueue::QueueEmpty() const

{

//通过队列长度来进行判断

if (m_iQueueLen == 0)

{

return true;

}

else

{

return false;

}

   

//或使用如下方法判断

//return m_iQueueLen == 0 ? true : false;

}

   

   

//判断队列是否为满

bool MyQueue::QueueFull() const

{

//当队列长度与队列容量相同时为满

if (m_iQueueLen == m_iQueueCapacity)

{

return true;

}

return false;

   

//或使用如下方法判断

//return m_iQueueLen == m_iQueueCapacity ? true:false;

}

   

   

//获取队列的长度

int MyQueue::QueueLength() const

{

return m_iQueueLen;

}

   

   

//新元素插入到队列中

bool MyQueue::EnQueue(int element)

{

//如果队列已满,自然就不能插入了

if (QueueFull())

{

return false;

}

//这里的 else 加不加均可

else

{

//新元素总是从队尾入队,即从队尾插入

m_pQueue[m_iTail] = element;

   

//新元素插入后,队尾移动到后一个位置

//因为队尾一直要指向要插入的那个位置

m_iTail++;

   

//m_iTail++ 后需要对队列总容量取余

//因为有可能会超出容量而报错,当队尾指向环形队列的

//最后一个位置时,下一次指向应该是第 0 个位置

//注意:这里是用数组来实现的环形队列

m_iTail = m_iTail % m_iQueueCapacity;

   

//每插入一个元素,都让队列长度加 1

m_iQueueLen++;

   

return true;

}

}

   

   

//元素出队:一个元素要想出队,肯定是从首元素开始出队的

//首元素即队头所指向的那个元素

bool MyQueue::DeQueue(int &element)

{

//如果队列为空,自然也无元素可出了

if (QueueEmpty())

{

return false;

}

//这里的 else 加不加均可

else

{

//将队头所指向的元素赋值为传入进来的参数 element

//注意:这个 element 必须是一个引用

element = m_pQueue[m_iHead];

   

//首元素移出后,队头要向后移动一位

//指向下一个位置,以便于下次正常的出队

m_iHead++;

   

//m_iHead++ 后同样要取余并赋值给本身

m_iHead = m_iHead % m_iQueueCapacity;

   

//每删除一个元素,都让队列长度减 1

m_iQueueLen--;

   

return true;

}

}

   

   

//对环形队列中的每一个元素进行遍历

void MyQueue::QueueTraverse()

{

//遍历时,让第一个元素指向队头

//m_iQueueLen + m_iHead 使得无论队头如何增长,

//在循环时都不受影响,保证循环次数不出错

for (int i = m_iHead; i < m_iQueueLen + m_iHead; i++)

{

//使用 i 队列容量取余

cout << m_pQueue[i%m_iQueueCapacity] << endl;

}

cout << endl;

}

   

   

   

main.cpp:

   

#include "stdlib.h"

#include "MyQueue.h"

#include <iostream>

using namespace std;

   

//环形队列检测

int main()

{

//初始化队列容量为 4

MyQueue *p = new MyQueue(4);

   

p->EnQueue(10);

p->EnQueue(12);

p->EnQueue(16);

p->EnQueue(18);

p->EnQueue(20);//插入 20 是失败的,因为容量是4

p->QueueTraverse();

   

int e = 0;

p->DeQueue(e);

cout << e << endl;

p->DeQueue(e);

cout << e << endl;

cout << endl;

p->QueueTraverse();

   

p->ClearQueue();

p->QueueTraverse();

   

p->EnQueue(20);

p->EnQueue(30);

p->QueueTraverse();

   

delete p;

p = NULL;

   

system("pause");

return 0;

}

   

   

运行一览:

   

   

   

   

   

   

   

程序 2:

   

Customer.h:

   

#ifndef CUSTOMER_H

#define CUSTOMER_H

   

#include <string>

using namespace std;

   

   

class Customer

{

public:

Customer(string name = "", int age = 0);

void printInfo() const;

   

private:

string m_strName;

int m_iAge;

};

   

   

#endif

   

   

   

Customer.cpp:

   

#include "Customer.h"

#include <iostream>

   

Customer::Customer(string name, int age)

{

m_strName = name;

m_iAge = age;

}

   

   

void Customer::printInfo() const

{

cout << "姓名:" << m_strName << endl;

cout << "年龄:" << m_iAge << endl;

cout << endl;

}

   

   

   

MyQueue.h:

   

#ifndef MYQUEUE_H

#define MYQUEUE_H

#include "Customer.h"

//环形队列

class MyQueue

{

public:

MyQueue(int queueCapacity); //创建队列

virtual ~MyQueue(); //销毁队列

void ClearQueue(); //清空队列

bool QueueEmpty() const; //判空队列

bool QueueFull() const; //判满队列

int QueueLength() const; //队列长度

bool EnQueue(Customer element); //新元素入队

bool DeQueue(Customer &element);//首元素出队

void QueueTraverse(); //遍历队列

   

private:

Customer *m_pQueue; //Customer类型的队列指针

int m_iQueueLen; //队列元素个数队列长度

int m_iQueueCapacity; //队列数组容量

int m_iHead; //队头

int m_iTail; //队尾

};

   

//使用 C++ 实现队列和使用 C 语言实现队列,略有不同

//但思想上是大同小异的

   

#endif

   

   

   

MyQueue.cpp:

   

#include "MyQueue.h"

#include "stdlib.h"

#include <iostream>

using namespace std;

   

   

//构造函数,创建一个队列

MyQueue::MyQueue(int queueCapacity)

{

//队列容量

m_iQueueCapacity = queueCapacity;

//初始化时,队头和队尾都为 0

m_iHead = 0;

m_iTail = 0;

//初始化时,队列元素个数队列长度为 0

m_iQueueLen = 0;

//用数组来存放该队列,从堆中申请内存有可能失败,暂不做处理

m_pQueue = new Customer[m_iQueueCapacity];

}

   

   

//析构函数,销毁整个队列

MyQueue::~MyQueue()

{

//回收内存

delete[]m_pQueue;

//将指针置于安全状态

m_pQueue = NULL;

}

   

   

//清空队列,即让队列还原成初始状态,

//也就是执行构造函数时创建队列的那个样子,

//只不过清空队列后,它的内存还保持着,元素不在了

//

//只需将队头、队尾、队列长度置为 0 即可

//不必重置容量和也不必对已经分配到的内存做相应的处理

//

//显然,ClearQueue() 中的代码和构造函数中部分重复

//可以将构造函数中相应的代码替换为 ClearQueue() 的调用

void MyQueue::ClearQueue()

{

m_iHead = 0;

m_iTail = 0;

m_iQueueLen = 0;

}

   

   

//判断队列是否为空

bool MyQueue::QueueEmpty() const

{

//通过队列长度来进行判断

if (m_iQueueLen == 0)

{

return true;

}

else

{

return false;

}

   

//或使用如下方法判断

//return m_iQueueLen == 0 ? true : false;

}

   

   

//判断队列是否为满

bool MyQueue::QueueFull() const

{

//当队列长度与队列容量相同时为满

if (m_iQueueLen == m_iQueueCapacity)

{

return true;

}

return false;

   

//或使用如下方法判断

//return m_iQueueLen == m_iQueueCapacity ? true:false;

}

   

   

//获取队列的长度

int MyQueue::QueueLength() const

{

return m_iQueueLen;

}

   

   

//新元素插入到队列中

bool MyQueue::EnQueue(Customer element)

{

//如果队列已满,自然就不能插入了

if (QueueFull())

{

return false;

}

//这里的 else 加不加均可

else

{

//新元素总是从队尾入队,即从队尾插入

m_pQueue[m_iTail] = element;

   

//新元素插入后,队尾移动到后一个位置

//因为队尾一直要指向要插入的那个位置

m_iTail++;

   

//m_iTail++ 后需要对队列总容量取余

//因为有可能会超出容量而报错,当队尾指向环形队列的

//最后一个位置时,下一次指向应该是第 0 个位置

//注意:这里是用数组来实现的环形队列

m_iTail = m_iTail % m_iQueueCapacity;

   

//每插入一个元素,都让队列长度加 1

m_iQueueLen++;

   

return true;

}

}

   

   

//元素出队:一个元素要想出队,肯定是从首元素开始出队的

//首元素即队头所指向的那个元素

bool MyQueue::DeQueue(Customer &element)

{

//如果队列为空,自然也无元素可出了

if (QueueEmpty())

{

return false;

}

//这里的 else 加不加均可

else

{

//将队头所指向的元素赋值为传入进来的参数 element

//注意:这个 element 必须是一个引用

element = m_pQueue[m_iHead];

   

//首元素移出后,队头要向后移动一位

//指向下一个位置,以便于下次正常的出队

m_iHead++;

   

//m_iHead++ 后同样要取余并赋值给本身

m_iHead = m_iHead % m_iQueueCapacity;

   

//每删除一个元素,都让队列长度减 1

m_iQueueLen--;

   

return true;

}

}

   

   

//对环形队列中的每一个元素进行遍历

void MyQueue::QueueTraverse()

{

//遍历时,让第一个元素指向队头

//m_iQueueLen + m_iHead 使得无论队头如何增长,

//在循环时都不受影响,保证循环次数不出错

for (int i = m_iHead; i < m_iQueueLen + m_iHead; i++)

{

cout << "前面还有 " << (i - m_iHead) << " " << endl;

//使用 i 队列容量取余

m_pQueue[i%m_iQueueCapacity].printInfo();

}

cout << endl;

}

   

   

   

main.cpp:

   

#include "stdlib.h"

#include "MyQueue.h"

#include <iostream>

using namespace std;

   

//队列其实是可以存放任何数据类型

//队列 MyQueue 也可以使用类模板来写

int main()

{

//初始化队列容量为 4

MyQueue *p = new MyQueue(4);

   

Customer c1("zhangsan",20);

Customer c2("lisi", 30);

Customer c3("wangwu", 24);

   

p->EnQueue(c1);

p->EnQueue(c2);

p->EnQueue(c3);

p->QueueTraverse();

   

Customer c4("", 0);

p->DeQueue(c4);

c4.printInfo();

cout << endl;

p->QueueTraverse();

   

delete p;

p = NULL;

   

system("pause");

return 0;

}

   

   

运行一览:

   

   

   

   

   

   

   

   

   

   

   

   

【made by siwuxie095】

原文地址:https://www.cnblogs.com/siwuxie095/p/6820878.html