概述
只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表 (头删尾插)
与同线性表相同,仍为一对一关系。
顺序队或链队,以循环顺序队更常见
只能在队首和队尾运算,且访问结点时依照先进先出(FIFO)的原则。
关键是掌握入队和出队操作,具体实现依顺序队或链队的不同而不同。
基本操作有入队或出队,建空队列,判队空或队满等操作。
队列 (Queue)是仅在表尾进行插入操作,在表头进行删除操作的线性表。
表尾即 an 端,称为 队尾 ; 表头即 a1 端,称为队头。
插入元素称为入队;删除元素称为出队。
它是一种先进先出(FIFO)的线性表。
队列的存储结构为链队或顺序队
(常用循环顺序队)
讨论:
空队列的特征?front=rear
队列会满吗?一般不会,因为删除时有free动作。除非内存不足
怎样实现入队和出队操作?
入队(尾部插入):rear->next=S; rear=S;
出队(头部删除):front->next=p->next;
操作
构造一个空队列
Status InitQuene( LinkQuene &Q )
{
Q.front = Q.rear = new QNode;
if ( !Q.front ) exit ( OVERFLOW);
Q.front —>next = NULL;
return OK;
}
销毁队列
Status DestroyQuene ( LinkQuene &Q)
{
while ( Q.front )
{
Q.rear = Q.front—>next;
delete Q.front;
Q.front = Q.rear;
}
return OK;
}
入队
Status EnQuene ( LinkQuene &Q, QElemType e)
{ //插入元素e为Q的新的队尾元素
p = new Qnode;
if ( !p) exit (OVERFLOW);
p—>data = e; p—>next = NULL;
Q.rear—>next = p;
Q.rear = p;
return OK;
}
出队
Status DeQuene ( LinkQuene &Q, QElemType &e)
{ //若队列不空,则删除Q的队头元素,用e返回其值,并返回OK;
// 否则返回ERROR
if ( Q.front = = Q.rear ) return ERROR;
p = Q.front —>next;
e = p —>data;
Q.front —>next = p —>next;
if ( Q.rear = = p) Q.rear = Q.front;
delete p;
return OK;
}
顺序队
#define MAXSIZE 100;
typedef struct {
SElemType *base;
int front;
int rear;
}SqQueue;
假溢出与循环队列
什么叫“假溢出” ?如何解决?
在顺序队中,当尾指针已经到了数组的上界,不能再有入队操作,但其实数组中还有空位置,这就叫“假溢出”。
解决假溢出的途径———采用循环队列
新问题:在循环队列中,空队特征是front=rear;队满时也会有front=rear;判决条件将出现二义性!
解决方案有三:
① 加设标志位,让删除动作使其为1,插入动作使其为0,则可识别当前 front=rear
② 使用一个计数器记录队列中元素个数(即队列长度);
③ 人为浪费一个单元,令队满特征为front=(rear+1)%N;
循环队列中的“长度”到底是指总长度还是数据元素个数?
答:一般情况下的长度(L)是指元素个数(不含头结点或空闲单元),而maxsize N 才是指所有单元总数,即“容量”。
为什么要设计队列?它有什么独特用途?
离散事件的模拟(模拟事件发生的先后顺序);
操作系统中多道作业的处理(一个CPU执行多个作业);
简化程序设计。
循环队列的操作
初始化一空队列
Status InitQueue ( SqQueue &q )
{//初始化空循环队列 q
q . base=(QElemType *)malloc(sizeof(QElemType)
* QUEUE_MAXSIZE);
//分配空间
if (!q.base) exit(OVERFLOW); //失败,退出程序
q.front =q.rear=0; //置空队列
return OK;
}//InitQueue;
入队操作
Status EnQueue(SqQueue &q, QElemType e)
{//向循环队列 q 的队尾加入一个元素 e
if ( (q.rear+1) % QUEUE_MAXSIZE = = q.front )
return ERROR ;
q.base [ q.rear ] = e; //存储
q.rear = ( q . rear + 1 ) % QUEUE_MAXSIZE;//指针后移
return OK;
}// EnQueue;
出队操作
在删除前应当判断队列是否空;
if (q.front = q.rear ) return ERROR;
插入动作分析;
因为队头指针front指向队头元素的位置,所以:
e = q.base [ q.front ] ;
q.front = ( q . front + 1 ) % QUEUE_MAXSIZE;
Status DeQueue ( SqQueue &q, QElemType &e)
{//若队列不空,删除循环队列 q 的队头元素,
//由 e 返回其值,并返回OK
if ( q.front = = q.rear ) return ERROR;//队列空
e = q.base [ q.front ] ; //队头元素出队
q.front=(q.front+1) % QUEUE_MAXSIZE ;
//指针后移
return OK;
}// DeQueue
小结
线性表、栈与队的异同点
相同点:逻辑结构相同,都是线性的;都可以用顺序存储或链表存储;栈和队列是两种特殊的线性表,即受限的线性表(只是对插入、删除运算加以限制)。
不同点:
① 运算规则不同,线性表为随机存取,而栈是只允许在一端进行插入和删除运算,因而是后进先出表LIFO;队列是只允许在一端进行插入、另一端进行删除运算,因而是先进先出表FIFO。
② 用途不同,线性表比较通用;堆栈用于函数调用、递归和简化设计等;队列用于离散事件模拟、多道作业处理和简化设计等。