赞
踩
观看本系列博文提醒:
你将学会队列的两种最基本的表现形式:顺序存储队列 和 链式存储队列;
一个扩展队列的使用方法:循环队列;
两个企业级队列的应用:线性池中的任务队列 和 优先链式存储队列。
队列的原理
队列是一种受限的线性表,(Queue),它是一种运算受限的线性表,先进先出(FIFO First In First Out).
例如上图中,圆球1先进,也是圆球1先出。
队列是一种受限的线性结构
由上图我们可以知道,队列中有两个“指针”,front指向队首,rear指向队尾;
至于length,他是整个队列的总长度(因为一个队列他是有固定长度的)。
顺序存储队列
采用数组来保存队列的元素,设立一个队首指针 front ,一个队尾指针 rear,分别指向队首和队尾元素。则 rear - front 即为存储的元素个数!
因为队列的长度是有限的,所以我们可以给队列的长度定义一个宏:
#define QUEUE_MAX 5 // 队列的最大存储个数
typedef int dateType; // 队列存储的类型
typedef struct arrayQueue {
dateType queue[QUEUE_MAX]; // 存储队列元素的数组
int front; // 队头“指针”
int rear; // 队尾“指针”
}seqQueue;
就是一个结构体,队列的储存是以数组方式存储的。
还定义了整型变量,front用于定位到队首元素,rear用于定位到队尾元素。
// 队列初始化
void inItQueue(seqQueue* seq) {
if (!seq) { // 合法性检查
return;
}
seq->front = seq->rear = 0; // 对头与队尾“指针”都为零
}
初始化后他是一个空队列,所以两个“指针”都得为零。
// 判断队列是否为空
bool estimateQueueEmpty(seqQueue* seq) {
if (!seq) { // 合法性检查
cout << "队列不存在!";
return false;
}
if (seq->front == seq->rear) { // 当头尾“指针”相等时,为空队列
return true;
}
return false;
}
由上图我们可以知道,当队列为空时,“指针”front和rear都是指向0的,所以我们可以以此作为判断。
// 判断队列是否已满
bool estimateQueueFull(seqQueue* seq) {
if (!seq) {
cout << "队列不存在!";
return false;
}
if (seq->rear == QUEUE_MAX) { // 当队尾“指针”等于队列最大存储个数时,为满
return true;
}
return false;
}
先提前跟大家说一下,因为顺序存储队列是以数组方式存储的,最大下标是4,他的尾“指针”rear作为下标的话都是指向最后一个元素的下一个位置的,所以我们插入元素时,可以直接使用rear作为插入位置的下标。也就是说,当队列满时,他是与我们定义的数组长度是相等的,即我们定义的宏定义#define QUEUE_MAX 5
,我们可以用rear与宏定义作比较,当他们相等时,说明队列已经满了。
// 入队,将元素插入队列中 bool enterQueue(seqQueue* seq, dateType date) { // 参数二是入队的元素 if (!seq) { cout << "队列不存在!"; return false; } if (estimateQueueFull(seq)) { cout << "队列已满!插入 " << date << " 失败!" << endl; return false; } seq->queue[seq->rear] = date; // 在队尾插入元素date seq->rear += 1; // 队尾“指针”后移一位 return true; }
就如同我们上面所说的,当需要插入一个元素时,只需要将“指针”rear作为下标插入即可,然后再将rear加一,指向下一个空白的区域(如上图)。front“指针”永远都是指向头部第一个元素的。
他有两种出队方式:
// 出队一,将队列中队首的元素出队,后面的元素向前移动 bool deleteQueueFront_1(seqQueue* seq, dateType* date) { // 参数二:保存出列的元素返回 if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return true; } // 保存出列的元素返回 *date = seq->queue[seq->front]; // 从队头元素的下一个元素开始遍历,将后续的所有元素都往前移动一个位置 for (int i = seq->front + 1; i < seq->rear; i++) { seq->queue[i - 1] = seq->queue[i]; } seq->rear -= 1; return true; }
从队头元素的下一个元素开始遍历,将后续的所有元素都往前移动一个位置。
上图就是出队前后的效果图。
可能有些朋友会有疑问,a1使出对了没错,但是为什么会有两个a3啊?
有这样的疑问是正常的。因为我们使用的删除方式是将后面的元素都往前移动一个位置,所以最后一个元素还是会保留在原来的位置的。但是,我们的尾“指针”rear最后自减了一,现在等于二,所以,就算是保留在哪里也不会出什么问题,因为我们根本访问不了他。
我们遍历队列最多只能访问前两个元素。
如果还是不懂的朋友可以把他理解成下图,就知道了。
准确来讲,还是第一幅图贴近现实!
// 出队二,将队列中的队首元素出队,出对后对头“指针”front后移一位 bool deleteQueueFront_2(seqQueue* seq, dateType* date) { // 参数二:保存出列的元素返回 if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return false; } if (seq->front >= QUEUE_MAX) { cout << "队列已到尽头!" << endl; return false; } // 保存出列的元素返回 *date = seq->queue[seq->front]; seq->front += 1; // 队首“指针”后移一位 return true; }
第二种方式要比第一种方式要多一次判断合法性检查。如果front“指针”已经大于或者等于队列的最大元素存储,那后面的操作就没有意义了。
如果合法性都正确,那么只需要将队首“指针”front自增一就行了。
如上图就是出队前和出队后的效果图。
也跟第一种删除方式差不多,现在是头元素保留在原来的位置,只是front“指针”只增一了,我们也就无法访问到原来的元素了。
不懂的朋友也可以把他理解成下图:
准确来讲,还是第一幅图贴近现实!
// 获取队列的首元素 bool gainQueueFrontValue(seqQueue* seq, dateType* date) { if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return false; } // 保存出列的元素返回 *date = seq->queue[seq->front]; return true; }
也就是和出队的代码差不多,合法性检查之后,就像元素赋值给date返回。
// 修改队列中任意位置的值 bool alterQueueValue(seqQueue* seq, int i, dateType date) { // 参数二:修改位置;参数三:修改后的值 if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return false; } if (i < seq->front || i >= seq->rear) { cout << "修改位置不合法!" << endl; return false; } for (int j = seq->front; j < seq->rear; j++) { if (i-1 == j) { seq->queue[j] = date; break; } } return true; }
这个也是很简单的,从队首开始遍历,直到找到对应位置位置。将元素修改后break退出,然后结束函数。
注意:代码中,我们判断时对 i 进行了减一,我们不是从0开始数的,而是从1开始数。
// 获取队列中的元素个数
int size(seqQueue* seq) {
if (!seq) {
cout << "队列不存在!" << endl;
return false;
}
return seq->rear - seq->front;
}
我们可以将队尾“指针”rear减去队首“指针”front,就可以得到队列的元素个数了。
// 清空队列
void clearQueue(seqQueue* seq) {
if (!seq) {
return ;
}
seq->front = seq->rear = 0; // 对头与队尾“指针”都为零
}
清空队列和初始化队列都是一样的,将队首和队尾“指针”赋值0即可。
// 输出队列中的元素 void queuePrint(seqQueue* seq) { if (!seq) { cout << "队列不存在!"; return; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return; } for (int i = seq->front; i < seq->rear; i++) { cout << seq->queue[i] << " "; } cout << endl; }
测试代码:
#include <iostream> #include <Windows.h> using namespace std; #define QUEUE_MAX 5 // 队列的最大存储个数 typedef int dateType; // 队列存储的类型 typedef struct arrayQueue { dateType queue[QUEUE_MAX]; // 存储队列元素的数组 int front; // 对头“指针” int rear; // 队尾“指针” }seqQueue; // 队列初始化 void inItQueue(seqQueue* seq) { if (!seq) { return; } seq->front = seq->rear = 0; // 对头与队尾“指针”都为零 } // 判断队列是否为空 bool estimateQueueEmpty(seqQueue* seq) { if (!seq) { cout << "队列不存在!"; return false; } if (seq->front == seq->rear) { // 当头尾“指针”相等时,为空队列 return true; } return false; } // 判断队列是否已满 bool estimateQueueFull(seqQueue* seq) { if (!seq) { cout << "队列不存在!"; return false; } if (seq->rear == QUEUE_MAX) { // 当队尾“指针”等于队列最大存储个数时,为满 return true; } return false; } // 入队,将元素插入队列中 bool enterQueue(seqQueue* seq, dateType date) { // 参数二是入队的元素 if (!seq) { cout << "队列不存在!"; return false; } if (estimateQueueFull(seq)) { cout << "队列已满!插入 " << date << " 失败!" << endl; return false; } seq->queue[seq->rear] = date; // 在队尾插入元素date seq->rear += 1; // 队尾“指针”后移一位 return true; } // 出队一,将队列中队首的元素出队,后面的元素向前移动 bool deleteQueueFront_1(seqQueue* seq, dateType* date) { // 参数二:保存出列的元素返回 if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return true; } // 保存出列的元素返回 *date = seq->queue[seq->front]; // 从头元素的下一个元素开始遍历,将后续的所有元素都往前移动一个位置 for (int i = seq->front + 1; i < seq->rear; i++) { seq->queue[i - 1] = seq->queue[i]; } seq->rear -= 1; return true; } // 出队二,将队列中的队首元素出队,出对后对头“指针”front后移一位 bool deleteQueueFront_2(seqQueue* seq, dateType* date) { // 参数二:保存出列的元素返回 if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return false; } if (seq->front >= QUEUE_MAX) { cout << "队列已到尽头!" << endl; return false; } // 保存出列的元素返回 *date = seq->queue[seq->front]; seq->front += 1; // 队首“指针”后移一位 return true; } // 获取队列的首元素 bool gainQueueFrontValue(seqQueue* seq, dateType* date) { if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return false; } // 保存出列的元素返回 *date = seq->queue[seq->front]; return true; } // 修改队列中任意位置的值 bool alterQueueValue(seqQueue* seq, int i, dateType date) { // 参数二:修改位置;参数三:修改后的值 if (!seq) { cout << "队列不存在!" << endl; return false; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return false; } if (!date) { cout << "date指针为空!" << endl; return false; } if (i < seq->front || i >= seq->rear) { cout << "修改位置不合法!" << endl; return false; } for (int j = seq->front; j < seq->rear; j++) { if (i-1 == j) { seq->queue[j] = date; break; } } return true; } // 获取队列中的元素个数 int size(seqQueue* seq) { if (!seq) { cout << "队列不存在!" << endl; return false; } return seq->rear - seq->front; } // 输出队列中的元素 void queuePrint(seqQueue* seq) { if (!seq) { cout << "队列不存在!"; return; } if (estimateQueueEmpty(seq)) { cout << "队列为空!" << endl; return; } for (int i = seq->front; i < seq->rear; i++) { cout << seq->queue[i] << " "; } cout << endl; } // 清空队列 void clearQueue(seqQueue* seq) { if (!seq) { return ; } seq->front = seq->rear = 0; // 对头与队尾“指针”都为零 } int main(void) { seqQueue que; // 初始化队列 inItQueue(&que); // 插入元素 for (int i = 0; i < 6; i++) { enterQueue(&que, i * 5); } // 输出队列中的元素 queuePrint(&que); // 元素出队1 dateType date = 0; deleteQueueFront_1(&que, &date); cout << "出队的元素是:" << date << endl; cout << "出队元素后:" << endl; queuePrint(&que); cout << endl; // 元素出队2 deleteQueueFront_2(&que, &date); cout << "出队的元素是:" << date << endl; cout << "出队元素后:" << endl; queuePrint(&que); cout << endl; /*for (int i = 0; i < 5; i++) { if (deleteQueueFront_2(&que, &date)) { cout << "出队的元素是:" << date << endl; } else { cout << "出队失败!" << endl; } }*/ // 获取队首的元素 gainQueueFrontValue(&que, &date); cout << "队首的元素是:" << date << endl; cout << endl; // 修改队列中任意位置的值 if (alterQueueValue(&que, 2, 666)) { cout << "修改成功!" << endl; } else { cout << "修改失败!" << endl; } cout << "修改后:" << endl; queuePrint(&que); cout << endl; cout << "队列的元素个数是:" << size(&que) << endl; // 清空队列 clearQueue(&que); system("pause"); return 0; }
总结:
顺序存储队列就跟数组一样,其操作都不难,只要注意下标就行!
注意:由于一篇博客内容太多,所以我将会把他分成几篇进行讲解!
祝各位学习愉快!
下集提醒:
你将学会队列的另一种最基本的表现形式:链式存储队列;
请持续关注!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。