3.4队列
3.4.1抽象数据类型队列的定义
与栈相反,队列是一种先进先出的线性表。它只允许在表的一端插入,另一端删除元素。类似于日常生活中的排队,最先进入队列的元素最先离开。在队列中,允许插入的一端叫队尾,允许删除的一端叫做队头。
队列在程序设计中经常出现,最典型的例子就是操作系统中的作业排队。
3.4.2链队列--队列的链式表示和实现
和线性表类似,队列也有两种存储表示
用链表表示的队列简称为链队列,如图3.10。一个链队列需要两个分别指示队头(头指针)和队尾(尾指针)才能唯一确定。为了方便操作也添加一个头结点,并令头指针指向头结点。所以,空的链队列的判决条件为头指针和尾指针均指向头结点。如图3.11(a)。
链队列基本操作:
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef int QElemType;
#define ERROR 0;
#define OK 1;
//#define OVERFLOW -2;
// 结点定义
typedef struct QNode {
QElemType data; // 数据域
struct QNode* next; // 指针域
}QNode,*QueuePtr;
// 链队列定义
typedef struct {
QueuePtr front; // 队列的头指针
QueuePtr rear; // 队列的尾指针
int length;
}Queue; // 链队列
//始化链队列为空队列
bool InitQueue(Queue& Q) {
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front)//内存分配失败
{
return ERROR;
}
Q.front->next = NULL;//头指针指针域为空
Q.length = 0;//链队列当前长度为0
return OK;
}
//新元素e入队列,成功返回true,失败返回false
bool EnQueue(Queue& Q, QElemType e) {
QNode* p;
p = (QueuePtr)malloc(sizeof(QNode));//新建一个结点P
if (!p) exit(OVERFLOW);//分配失败返回
p->data = e;//数据域为输入的e
p->next = NULL;//指针域为空
Q.rear->next = p;//插入队尾
Q.rear = p;//队尾指针重新指向队尾
return OK;
}
//出队列,即删除队首元素,并用e返回出元素值 ,成功返回true,失败返回false
bool DeQueue(Queue& Q, QElemType& e) {
if (Q.front == Q.rear) return ERROR;//如果是空队列,返回错误值
QNode* p;//新建指针P
p = Q.front->next;//指针p指向队头元素准备删除
e = p->data;//队头元素赋值给e
Q.front->next = p->next;//对头指针向下移动一位
if (Q.rear == p) Q.rear = Q.front;//如果队尾指针等于P,空队。队尾指针重新指向对头指针,如果不把队尾指针指向对头指针,队尾指针会丢失
free(p);
return OK;
}
//读队首元素,用e返回队首元素,不出队 ,成功返回true,失败返回false
bool GetHead(Queue& Q, QElemType& e) {
QNode* p;//指针p
if (Q.front == Q.rear) return ERROR;//空队错误
p = Q.front->next;
e = p->data;
return OK;
}
//遍历链队列,将队列数据元素依次输出
void Traverse(Queue Q) {
QNode* p;
p = Q.front->next;
while (p) {
cout << p->data << " ";
p = p->next;
}
}
//销毁链队列
void Destroy(Queue& Q) {
while (Q.front) {//只要头指针存在
Q.rear = Q.front->next;
free(Q.front);
Q.front = Q.rear;
}
}
int main(void)
{
Queue Lque;
Queue Q;
QElemType e;
int n;
int c = 0;
while (c != 7)
{
cout << endl << "1. 初始化链队";
cout << endl << "2. 入队";
cout << endl << "3. 出队";
cout << endl << "4. 读队首";
cout << endl << "5. 遍历队列";
cout << endl << "6. 销毁队列";
cout << endl << "7. 退出";
cout << endl << "选择功能(1~7):";
cin >> c;
switch (c)
{
case 1:
{
InitQueue(Q);
cout << "初始化链队成功!" << endl;
break;
}
case 2:
{
cout << "请输入要入队的元素个数:" << endl;
cin >> n;
for (int i = 1; i <= n; i++) {
cout << "请输入要入队的元素:" << endl;
cin >> e;
EnQueue(Q, e);
}
cout << "入队的元素为:" << endl;
Traverse(Q);
break;
}
case 3:
{
cout << "出队成功!" << endl;
DeQueue(Q, e);
break;
}
case 4:
{
GetHead(Q, e);
cout << "队首元素为:" << e << endl;
break;
}
case 5:
{
cout << "队列元素为:" << endl;
Traverse(Q);
break;
}
case 6:
{
Destroy(Q);
cout << "销毁成功!" << endl;
break;
}
case 7:
{
cout << "感谢您的使用!" << endl;
break;
}
}
}
}
3.4.3循环队列--队列的顺序表示和实现
和顺序栈类似,除了使用一组地址连续的存储单元,还需要附设两个指针front和rear分别指示队列头元素和队列尾元素的位置。非空队列中,头指针始终指向对头元素,尾指针始终指向队尾元素下一个位置。如图。
当处于d状态时,已经不可以再继续插入新的队尾元素,又不宜类似栈一样扩大数组空间,一个巧妙地办法是臆造一个环状空间,如图3.13,称之为循环队列。指针和队列元素关系不变,但是此时判断空/满条件均为Q.front=Q.rear。有两种处理方法:1)另外设置一个标志位区域区别空或满; 2)少用一个元素空间,约定以“队列头指针在队列尾指针的下一位置(环的下一位置)”作为队列“满”的标志。
如果无法预估最大队列长度,最好采用链队列
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef int QElemType;
#define ERROR 0;
#define OK 1;
#define MAXQSIZE 100
//循环队列定义
typedef struct {
QElemType* base; // 存储空间基址
int rear; // 队尾指针
int front; // 队头指针
int queuesize; // 允许的最大存储空间
} SqQueue;
//初始化长度为MAXQSIZE的空队列
bool InitQueue(SqQueue& Q) {
//用malloc申请一块内存,大小是MAXQSIZE*sizeof(ElemType)。
//将此函数返回的内存地址强制转换成QElemtype类型的指针,即QElemType*
Q.base = (QElemType*)malloc(MAXQSIZE * sizeof(QElemType));
if (!Q.base)
return ERROR;
Q.front = Q.rear = 0; return OK;
return OK;
}
//新元素e入队列,成功返回true,失败返回false
bool EnQueue(SqQueue& Q, QElemType e) {
if ((Q.rear + 1) % MAXQSIZE == Q.front)
return ERROR;
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXQSIZE;
return OK;
}
//出队列,即删除队首元素,并用e返回出元素值 ,成功返回true,失败返回false
bool DeQueue(SqQueue& Q, QElemType& e) {
if (Q.front == Q.rear) return ERROR;//空,返回错误
e = Q.base[Q.front];
Q.front = (Q.front + 1) % MAXQSIZE;
return OK;
}
//读队首元素,用e返回队首元素,不出队 ,成功返回true,失败返回false
bool GetHead(SqQueue& Q, QElemType& e) {
if (Q.front == Q.rear) return ERROR;
e = Q.base[Q.front];
return OK;
}
//遍历队列
bool Traverse(SqQueue Q) {
int i;
if (Q.front == Q.rear) return ERROR;
for (i = Q.front; i != Q.rear; i = (i + 1) % MAXQSIZE)
cout << Q.base[i] << endl;
return OK;
}
//销毁循环队列
bool Destroy(SqQueue Q) {
if (Q.base) free(Q.base);
Q.base = NULL;
Q.front = Q.rear = 0;
return OK;
}
int main(void)
{
SqQueue Sque;
QElemType e;
SqQueue Q;
int n;
int c = 0;
while (c != 7)
{
cout << endl << "1. 初始化循环队列";
cout << endl << "2. 入队";
cout << endl << "3. 出队";
cout << endl << "4. 读队首";
cout << endl << "5. 遍历队列";
cout << endl << "6. 销毁循环队列";
cout << endl << "7. 退出";
cout << endl << "选择功能(1~7):";
cin >> c;
switch (c)
{
case 1:
{
InitQueue(Q);
cout << "初始化循环队列成功!" << endl;
break;
}
case 2:
{
cout << "请输入要入队的元素个数:" << endl;
cin >> n;
for (int i = 1; i <= n; i++) {
cout << "请输入要入队的元素:" << endl;
cin >> e;
EnQueue(Q, e);
}
cout << "入队的元素为:" << endl;
Traverse(Q);
break;
}
case 3:
{
cout << "出队成功!" << endl;
DeQueue(Q, e);
break;
}
case 4:
{
GetHead(Q, e);
cout << "队首元素为:" << e << endl;
break;
}
case 5:
{
cout << "队列元素为:" << endl;
Traverse(Q);
break;
}
case 6:
{
Destroy(Q);
cout << "销毁成功!" << endl;
break;
}
case 7:
{
cout << "感谢您的使用!" << endl;
break;
}
}
}
}