队列
队列的定义
队是一种只能从一端插入的线性表。队列只能选取一端进行插入操作,另一个端点进行删除操作
队列的概念
插入的一端称为队尾(rear)
删除的一端称为队头或队首(front)
向队列中插入新的元素称为进对或入队,新元素进队后成为新的队尾元素。
向队列中删除元素称为出队或离队,元素出队后其后继结点称为新的队首元素
队列的特点是先进先出,又把队列称为“先进先出表”
抽象数据类型=逻辑结构+基本运算(运算描述)
栈的几种基本运算如下:
InitQueue(&q)初始化队列。构造一个空队列q
DestroyQueue(&q)销毁队列。释放队列q所占的存储空间
QueueEmpty(q)判断队列是否为空:若队列q为空,则返回真,否则返回假
enQueue(&q,e)进队列,将元素e进队作为队尾元素
deQueue(&q,&e)出队列,从队列q中出队一个元素,并将其值赋给e
队列中元素逻辑关系与线性表中的相同,队列可以采用与线性表相同的存储结构。
思考
队列和线性表有什么不同?
队列和栈有什么不同?
队列的顺序存储结构及其基本运算的实现
顺序队
假设栈中的存储元素个数最大不超过正整数MaxSize个,所有元素类型都具有相同一数据类型ElemType,则可以用以下方式来定义顺序队类型SqQueuetypedef struct
{
ElemType daat[MaxSize];
int front,rear; //队首和队尾指针
}SqStack;
以为队的两端都在变化,所以需要两个指针来标识队列
小结
- 约定rear总是指向队尾元素,元素进队rear+1;
- 约定front总是指当前队中队头元素的前一位置,元素出队front+1
- 当MaxSize=-1时不能再进队
顺序队的四要素
队空条件:front=rear
队满条件:rear=MaxSize-1
元素e进队操作:raar++;将元素e放入rear处,data[rear]=e
元素e出队操作:front++;将队首指针的值赋值给元素e e=data[front]
在顺序栈中实现栈基本运算的算法
InitQueue(&s)初始化队
建立一个新的队q,实际上是将队首指针front和队尾指针rear都等于-1;void InitQueue(SqQueue*& q) {
q = (SqQueue*)malloc(sizeof(SqQueue));
q->front = q->rear = -1;
}
DestroyQueue(&s)销毁队列
释放队q的存储空间//销毁队列
//释放队q的内存
void DestroyQueue(SqQueue*& q) {
free(q);
}
QueueEmpty(s)判断队列是否为空
若满足q->rear==q->front返回真,否则假//判断队列是否为空
//若满足q->rear==q->front返回真,否则假
bool QueueEmpty(SqQueue* q) {
return (q->rear == q->front);
}
enQueue(&s,e)进队
若队不满,让队尾指针rear循环增1,将元素e插入到rear所指的元素上//进队
//若队不满,让队尾指针rear循环增1,将元素e插入到rear所指的元素上
void enQueue(SqQueue* q, ElemType e) {
if (q->rear==MaxSize-1)return;
q->rear++;
q->data[q->rear] = e;
}
deQueue(&s,&e)出队
若队不为空,则让队首指针增1,然后将所指的元素赋值给e//出队
//若队不为空,则让队首指针增1,然后将所指的元素赋值给e
void enQueue(SqQueue*&q, ElemType &e) {
if (q->front == q->rear)return;
q->front++;
e = q->data[q->front];
}
环形队列(或循环队列)中实现队列的基本运算
解决方案
把数组的前端和后端链接起来,形成一个环形的顺序表,即把存储队列元素从逻辑上看作是一个环,称为环形队列或循环队列
如何实现
实际上内存地址一定是连续的,不可能是环形的,这里是通过逻辑方式实现环形队列,也就是将rear++和front++该为:rear=(rear+1)%MaxSize
front=(front+1)%MaxSize
演示
环形队列四要素
队空条件rear=front
队满条件(rear+1)%MaxSize=front
元素e进队操作rear=(rear+1)%MaxSize;将e放在rear处
元素e出队操作fornt=(front+1)%MaxSize;取出front处元素e
环形队列的应用
对应环形队列来说,如果知道队头指针和队列中元素个数,则可以计算处队尾指针。也就是说,可以用队列中元素个数代替队尾指针。
设计处这种环形队列的初始化、入队、出队和判空算法
已知front、rear求元素个数count? MaxSize=5
得出结论:
- 已知front、rear,求队中元素个数count
count(rear-front+MaxSize)%MaxSize
- 已知front、count,求rear:
rear=(front+count)%MaxSize
- 已知rear,count求front
front=(rear-count+MaxSize)%MaxSize
带元素个数的循环队列基本运算的算法
依题意设计的环形队列类型如下:
typedef struct {
ElemType data[MaxSize];
int front; //队首指针
int count; //队列中元素个数
}QuType;
环形队列四要素
队空条件count=0
队满条件count=MaxSize
元素e进队操作rear=(rear+1)%MaxSize;将e放在rear处,count++
元素e出队操作fornt=(front+1)%MaxSize;取出front处元素e,count--
InitQueue(&s)初始化队
建立一个新的队q,实际上是将队首指针front和队内元素个数都等于0;void InitQueue(SqQueue*& qu) {
qu = (SqQueue*)malloc(sizeof(SqQueue));
qu->front = qu->count = 0;
}
EnQueue(&s,e)进队
若队不满,算出队尾指针,让队尾指针rear循环增1,将元素e插入到rear所指的元素上,对内元素count+1//进队
bool EnQueue(QuType* &qu, ElemType e)
{
int rear;
if (qu->count == MaxSize)return false; //队满
else {
rear = (qu->count + qu->front) % MaxSize; //求队尾位置
rear = (rear + 1) % MaxSize; //队尾+1
qu->data[rear] = e; //插入元素e
qu->count++; //队内元素+1
return ture;
}
}
DeQueue(&s,&e)出队
若队不为空,则让队首指针增1,然后将所指的元素赋值给e,队内元素个数count+1//出队
bool DeQueue(QuType* &qu, ElemType &e) {
if (qu->count == 0)return false;
else {
qu->front = (qu->front + 1) % MaxSize;
e = qu->data[qu->front];
qu->count--;
return true;
}
}
QueueEmpty(s)判空
//判空
bool QueueEmpty(QuType* qu) {
return (qu->count==0);
}
注意
显然环形队列比非环形队列更有效率利用内存空间,即环形队列会重复使用已经出队元素的空间。不会出现假溢出
但如果算法中需要使用所以进队的元素来进一步求解此时可以使用非环形队列