队列(Queue):和栈类似同样是受限的线性表,区别在于队列只允许在一端插入,在另一端删除,称为先进先出表FIFO;
定义:
· 队列(Queue)简称为“队”,也是一种受限的线性表。只允许在线性表的一端进行插入,在另一端进行删除;
· 称插入数据的一端为队尾(rear),称删除数据的一端为队首(front);向队尾添加数据称为入队,新入队的元素称为队尾元素;在队列中删除元素称为出队,元素出队后它的后续元素称为新的队首元素
· 双端队列:允许在两端进行插入和删除;相应的还有输入受限的双端队列和输出受限的双端对列;只要栈能做到的双端队列同样可以做到
· 队列的抽象数据类型定义:
ADT Queue{
数据对象 : D={a0,a1,a2...,an, 其中ai(i=1,2..n)是同一种数据类型的元素}
数据关系 : R={<ai,ai+1>}
数据操作 :
InitQueue(&Q): 初始化队列
DestroyQueue(&Q): 销毁队列
ClearQueue(&Q): 清空队列
IsEmpty(Q): 队列判空
GetSize(Q): 返回队列长度
GetHead(Q): 查看队头
GetTail(Q): 查看队尾
EnQueue(&Q,e): 入队
DeQueue(&Q,&e): 出队
Traverse(Q): 遍历队列
}
队列的顺序存储实现:
*在队列的顺序实现中,一般将队列设置为一个逻辑上的圆环,这种队列称为“循环队列”,通常用循环数组实现来实现;
*循环队列中,通常会设置两个指针,front头指针和rear尾指针;
*在元素入队时,将新入队的元素保存到rear指向的单元,然后rear指针后移;在出队时,将队首指针front指向的元素返回,然后front后移
*在这种循环队列中,队空是指,front和rear指向同一个单元;队满也是front和rear指向同一个单元,怎么区分队空和队满呢?
一般用这3种方式:
- 队列中定义一个size属性,如果size等于数组容量,则为队满;
- 或者数组最后一个单元不存放元素,当(rear+1)%capacity==队首,则队满 - 队空则头尾指针为-1,队满则(q.rear-q.front+MaxSize)%MaxSize +1等于队列最大容量MaxSize。
代码实现:(这里区分队空队满采用的第三种方式)
#include <stdio.h>
#include <stdlib.h>
#define ElemType int
//队列容量固定10
#define MaxSize 10
typedef struct Queue{
ElemType *data;
int front;
int rear;
}Queue;
// InitQueue(&Q): 初始化队列
void InitQueue(Queue* q){
//申请一片容量为MaxSize大小的连续空间
q->data = (ElemType*)malloc(MaxSize * sizeof(ElemType));
if(q->data == NULL){ exit(1); }
q->front = -1;
q->rear = -1;
}
// DestroyQueue(&Q): 销毁队列
void DestroyQueue(Queue* q){
//释放申请的内存
free(q->data);
//置空指针
q->data = NULL;
q->front = -1;
q->rear = -1;
}
// ClearQueue(&Q): 清空队列
void ClearQueue(Queue* q){
q->front = -1;
q->rear = -1;
}
// IsEmpty(Q): 队列判空
int IsEmpty(Queue q){
return q.front == -1;
}
// GetSize(Q): 返回队列长度
int GetSize(Queue q){
if(q.front == -1){
return 0;
}else{
return (q.rear-q.front+MaxSize)%MaxSize +1;
}
}
// GetHead(Q): 查看队头
ElemType GetHead(Queue q){
if(q.front == -1){ exit(1); }//队空,获取失败,退出程序
return q.data[q.front];//返回队头元素
}
// GetTail(Q): 查看队尾
ElemType GetTail(Queue q){
if(q.front == -1){ exit(1); }//队空,获取失败,退出程序
return q.data[q.rear];//返回队尾元素
}
// EnQueue(&Q,e): 入队
void EnQueue(Queue* q, ElemType e){
if(GetSize(*q) == MaxSize){ exit(1); }//队满,入队失败
//如果此时队空,那么头尾指针都是-1,入队前要改头尾指针
if(q->front == -1){
q->rear = 0;
q->front = 0;
}else{//否则只改尾指针即可
q->rear = (q->rear+1)%MaxSize;
}
q->data[q->rear] = e;
}
// DeQueue(&Q,&e): 出队,并返回该元素
ElemType DeQueue(Queue* q){
if(GetSize(*q) == 0){ exit(1); }//队空出队失败
//保存队头元素
ElemType ret = q->data[q->front];
//如果队头队尾指针相等,说明队中元素就一个,队后应该将队头队尾设置为-1
if(q->rear == q->front){
q->front = -1;
q->rear = -1;
}else{
//更新队头指针
q->front = (q->front+1)%MaxSize;
}
return ret;
}
// Traverse(Q): 遍历队
void Traverse(Queue q){
printf("\n");
int i, j;
j = q.front;
for( i=1; i<=GetSize(q); i++ ){
printf("%d ", q.data[j]);
j = (j+1)%MaxSize;
}
printf("\n");
}
int main(int argc, char const *argv[])
{
Queue q;
InitQueue(&q);
Traverse(q);
EnQueue(&q, 2);
EnQueue(&q, 12);
EnQueue(&q, 22);
EnQueue(&q, 32);
EnQueue(&q, 42);
EnQueue(&q, 52);
EnQueue(&q, 62);
EnQueue(&q, 72);
Traverse(q);
printf("size==%d\n", GetSize(q));
printf("IsEmpty:%d\n", IsEmpty(q));
printf("head:%d\n", GetHead(q));
DeQueue(&q);
DeQueue(&q);
DeQueue(&q);
DeQueue(&q);
printf("head:%d\n", GetHead(q));
Traverse(q);
DeQueue(&q);
DeQueue(&q);
DeQueue(&q);
DeQueue(&q);
printf("size==%d\n", GetSize(q));
DestroyQueue(&q);
Traverse(q);
printf("IsEmpty:%d\n", IsEmpty(q));
return 0;
}
运行结果:
代码不够完善,欢迎评论补充^0^