实验五 队列的实现

  • 一、实验目的
  • 二、实验环境
  • 三、实验内容和步骤
  • 四、编程题:(根据附件中的项目,上机验证下面各题目)


一、实验目的

(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)函数的功能是逆置队列
运行结果如下:

数据结构与算法头歌预备实验答案_出队_02


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步,直到人数多的队伍空。

【运行效果参考】:

数据结构与算法头歌预备实验答案_数据结构与算法头歌预备实验答案_03


源代码如下:

#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;
}