一、队列的定义
队列简称队,是一种操作受限的线性表,只能在表的一端进行插入操作,而在另一端进行删除操作。将进行插入操作的一端称为队尾,进行删除操作的一端称为队首。向队中插入新元素称为进队或入队,从队中删除元素称为出队或退队。元素出队后其后继元素就成为新的队首元素。
队的主要特征为“先进先出”,就像排队买票,先来排队的人先买到票离开,因此也被称为先进先出表。队中元素逻辑关系与线性表的相同,可以采用与线性表相同的存储结构。
队的抽象数据类型定义如下:
ADT SQueue
{
D={ai|1<=i<=n,n>=0,ai为ELeType类型}
R={<ai,ai+1>|ai~ai+1∈D,i=1,...,n-1}
InitQueue(&q):初始化队列。构造一个空队列q。
DestroyQueue(&q):销毁队列。释放队列q占用的存储空间。
QueueEmpty(q):判断队列是否为空。若队列q为空,则返回真;否则返回假。
enQueue(&q,e):进队列。将元素e进队作为队尾元素。
deQueue(&q,&e):出队列。从队列q中出队一个元素,并将其值赋给e。
}
二、队的顺序存储结构及其基本运算的实现
采用顺序存储结构的队列称为顺序队。顺序存储队列即分配一块连续的存储空间来存放队列中的元素,并用两个整型变量来反映队中元素的变化(分别用来储存队首和队尾元素的下标)。
1、结构体定义
typedef struct
{
ElemType data[MaxSize];
int front,rear;//队首、队尾指针
} SqQueue;
注意:
1、rear指向队尾,进队rear增1;元素e进队:rear++; data[rear]=e;
2、front指向当前队首元素的前一位置,出队front增1;元素e出队:front++; e=data[front];
3、rear=MaxSize-1时队满(或假溢出),不能再进栈
4、 队空条件:front = rear
2、顺序队的基本运算
(1)初始化队
SqQueue *InitQueue()
{ SqQueue *q=(SqQueue *)malloc (sizeof(SqQueue));
q->front=q->rear=-1;
return q;
}
(2)销毁队
void DestroyQueue(SqQueue *q)
{
free(q);
}
(3)判断队列是否为空
bool QueueEmpty(SqQueue *q)
{
return(q->front==q->rear);
}
(4)进队
bool enQueue(SqQueue *q,ElemType e)
{ if (q->rear==MaxSize-1) //队满上溢出
return false;
q->rear++;
q->data[q->rear]=e;
return true;
}
(5)出队
bool deQueue(SqQueue *q,ElemType *e)
{ if (q->front==q->rear) //队空下溢出
return false;
q->front++;
*e=q->data[q->front];
return true;
}
3、环形队列(循环队列)
采用rear==MaxSize-1作为队满条件可能会出现假溢出现象(如图)。
为了解决这个问题,我们把数组的前端和后端连接起来,形成一个环形的顺序表,即把存储队列元素的表看成一个环,称为环形队列或循环队列。
实际上内存地址一定是连续的,不可能是环形的,这里是通过逻辑方式实现环形队列,也就是将rear++和front++改为:rear=(rear+1)%MaxSize; front=(front+1)%MaxSize
此时
1、队空条件:front = rear;
2、队满条件:(rear+1)%MaxSize = front;
3、进队e操作:rear=(rear+1)%MaxSize; 将e放在rear处
4、出队操作:front=(front+1)%MaxSize; 取出front处元素e;
为了区分队满与队空牺牲一个存储空间,否则队满与队空都将满足front
三、队的链式存储结构及其基本运算的实现
采用链式存储结构的队列称为链队。
1、结构体定义
typedef struct qnode
{ ElemType data; //数据元素
struct qnode *next;
}DataNode;//数据结点
typedef struct
{ DataNode *front; //指向单链表队头结点
DataNode *rear; //指向单链表队尾结点
}LinkQuNode; //链队结点
注意:
3、进队e操作:先建立一个结点用于存放元素e(由p指向它),将结点p插入到原单链表表尾后作为新尾节点
4、出队操作:取出队首结点的data值并删除
2、链队的基本运算
(1)初始化队
LinkQuNode *InitQueue()
{ LinkQuNode *q=(LinkQuNode *)malloc(sizeof(LinkQuNode));
q->front=q->rear=NULL;
return q;
}
(2)销毁队
void DestroyQueue(LinkQuNode *q)
{ DataNode *p=q->front,*r; //p指向队头数据结点
if (p!=NULL) //释放数据结点占用空间
{ r=p->next;
while (r!=NULL)
{ free(p);
p=r;r=p->next;
}
}
free(p); free(q); //释放链队结点占用空间
}
(3)判断队列是否为空
bool QueueEmpty(LinkQuNode *q)
{
return(q->rear==NULL);
}
(4)进队
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; //将p结点链到队尾,并将rear指向它
q->rear=p;
}
}
(5)出队
bool deQueue(LinkQuNode *q,ElemType *e)
{ DataNode *t;
if (q->rear==NULL) return false; //队列为空
t=q->front; //t指向第一个数据结点
if (q->front==q->rear) //队列中只有一个结点时
q->front=q->rear=NULL;
else //队列中有多个结点时
q->front=q->front->next;
*e=t->data;
free(t);
return true;
}