【栈和队列】队列

队列 ,又一种特殊的线性表


队列的定义

队列(Queue)是只容许在一端进行插入,而在另外一端进行删除的运算受限的线性表html

容许删除的一端称为队头(Front)web

容许插入的一端称为队尾(Rear)算法

当队列中没有元素时称为空队列
测试

队列的修改是依先进先出的原则进行的。动画

新来的成员老是加入队尾(即不容许"加塞"),每次离开的成员老是队列头上的(不容许中途离队),即当前"最老的"成员离队。ui


 



队列的基本逻辑运算

(1)InitQueue(Q)spa

置空队。构造一个空队列Q。

(2)QueueEmpty(Q)3d

判队空。若队列Q为空,则返回真值,不然返回假值。

(3) QueueFull(Q)指针

     判队满。若队列Q为满,则返回真值,不然返回假值。
   注意:
     此操做只适用于队列的顺序存储结构。

(4) EnQueue(Q,x)code

     若队列Q非满,则将元素x插入Q的队尾。此操做简称 入队

(5) DeQueue(Q)

     若队列Q非空,则删去Q的队头元素,并返回该元素。此操做简称 出队

(6) QueueFront(Q)

     若队列Q非空,则返回队头元素,但不改变队列Q的状态。


队列的分类


顺序队列:


(1)定义 
队列的顺序存储结构称为顺序队列,顺序队列其实是运算受限的顺序表。
须要两个变量frontrear 分别指示队列头元素队列尾元素的位置!



(2) 基本操做
  1. 入队:将新元素插入 rear 所指的位置,而后将rear加1。
  2. 出队:删去front所指的元素,而后将front加1并返回被删元素。
注意:
  1. front = rear 时,队列为空
  2. 在非空队列里,队头指针始终指向队头元素,尾指针始终指向队尾元素的下一位置。
(3)溢出现象
  1.     "下溢"现象:当队列为空时,作 出队 运算产生的溢出现象。“下溢”是正常现象,经常使用做程序控制转移的条件。
  2. "真上溢"现象:当队列满时,作 进队 运算产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。
  3. "假上溢"现象:因为入队和出队操做中,头尾指针只增长不减少,导致被删元素的空间永远没法从新利用。当队列中实际的元素个数远远小于向量空间的规模时,也可能因为尾指针已超越向量空间的上界而不能作入队操做。该现象称为"假上溢"现象。为了解决这个问题,咱们可使用‘循环队列’。(因此不少状况下,咱们不会使用普通的顺序表)



循环队列:


为了克服顺序队列的 "假上溢"现象,将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。
 

(1)  循环队列的类型定义
 
#define MaxQSize 100   //应根据具体状况定义该值
typedef char  QElemType;  //QElemType的类型依赖于具体的应用

typedef struct
{                  
	QElemType * base;	//初始化的动态分配存储空间
	int front;			//头指针,队非空时指向队头元素              
	int rear;			//尾指针,队非空时指向队尾元素的下一位置           
}CirQueue;

(2)循环队列的基本操做

跟普通顺序表同样,循环队列中进行出队、入队操做时,头尾指针仍要加1,朝前移动。
只不过当头尾指针指向向量上界(MaxQSize-1)时,其加1操做的结果是指向向量的下界0。
这种循环意义下的加1操做能够描述为:
方法一:
if(i+1 == MaxQSize) // i 表示front或rear
	i=0;
else
    i++;

方法二:利用"模运算"
i = (i+1) % MaxQSize;

(3) 循环队列边界条件处理
循环队列中,因为入队时尾指针向前追赶头指针;出队时头指针向前追赶尾指针,形成队空和队满时头尾指针均相等。所以,没法经过条件front==rear来判别队列是"空"仍是"满"。 【 参见动画演示
解决这个问题的方法至少有三种:
  1. 另设一布尔变量以区别队列的空和满;
  2. 少用一个元素的空间。约定入队前,测试尾指针在循环意义下加1后是否等于头指针,若相等则认为队满(注意:rear所指的单元始终为空);
  3. 使用一个计数器记录队列中元素的总数(即队列长度)。

(4) 循环队列的基本运算(咱们使用第二种方法)

构造空队列
bool InitQueue(CirQueue * Q)
{
	Q->base = (QElemType *)malloc(MaxQSize * sizeof(QElemType));
	if(!Q->base) exit(OVERFLOW);	//分配内存失败
	Q->front = Q->rear = 0;			//初始化为空栈
	return true;
}


判队空
bool QueueEmpty(CirQueue * Q)
{//由于少用一个元素,因此只有空队列才有 front = rear
	if (Q->front == Q->rear)
	{
		return true;
	}
	else 
		return false;
}


判队满
bool QueueFull(CirQueue * Q)
{//若是插入后尾指针的位置等于头指针的位置,则说明队列满了
	if ((Q->rear + 1) % MaxQSize == Q->front)
	{
		return true;
	}
	else
		return false;
}


入队
bool EnQueue(CirQueue * Q, QElemType e)
{//插入元素e
	if (QueueFull(Q))
	{//若是队满,则返回false
		return false;
	}
	Q->base[Q->rear] = e;	//插入元素
	Q->rear = (Q->rear + 1) % MaxQSize;	//修改尾指针
	return true;
}


出队
bool DeQueue(CirQueue * Q, QElemType * e)
{
	if (QueueEmpty(Q))
	{//若是队空,则返回false
		return false;
	}
	*e = Q->base[Q->front];	//返回元素值
	Q->front = (Q->front + 1) % MaxQSize;	//修改头指针
}


链队列 :


(1)链队列的定义 
队列的链式存储结构简称为链队列。它是限制仅在表头删除和表尾插入的单链表。


注意:
有一个头结点!

(2)链队列的类型定义
typedef struct QNode
{
	QElemType data;			//数据域
	struct QNode * next;	//指针域
}QNode, *QueuePtr;

typedef struct
{
	QueuePtr front;	//队头指针
	QueuePtr rear;	//队尾指针
}LinkQueue;

(3)链队列的基本运算
构造空队
bool InitQueue(LinkQueue * Q)
{
	Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));//建立头结点
	if (!Q->front) 
	{//若是内存分配失败
		exit(OVERFLOW);
	}
	Q->front->next = NULL;	//初始化空队列
	return true;
}

销毁队列
bool DestroyQueue(LinkQueue * Q)
{
	while(Q->front)
	{
		Q->rear = Q->front->next;//用rear暂时存储新的队头指针
		free(Q->front);	//释放内存
		Q->front = Q->rear;
	}
	return true;
}

入队
bool EnQueue(LinkQueue * Q, QElemType e)
{
	QueuePtr p = (QueuePtr)malloc(sizeof(QNode));//为新元素分配内存
	if (!p)
	{
		exit(OVERFLOW);
	}
	p->data = e;	//初始化新结点
	p->next = NULL;
	Q->rear->next = p;	//修改尾指针
	Q->rear = p;
	return true;
}


出队
bool DeQueue(LinkQueue * Q, QElemType * e)
{
	if (Q->front == Q->rear)
	{//若是队空
		return false;
	}
	QueuePtr p = Q->front->next;	//保存第一个结点的位置
	*e = p->data;
	Q->front->next = p->next;	//第一个结点出队
	if (Q->rear == p)
	{//若是只有一个结点,置为空队
		Q->rear = Q->front
	}
	free(p);
	return true;
}

注意:
和链栈相似,无须考虑判队满的运算及上溢。

在出队算法中,通常只需修改队头指针。但当原队中只有一个结点时,该结点既是队头也是队尾,故删去此结点时亦需修改尾指针,且删去此结点后队列变空。