数据结构-队列(第三章)的整理笔记,若有错误,欢迎指正。
栈
队 列 的 基 本 操 作 ( “ & ” 表 示 引 用 调 用 ) { I n i t Q u e u e ( & q ) : 初 始 化 队 列 , 构 造 一 个 空 队 列 q 。 Q u e u e E m p t y ( q ) : 判 断 队 列 是 否 为 空 , 若 队 列 q 为 空 , 则 返 回 真 ; 否 则 返 回 假 。 e n Q u e u e ( & q , e ) : 进 队 列 , 将 元 素 e 进 队 作 为 队 尾 元 素 。 d e Q u e u e ( & q , & e ) : 出 队 列 , 从 队 列 中 出 队 一 个 元 素 , 并 将 其 值 赋 给 e 。 D e s t r o y Q u e u e ( & q ) : 销 毁 队 列 , 释 放 队 列 q 占 用 的 存 储 空 间 。 队列的基本操作(“\&”表示引用调用)\begin{cases} InitQueue(\&q):初始化队列,构造一个空队列q。\\ QueueEmpty(q):判断队列是否为空,若队列q为空,则返回真;否则返回假。\\ enQueue(\&q,e):进队列,将元素e进队作为队尾元素。\\ deQueue(\&q,\&e):出队列,从队列中出队一个元素,并将其值赋给e。\\ DestroyQueue(\&q):销毁队列,释放队列q占用的存储空间。\\ \end{cases} 队列的基本操作(“&”表示引用调用)⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧InitQueue(&q):初始化队列,构造一个空队列q。QueueEmpty(q):判断队列是否为空,若队列q为空,则返回真;否则返回假。enQueue(&q,e):进队列,将元素e进队作为队尾元素。deQueue(&q,&e):出队列,从队列中出队一个元素,并将其值赋给e。DestroyQueue(&q):销毁队列,释放队列q占用的存储空间。
队列的顺序存储结构
队列的顺序存储类型描述
typedef struct
{
ElemType data[MaxSize]; //存放队中元素
int front, rear; //对头和队尾指针
}SqQueue; //顺序队类型
初始化队列—InitQueue(&q)
void InitQueue(SqQueue &q)
{
q.front = q.rear = 0; //初始化队首、队尾指针
}
判断队列是否为空—QueueEmpty(q)
bool QueueEmpty(SqQueue q)
{
return q.rear == q.front;
}
进队列—enQueue(&q,e)
bool enQueue(SqQueue& q, ElemType e)
{
if (q.rear == MaxSize - 1) return false;
q.data[q.rear++] = e;
return true;
}
分析
- 顺序队操作中,元素进队时队尾指针rear增1,元素出队时队头指针front增1,当队满的条件(即rear== Max Size-1)成立时,表示此时队满(上溢出)了,不能再进队元素。实际上,当rear== Maxsize-1成立时,队列中可能还有空位置,这种因为队满条件设置不合理导致队满条件成立而队列中仍然有空位置的情况称为假溢出。
出队列—deQueue(&q,&e)
bool deQueue(SqQueue& q, ElemType &e)
{
if (q.rear == q.front) return false;
e = q.data[q.front++];
return true;
}
销毁队列—DestroyQueue(&q)
void Destroy(SqQueue*& q)
{
free(q.data);
}
完整代码(静态队列)
#include<iostream>
using namespace std;
#define MaxSize 10
typedef int ElemType;
typedef struct
{
ElemType data[MaxSize];
int front, rear;
}SqQueue;
void InitQueue(SqQueue& q)
{
q.front = q.rear = 0; //初始化队首、队尾指针
}
bool QueueEmpty(SqQueue q)
{
return q.rear == q.front;
}
bool enQueue(SqQueue& q, ElemType e)
{
if (q.rear == MaxSize) return false;
q.data[q.rear++] = e;
return true;
}
bool deQueue(SqQueue& q, ElemType& e)
{
if (q.rear == q.front) return false;
e = q.data[q.front++];
return true;
}
void PriQueue(SqQueue& q)
{
int i, j = q.rear, num;
for (i = 0; i < j; i++)
{
deQueue(q, num);
printf("%d ", num);
}
}
int main()
{
SqQueue q;
InitQueue(q);
cout << "队列为空吗?—" << boolalpha << QueueEmpty(q) << endl;
ElemType num;
printf("输入:");
scanf("%d", &num);
while (num != 999)
{
enQueue(q, num);
scanf("%d", &num);
}
cout << "队列为空吗?—" << boolalpha << QueueEmpty(q) << endl;
PriQueue(q);
return 0;
}
分析
- 静态队列数组长度固定,不可更改,最多只能存MaxSize个数。
部分代码(动态队列)
#include<iostream>
#include<malloc.h>
using namespace std;
#define InitSize 10
typedef int ElemType;
typedef struct
{
int *data;
int MaxSize;
int front, rear;
}SqQueue;
void InitQueue(SqQueue& q)
{
q.data = (int*)malloc(sizeof(int) * InitSize);
q.front = q.rear = 0; //初始化队首、队尾指针
q.MaxSize = InitSize;
}
bool QueueEmpty(SqQueue q)
{
return q.rear == q.front;
}
bool IncreaseSize(SqQueue& q, int len)
{
int* p = q.data; //定义整型指针变量,指向队列的首地址
q.data = (int*)malloc(sizeof(int) * (q.MaxSize + len)); //申请一片更大的连续的存储空间
if (q.data == NULL) return false; //没有连续的存储空间,分配失败
for (int i = 0; i < q.MaxSize; i++)
q.data[i] = p[i]; //将数据复制到新的存储区域中
q.rear = q.MaxSize;
q.MaxSize = q.MaxSize + len; //队列的最大长度增加
free(p); //释放原来的内存空间
return true;
}
bool enQueue(SqQueue& q, ElemType e)
{
ElemType num;
if (q.rear == InitSize) IncreaseSize(q, q.MaxSize);
q.data[q.rear++] = e;
return true;
}
bool deQueue(SqQueue& q, ElemType& e)
{
if (q.rear == q.front) return false;
e = q.data[q.front++];
return true;
}
void PriQueue(SqQueue& q)
{
int i, j = q.rear, num;
for (i = 0; i < j; i++)
{
deQueue(q, num);
printf("%d ", num);
}
}
void DestroyQueue(SqQueue& q)
{
free(q.data);
}
int main()
{
SqQueue q;
InitQueue(q);
cout << "队列为空吗?—" << boolalpha << QueueEmpty(q) << endl;
ElemType num;
printf("输入:");
scanf("%d", &num);
while (num != 999)
{
enQueue(q, num);
scanf("%d", &num);
}
cout << "队列为空吗?—" << boolalpha << QueueEmpty(q) << endl;
PriQueue(q);
return 0;
}
分析
- 上述5个基本运算算法的时间复杂度均为O(1);
环形队列
- 可以看出,在出现假溢出时队尾指针rear指向data数组的最大下标,而另外一端还有若干个空位置。
- 解决的方法是把data数组的前端和后端连接起来,形成一个环形数组,即把存储队列元素的数组从逻辑上看成一个环,称为环形队列或者循环队列(circular queue)。
- 环形队列首尾相连后,当队尾指针rear= Maxsize-1后,再前进一个位置就到达0,于是就可以使用另一端的空位置存放队列元素了。实际上存储器中的地址总是连续编号的,为此采用数学上的求余运算(%)来实现:
//队头指针front循环增1:
front = (front + 1) % MaxSize
//队尾指针rear循环增1:
rear = (rear + 1) % MaxSize
//队列长度:
(q->rear + MaxSize - q->front) % MaxSize
- 环形队列的队头指针front和队尾指针rear初始化时都置为0,即 front=rear=0。在进队元素和出队元素时,队尾指针和队头指针分别循环增1。
三种处理方式区分是队空还是队满的情况
- 牺牲一个单元来区分队空和队满,入队时少用一个队列单元(普遍的做法),约定以“队头指针在队尾指针的下一位置作为队满的标志”。
- 设置队空条件是:
q->rear==q->ront
;队满条件是(q->rear+1)%Maxsize==q->front
。而进队操作和出队操作改为分别将队尾指针rear和队头指针front循环进1。
进队列—enQueue(&q,e)
bool enQueue(SqQueue*& q, ElemType e)
{
if ((q->rear + 1) % MaxSize == q->front) return false; //队空则报错
q->data[q->rear] = e;
q->rear = (q->rear + 1) % MaxSize; //队尾指针加1取模
return true;
}
出队列—deQueue(&q,&e)
bool deQueue(SqQueue*& q, ElemType &e)
{
if (q->rear == q->front) return false; //队满则报错
e = q->data[q->front];
q->front = (q->front + 1) % MaxSize; //队头指针加1取模
return true;
}
- 类型中增设表示元素个数的数据成员,入队时q.size++,出队时q.size--。
- 设置队空的条件为
q.size==0
;队满的条件为q.size==MaxSize
。
类型中增设表示元素个数的数据成员:
typedef struct
{
ElemType data[MaxSize]; //存放队中元素
int front, rear; //对头和队尾指针
int size; //队列当前长度
}SqQueue; //顺序队类型
初始化时将size设置为0:
void InitQueue(SqQueue *&q)
{
q = (SqQueue*)malloc(sizeof(SqQueue));
q->front = q->rear = 0; //初始化队首、队尾指针
q->size = 0;
}
进队列—enQueue(&q,e)
bool enQueue(SqQueue*& q, ElemType e)
{
if (q->size==MaxSize) return false;
q->data[q->rear++] = e;
q->size++;
printf("size:%d ", q->size);
return true;
}
出队列—deQueue(&q,&e)
bool deQueue(SqQueue*& q, ElemType &e)
{
if (q->size==0) return false;
e = q->data[q->front++];
q->size--;
return true;
}
- 类型中增设tag数据成员,以区分是队满还是队空。只有删除操作,才可能导致队空;只有插入操作,才可能导致队满。
- tag等于0时,若因删除导致q.front==q.rear,则为队空;tag等于1时,若因插入导致q.front==q.rear,则为队满。
类型中增设表示元素个数的数据成员:
typedef struct
{
ElemType data[MaxSize]; //存放队中元素
int front, rear; //对头和队尾指针
int tag; //最近进行的是删除/插入
}SqQueue; //顺序队类型
初始化时将size设置为0:
void InitQueue(SqQueue *&q)
{
q = (SqQueue*)malloc(sizeof(SqQueue));
q->front = q->rear = 0; //初始化队首、队尾指针
q->tag = 0;
}
进队列—enQueue(&q,e)
bool enQueue(SqQueue*& q, ElemType e)
{
if (q->front == q->rear && q->tag == 1) return false;
q->data[q->rear++] = e;
q->tag = 1;
return true;
}
出队列—deQueue(&q,&e)
bool deQueue(SqQueue*& q, ElemType &e)
{
if (q->rear == q->front && q->tag == 0) return false;
e = q->data[q->front++];
q->tag == 0;
return true;
}
队列的链式存储结构
队列的链式存储类型描述
typedef struct LinkNode//链式队列结点
{
ElemType data;
struct LinkNode* next;
}LNode;
typedef struct //链式队列
{
LNode* front, * rear; //队列的队头和队尾
}LinkQueue;
初始化队列—InitQueue(&q)
void InitQueue(LinkQueue*& q)
{
q = (LinkQueue*)malloc(sizeof(LinkQueue)); //建立头结点
q->front = q->rear = NULL; //初始为空
}
判断队列是否为空—QueueEmpty(q)
bool EmptyQueue(LinkQueue*& q)
{
return q->rear == NULL;
}
进队列—enQueue(&q,e)
void enQueue(LinkQueue*& q, ElemType e)
{
LinkNode* s;
s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = e;
s->next = NULL;
if (q->rear == NULL) q->front = q->rear = s; //创建新节点,插入到链尾
else
{
q->rear->next = s;
q->rear = s;
}
}
出队列—deQueue(&q,&e)
bool deQueue(LinkQueue*& q, ElemType& e)
{
LinkNode* p;
if (q->rear == NULL) return false; //空队
p = q->front;
q->front->next = p->next;
if (q->rear == q->front) q->rear = q->front = NULL; //若队列中只有一个结点,删除后变空
else q->front = q->front->next;
e = p->data;
free(p);
return true;
}
销毁队列—DestoryQueue(&q)
void DestoryQueue(LinkQueue*& q)
{
LinkNode* p = q->front, * r;
if (p != NULL)
{
r = p->next;
while (r != NULL)
{
free(p);
p = r;
r = p->next;
}
}
free(p);
free(q);
}
完整代码(带头结点的链式队列)
#include<iostream>
using namespace std;
#include<malloc.h>
typedef int ElemType;
typedef struct LinkNode//链式队列结点
{
ElemType data;
struct LinkNode* next;
}LNode;
typedef struct //链式队列
{
LNode* front, * rear; //队列的队头和队尾
}LinkQueue;
void InitQueue(LinkQueue*& q) //初始化队列
{
q = (LinkQueue*)malloc(sizeof(LinkQueue)); //建立头结点
q->front = q->rear = NULL;
}
bool IsEmpty(LinkQueue *q) //判断队列是否为空
{
return q->rear == q->front;
}
void EnQueue(LinkQueue*& q,BTree t)
{
LNode* p = (LNode*)malloc(sizeof(LNode)); //创建新结点
p->data = t; //插入到队尾
p->next = NULL;
q->rear->next = p;
q->rear = p;
}
bool DeQueue(LinkQueue*& q, BTree &t)
{
LNode* p = q->front->next;
if (q->rear == q->front) return false; //空队
t= p->data;
p = p->next;
if (q->rear == p) q->rear = q->front; //若原队列中只有一个结点,删除后变空
free(p);
return true;
}
void DestoryQueue(LinkQueue*& q)
{
LinkNode* p = q->front, * r;
if (p != NULL)
{
r = p->next;
while (r != NULL)
{
free(p);
p = r;
r = p->next;
}
}
free(p);
free(q);
}
int main()
{
LinkQueue *q;
InitQueue(q);
cout << boolalpha << "队列为空吗?—" << EmptyQueue(q) << endl;
int i;
for (i = 0; i < 10; i++)
enQueue(q, i);
cout << boolalpha << "队列为空吗?—" << EmptyQueue(q) << endl;
ElemType num;
for (i = 0; i < 10; i++)
{
deQueue(q, num);
cout << num << ' ';
}
DestoryQueue(q);
return 0;
}
部分代码(不带头结点的链式队列)
void InitQueue(LinkQueue*& q)
{
q->rear = q->front = NULL; //初始化队列
}
bool EmptyQueue(LinkQueue *q)
{
return (q->front == NULL); //判空条件:头指针是否为空
}
void enQueue(LinkQueue*& q, ElemType e)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = e;
s->next = NULL;
if (EmptyQueue(q)) //如果队列为空,则将头指针尾指针同时指向新插入的节点
{
q->front = s;
q->rear = s;
return;
}
q->rear->next = s;
q->rear = s;
}
bool deQueue(LinkQueue*& q, ElemType& e)
{
if (q->front == NULL)
return false;
LinkNode* p = q->front; //p指向头指针
e = p->data;
q->front = p->next; //队列的头指针指向p指针的下一节点
if (q->rear == p) q->rear = q->front = NULL; //此次是最后一个节点,队列置空
free(p);
return true;
}
void DestoryQueue(LinkQueue*& q)
{
LinkNode* p = q->front, * r;
if (p != NULL)
{
r = p->next;
while (r != NULL)
{
free(p);
p = r;
r = p->next;
}
}
free(p);
}
分析
- 入队时不考虑队满的条件。
双端队列
- 双端队列是一种具有队列和栈的性质的数据结构,是指两端都可以进行进队和出队操作的队列。