实验题3:实现环形队列的各种基本运算的算法
题目描述
编写一个程序sqqueue.cpp,实现环形队列(假设栈中的元素类型ElemTye为char)的各种基本运算,并在此基础上设计一个程序exp3-3.cpp完成以下功能。
(1)初始化队列q。
(2)判断队列q是否非空。
(3)依次进队元素a、b、c。
(4)出队一个元素,输出该元素。
(5)依次进队元素d、e、f。
(6)输出出队序列。
(7)释放队列。
sqqueue.cpp程序,其中包含如下函数。
· InitQueue(SqQueue *&q):初始化环形队列q。
· DestroyQueue(SqQueue *&q):销毁环形队列q。
·QueueEmpty(SqQueue *q):判断环形队列q是否为空。
· enQueue(SqQueue *&q,ElemType e):环形队列进队一个元素e。
·deQueue(SqQueue *&q,ElemType &e):环形队列出队一个元素e。
运行代码
sqqueue.cpp
#include<iostream>
using namespace std;
#include<stdio.h>
#include<malloc.h>
#define Maxsize 100
typedef char Elemtype;
typedef struct {
Elemtype data[Maxsize];
int front,rear;
}SqQueue;
//初始化队列q
void InitQueue(SqQueue*& q) {
q = (SqQueue*)malloc(sizeof(SqQueue));
q->front = q->rear = 0;
}
// 销毁队列 q
void DestoryQueue(SqQueue*& q) {
free(q);
}
// 判断队列 q 是否为空
bool QueueEmpty(SqQueue* q) {
return(q->front == q->rear);
}
// 进队列 q
bool enQueue(SqQueue*& q, Elemtype e) {
if ((q->rear + 1) % Maxsize == q->front)
return false;
q->rear = (q->rear + 1) % Maxsize;
q->data[q->rear] = e;
return true;
}
// 出队列 q
bool deQueue(SqQueue*& q, Elemtype& e) {
if (q->front == q->rear)
return false;
q->front = (q->front + 1) % Maxsize;
e = q->data[q->front];
return true;
}
exp3-3.cpp
#include "sqqueue.cpp"
using namespace std;
int main() {
Elemtype e;
SqQueue* q;
cout << "环形队列的基本运算如下:\n";
cout << " (1)初始化队列 q\n";
InitQueue(q);
cout << " (2)依次进入队列的元素为 a,b,c\n";
if (!enQueue(q, 'a')) printf("\t提示:队满,不能进队\n");
if (!enQueue(q, 'b')) printf("\t提示:队满,不能进队\n");
if (!enQueue(q, 'c')) printf("\t提示:队满,不能进队\n");
cout << " (3)队列为" << (QueueEmpty(q) ? "空" : "非空") << endl;
if (deQueue(q, e) == 0)
cout << ("队空,不能出队\n");
else
cout << " (4)出队的第一个元素:" << e << endl;
cout << " (5)依次进队列的元素:d,e,f\n";
if (!enQueue(q, 'd')) printf("\t提示:队满,不能进队\n");
if (!enQueue(q, 'e')) printf("\t提示:队满,不能进队\n");
if (!enQueue(q, 'f')) printf("\t提示:队满,不能进队\n");
cout << " (6)出队列序列";
while (!QueueEmpty(q)) {
deQueue(q, e);
cout << e << " ";
}
cout << endl;
cout << " (7)释放队列\n";
DestoryQueue(q);
return 1;
}
代码思路
环形队列(也称为循环队列)的基本操作,包括初始化、进队、出队、判断队列是否为空以及销毁队列等功能,并在 main
函数中对这些功能进行了测试。
-
InitQueue
函数:作用是初始化环形队列。通过动态分配内存为环形队列结构体SqQueue
分配空间,并将队首指针front
和队尾指针rear
初始化为 0,表示队列为空。 -
DestoryQueue
函数:用于销毁环形队列。释放动态分配给环形队列的内存空间。 -
QueueEmpty
函数:判断环形队列是否为空。如果队首指针front
等于队尾指针rear
,则说明队列为空,返回true
;否则返回false
。 -
enQueue
函数:实现进队操作。- 首先检查队列是否已满,判断条件是
(q->rear + 1) % Maxsize == q->front
,如果满足这个条件则表示队列已满,进队失败,返回false
。 - 如果队列未满,将队尾指针
rear
向后移动一位(q->rear = (q->rear + 1) % Maxsize
),并将元素e
存入队尾位置(q->data[q->rear] = e
),然后返回true
。
- 首先检查队列是否已满,判断条件是
-
deQueue
函数:执行出队操作。- 检查队列是否为空,如果队首指针
front
等于队尾指针rear
,则出队失败,返回false
。 - 如果队列非空,将队首指针
front
向后移动一位(q->front = (q->front + 1) % Maxsize
),并将队首元素赋值给变量e
,然后返回true
。
- 检查队列是否为空,如果队首指针
实验题4:实现链队的各种基本运算的算法
题目描述
编写一个程序liqueue.cpp,实现链队(假设栈中的元素类型ElemType为char)
的各种基本运算,并在此基础上设计一个程序exp3-4.cpp完成以下功能。
(1)初始化链队q。
(2)判断链队q是否非空。
(3)依次进队元素a、b、c。
(4)出队一个元素,输出该元素。
(5)依次进队元素d、e、f。
(6)输出出队序列。
(7)释放链队。
liqueue.cpp程序,其中包含如下函数。
· InitQueue(LinkQuNode*&q):初始化链队q。
· DestroyQueue(LinkQuNode *&q):销毁链队q。
· QueueEmpty(LinkQuNode*q):判断链队q是否为空。
· enQueue(LinkQuNode*&q,ElemType e):链队进队一个元素e。
· deQueue(LinkQuNode *&q,ElemType &e):链队出队一个元素e。
运行代码
liqueue.cpp
#include<iostream>
using namespace std;
#include<stdio.h>
#include<malloc.h>
#define Maxsize 100
typedef char Elemtype;
typedef struct DataNode {
Elemtype data;
struct DataNode* next;
}DataNode;
typedef struct {
DataNode* front;
DataNode* rear;
}LinkQuNode;//声明链队类型
//初始化链队q
void InitQueue(LinkQuNode*& q) {
q = (LinkQuNode*)malloc(sizeof(LinkQuNode));
q->front = q->rear = NULL;
}
// 销毁链队 q
void DestoryQueue(LinkQuNode*& q) {
DataNode* p = q->front, * r;
if (p != NULL) {
r = p->next;
while (r != NULL) {
free(p);
p = r; r = p->next;
}
}
free(p);
free(q);
}
// 判断链队 q 是否为空
bool QueueEmpty(LinkQuNode* q) {
return( q->rear==NULL);
}
// 进队
void enQueue(LinkQuNode*& q, Elemtype e) {
DataNode* p;
p = (DataNode*)malloc(sizeof(DataNode));
p->data = e;
p->next = NULL;
if (q->rear == NULL)
q->front=q->rear = p;
else {
q->rear->next = p;
q->rear = p;
}
}
// 出链队 q
bool deQueue(LinkQuNode*& q, Elemtype& e) {
DataNode* t;
if (q->rear==NULL)
return false;
t = q->front;
if (q->front == q->rear)
q->front = q->rear = NULL;
else
q->front = q->front->next;
e = t->data;
free(t);
return true;
}
exp3-4.cpp
#include "liqueue.cpp"
using namespace std;
int main() {
Elemtype e;
LinkQuNode* q;
cout << "链队的基本运算如下:\n";
cout << " (1)初始化链队 q\n";
InitQueue(q);
cout << " (2)依次进入链队的元素为 a,b,c\n";
enQueue(q, 'a');
enQueue(q, 'b');
enQueue(q, 'c');
cout << " (3)链队为" << (QueueEmpty(q) ? "空" : "非空") << endl;
if (deQueue(q, e) == 0)
cout << ("\t提示:队空,不能出队\n");
else
cout << " (4)出队一个元素:" << e << endl;
cout << " (5)依次进链队的元素:d,e,f\n";
enQueue(q, 'd');
enQueue(q, 'e');
enQueue(q, 'f');
cout << " (6)出链队序列";
while (!QueueEmpty(q)) {
deQueue(q, e);
cout << e << " ";
}
cout << endl;
cout << " (7)释放链队\n";
DestoryQueue(q);
return 1;
}
代码思路
实现了链队(一种基于链表实现的队列数据结构)的基本操作,包括初始化、进队、出队、判断队列是否为空以及销毁队列等功能,并在 main
函数中对这些功能进行了测试。
-
InitQueue
函数:作用是初始化链队。通过动态分配内存为链队结构体LinkQuNode
分配空间,并将队首指针front
和队尾指针rear
初始化为NULL
,表示队列为空。 -
DestoryQueue
函数:用于销毁链队。首先遍历链表,从队首开始,依次释放每个节点的内存空间。使用两个指针p
和r
,p
指向当前节点,r
指向下一个节点。在循环中,先释放p
指向的节点,然后将p
指向r
,r
指向下一个节点,直到遍历完整个链表。最后释放链队结构体的内存空间。 -
QueueEmpty
函数:判断链队是否为空。如果队尾指针rear
为NULL
,则说明队列为空,返回true
;否则返回false
。 -
enQueue
函数:实现进队操作。- 首先动态分配一个新节点
p
,将待进队元素e
存入新节点的数据域data
,并将新节点的next
指针初始化为NULL
。 - 如果队列为空(即队尾指针
rear
为NULL
),则将队首指针front
和队尾指针rear
都指向新节点。 - 如果队列不为空,则将当前队尾节点的
next
指针指向新节点,然后将队尾指针rear
指向新节点。
- 首先动态分配一个新节点
-
deQueue
函数:执行出队操作。- 如果队列为空(即队尾指针
rear
为NULL
),则出队失败,返回false
。 - 如果队列非空,将队首节点赋值给临时指针
t
。如果队首节点就是队尾节点(即只有一个节点),则将队首指针front
和队尾指针rear
都置为NULL
;否则将队首指针front
指向队首节点的下一个节点。然后将队首节点的数据域data
赋值给变量e
,释放队首节点的内存空间,并返回true
。
- 如果队列为空(即队尾指针
环形队列和链队的异同
相同点
- 逻辑结构:两者都遵循队列先进先出(FIFO - First In First Out)的原则。在这两种数据结构中,最先进入的数据元素将最先被取出,就像排队一样,先到的人先接受服务。
- 基本操作功能:都具备队列的基本操作,包括初始化(创建一个空的队列结构)、判断队列是否为空(确定队列中是否有元素)、入队(将元素添加到队列尾部)、出队(将队列头部的元素移除)等操作。这些操作在概念上是相同的,只是在具体的实现方式上存在差异。
不用点
- 存储结构
- 环形队列:环形队列通常基于数组实现,它利用数组的循环特性来模拟队列的操作。在环形队列中,需要通过取模运算来处理队首和队尾指针的移动,以实现循环利用数组空间的目的。例如,在判断队满和队空条件以及指针的移动操作(如入队时队尾指针
rear=(rear + 1)%Maxsize
,出队时队首指针front=(front+1)%Maxsize
)上都体现了这种循环特性。这种存储结构的优点是实现相对简单,并且在空间利用上较为高效(不需要额外的指针域来存储节点间的连接关系),访问元素时可以直接通过数组下标进行,速度较快。但是,它的缺点是需要预先确定数组的大小,如果队列中元素数量超过数组大小,可能会出现问题(如队满判断不准确等)。 - 链队:链队是通过链表实现的,由节点组成,每个节点包含数据域和指向下一个节点的指针域。链队中的节点在内存中可以不连续存储,通过指针将各个节点连接起来形成队列结构。这种存储方式的优点是可以动态地分配内存,不需要预先确定队列的大小,适合处理元素数量不确定或者变化较大的情况。然而,由于需要额外的指针域来存储节点之间的连接关系,并且访问元素需要通过指针遍历链表,所以在空间和时间效率上相对环形队列可能会稍低一些。
- 环形队列:环形队列通常基于数组实现,它利用数组的循环特性来模拟队列的操作。在环形队列中,需要通过取模运算来处理队首和队尾指针的移动,以实现循环利用数组空间的目的。例如,在判断队满和队空条件以及指针的移动操作(如入队时队尾指针
- 空间利用效率
- 环形队列:环形队列预先分配固定大小的数组空间,如果队列中元素较少,会造成一定的空间浪费;但由于不需要额外的指针空间(相比于链队),在元素数量接近数组容量时,空间利用率较高。
- 链队:链队的空间是根据实际元素数量动态分配的,每个节点除了数据域外还需要额外的指针域,所以在存储少量元素时,由于指针域的存在会占用一定的额外空间。不过,链队不会像环形队列那样存在固定大小导致的空间浪费问题,适合元素数量波动较大的情况。
- 操作的实现细节
- 环形队列:在入队操作时,需要检查队列是否已满,通过特定的队满判断条件(如
(rear + 1)%Maxsize==front
)来确定,并且队尾指针的移动通过取模运算实现循环效果。出队操作时同样需要检查队列是否为空(front==rear
),队首指针的移动也涉及取模运算。 - 链队:入队操作需要动态分配新的节点内存,当队列为空时需要特殊处理队首和队尾指针(都指向新节点),非空时只需将新节点连接到队尾并更新队尾指针。出队操作要判断队列是否为空,若队首节点是队尾节点(即只有一个节点)时需要特殊处理队首和队尾指针(都置为
NULL
),否则只需更新队首指针并释放出队节点的内存。
- 环形队列:在入队操作时,需要检查队列是否已满,通过特定的队满判断条件(如