实验五 队列的实现
- 一、实验目的
- 二、实验环境
- 三、实验内容和步骤
- 四、编程题:(根据附件中的项目,上机验证下面各题目)
一、实验目的
(1) 链式存储结构的队列的特点与实现;
(2) 循环顺序存储结构的队列的特点与实现;
(3) 栈和队列的简单应用;
二、实验环境
Windows 7以上版本的操作系统, VS2010以上编程环境。
三、实验内容和步骤
本实验项目包含如下文件:
LinkQueue.h和LinkQueue.cpp分别是实现队列链式存储结构的头文件和源代码;
SqQueue.h和SqQueue.cpp分别是实现队列顺序存储结构的头文件和源代码;
LinkStack.h和LinkStack.cpp分别是实现栈链式存储结构的头文件和源代码;
SqStack.h和SqStack.cpp分别是实现栈顺序存储结构的头文件和源代码。
链式队列示意图:(下图包含头结点。课本P104页图3.20没有头结点,程序较复杂。本程序使用头结点的好处是可以在入队和出队时简化程序逻辑。)
验证题:(根据附件中的项目,上机验证下面各题)
根据课件目录提供的 .cpp 和 .h 文件建立项目。
1. 队列的 链式存储 结构实现。
1)在语句 EnQueue(Q1,‘a’); 处按“F9”设置断点①,按“F5”调试程序至断点处暂停(暂停序号0),然后按“F10”调试程序3次,每次程序暂停时记录数据。调试结束时按“Shift+F5”结束调试过程。
链队列队头元素位置: Q1.front->next
链队列队尾元素位置:Q1.rear
链队列队空的标志: Q1.front == Q1.rear 或 Q1.front->next == NULL
暂停序号 | 队头元素 | 队尾元素 | 队列中全部元素 |
0 | 无 | 无 | 无 |
1 | ‘a’ | ’a’ | ’a’ |
2 | ‘a’ | ’b’ | ‘a’, 'b’ |
3 | ‘a’ | ’c’ | ‘a’, ‘b’, 'c’ |
2)取消其它断点,在语句 DeQueue(Q1, temp); 处按“F9”设置断点②,按“F5”调试程序至断点处暂停(暂停序号0),然后按“F10”调试程序3次,每次程序暂停时记录数据。
暂停序号 | 队头元素 | 队尾元素 | 队列中全部元素 |
0 | ‘a’ | ‘c’ | ’a’, ‘b’, ‘c’ |
1 | ‘b’ | ‘c’ | ’b’, ‘c’ |
2 | ’c’ | ‘c’ | ’c’ |
3 | 无 | 无 | 无 |
2. 队列的 顺序存储 结构的实现(少用一个存储单元实现循环队列)。
1)取消其它断点,在语句**EnQueue(Q2,‘a’);**处按“F9”设置断点③,按“F5”调试程序至断点处暂停(暂停序号0),然后按“F10”调试程序5次,每次程序暂停时记录数据。观察第5次调试时队列中数据,分析入队EnQueue(Q2,‘e’);是否成功,并说明为什么?
循环队列队头元素位置:Q2.front
循环队列队尾元素位置: (Q2.rear - 1 + MaxSize) % MaxSize
循环队列容量: MaxSize - 1
暂停序号 | front | rear | 队列中全部元素 |
0 | 0 | 0 | 无 |
1 | 0 | 1 | ’a’ |
2 | 0 | 2 | ‘a’, 'b’ |
3 | 0 | 3 | ‘a’, ‘b’, 'c’ |
4 | 0 | 4 | ‘a’, ‘b’, ‘c’, 'd’ |
5 | 0 | 4 | ‘a’, ‘b’, ‘c’, ‘d’ |
答:入队EnQueue(Q2,‘e’);失败,因为队列容量MaxSize-1为4,第4次调试时队列已满。
2)取消其它断点,在语句 DeQueue(Q2, temp); 处按“F9”设置断点④,按“F5”调试程序至断点处暂停(暂停序号0),然后按“F10”调试程序4次,每次程序暂停时记录数据。
注意:出队时,并不会在内存中清除掉实际的元素,这是顺序存储(数组)的特点。队列由 front 和 rear 指示即可。
循环队列队空的标志: Q2.front == Q2.rear
暂停序号 | front | rear | 队列中全部元素 |
0 | 0 | 4 | ’a’, ‘b’, ‘c’, ‘d’ |
1 | 1 | 4 | ’b’, ‘c’, ‘d’ |
2 | 2 | 4 | ’c’, ‘d’ |
3 | 3 | 4 | ’d’ |
4 | 4 | 4 | 无 |
3)取消其它断点,在语句 EnQueue(Q2,‘j’); 处按“F9”设置断点⑤,按“F5”调试程序至断点处暂停,然后按“F10”调试程序1次,程序暂停时记录数据。观察队列中数据,分析入队 EnQueue(Q2,‘j’); 是否成功,并说明为什么?
循环队列队满的标志:(Q2.rear + 1) % MaxSize == Q2.front
暂停序号 | front | rear | 队列中全部元素 |
0 | 4 | 3 | ‘f’, ‘g’, ‘h’. ‘i’ |
1 | 4 | 3 | ‘f’, ‘g’, ‘h’. ‘i’ |
答:入队EnQueue(Q2,‘j’);失败,因为循环队列堆满的标志成立,无法再入队。
通过上述验证过程,总结链式结构和顺序结构在入队、出队操作时的异同?
答:
相同点:
入队操作:在队尾位置进行,需要更新队尾rear;
出队操作:在对头位置进行,需要更新对头front;
不同点:
链式结构最后一个元素出队时,在更新对头指针的同时也需更新队尾指针。
顺序结构入队操作只需要更新队尾rear;出队操作只需更新对头front。
四、编程题:(根据附件中的项目,上机验证下面各题目)
阅读Content.cpp文件中的void Reverse(LinkQueue Q)函数,回答下列问题:
1. 该函数的功能是什么?利用下面的主函数替代原来文件的主函数,观察运行结果验证你的结论。//利用栈结构逆置队列
int main()
{
LinkQueue Q;
InitQueue(Q);
for(int i=0; i<4; i++)
EnQueue(Q, 'a'+i);
TraverseQueue(Q);
Reverse(Q);
TraverseQueue(Q);
return 0;
}
答:Reverse(LinkQueue Q)函数的功能是逆置队列
运行结果如下:
2. 函数中两个循环的作用分别是什么?
while( !QueueEmpty(Q) )
{
DeQueue(Q, t);
Push(S,t);
} //作用:将队列Q中全部的数据逐一出队,并压入栈S中
while( !StackEmpty(S) )
{
Pop(S, t);
EnQueue(Q,t);
} //作用:将栈S中全部的数据逐一出栈,并入队Q中
3. 参考void Reverse(LinkQueue Q)函数,定义新的函数:
bool Palindrome_Test(char *str)
判断字符串str是否回文序列,若是则返回true,否则返回false。写出程序或用文字描述算法的执行步骤。
int main()
{
char str[20];
cout << "输入一个字符串:";
cin >> str;
if (Palindrome_Test(str))
cout << str << "是回文" << endl;
else
cout << str << "不是回文" << endl;
return 0;
}
【算法步骤】
① 将串str分别入队Q中和入栈S中
② 将Q的队头元素出队至变量t1中,将S的栈顶元素出栈至变量t2中
③ 若t1==t2,重复步骤②;若t1!=t2,则退出循环,返回false
④ 返回true
Palindrome_Test(char *str)函数定义如下:
bool Palindrome_Test(char *str)
{
LinkStNode *S;
InitStack(S);
LinkQueue Q;
InitQueue(Q);
ElemType t1, t2;
for (int i = 0; str[i] != '\0'; i++) {
Push(S, str[i]);
EnQueue(Q, str[i]);
}
while (!StackEmpty(S)){
Pop(S, t2);
DeQueue(Q, t1);
if (t1 != t2)
return false;
}
return true;
}
课后练习:
利用本课资源,实现舞伴匹配问题。
【问题描述】:
舞会上男士和女士各成一队。舞曲开始时,依次从每只队伍的队头位置各出一人匹配舞伴。若两支队伍初始人数不同时,较长队伍中未匹配者等待下一支舞曲。编写程序给出每人至少跳一支舞曲的舞伴匹配方案。
【算法分析】:
用循环队列存储元素(队员)
匹配成功即是出队操作
人数少的队伍出队后还要再入队
【算法描述】:
① 创建两个队列Q1、Q2,元素类型为String;
② 两个队伍分别出队t1、t2,此时(t1,t2)匹配成功;
③ 较短队伍刚刚出队的元素要再入队;
④ 重复第2、3步,直到人数多的队伍空。
【运行效果参考】:
源代码如下:
#include <iostream>
using namespace std;
#define MaxSize1 4
#define MaxSize2 9
//入队函数
bool EnQueue(char Q[], char item, int front, int &rear, int MaxSize)
{
if ((rear + 1) % MaxSize == front) {
cout << "队满";
return false;
} //判队满
Q[rear%MaxSize] = item; //新元素放尾指针位置
rear = (rear + 1) % MaxSize; //尾指针后移
return true;
}
//出队函数
bool DeQueue(char Q[], char &item, int &front, int rear, int MaxSize)
{
if (rear == front) {
cout << "队空";
return false;
} //判队空
item = Q[front]; //删除队头元素
front = (front + 1) % MaxSize; //头指针后移
return true;
}
int main()
{
char item1, item2;
char Q1[MaxSize1];
char Q2[MaxSize2];
int front1 = 0, rear1 = MaxSize1;
int front2 = 0, rear2 = MaxSize2;
for (int i = 0; i < (MaxSize1 - 1); i++)
EnQueue(Q1, 'A' + i, front1, rear1, MaxSize1);
for (int i = 0; i < (MaxSize2 - 1); i++)
EnQueue(Q2, 'a' + i, front2, rear2, MaxSize2);
cout << "Q1: ";
for (int i = 0; i < MaxSize1 - 1; i++)
cout << Q1[i] << " ";
cout << endl << "Q2: ";
for (int i = 0; i < MaxSize2 - 1; i++)
cout << Q2[i] << " ";
cout << endl << "匹配结果:" << endl;
for (int i = 0; front2 != rear2; i++){
DeQueue(Q1, item1, front1, rear1, MaxSize1);
DeQueue(Q2, item2, front2, rear2, MaxSize2);
cout << item2 << "与" << item1 << "配对" << endl;
if (front1 == rear1){
front1 = 0; rear1 = MaxSize1 - 1;
}
}
return 0;
}