队列总结:Queue



 



(1)队列的基本知识:



        (1.1)特点:尾进头出,先进先出。(rear in front out)



 



        (1.2)与线性表比较:



        队列是一种插入,删除操作受限的线性表,插入元素的一端为队尾,删除元素的一端为队头。队列中数据元素之间的逻辑关系与线性表中数据元素之间的逻辑关系完全相同,但是他们的运算是不同的。



 



(2)队列的基本操作:



        初始化,判队空,进队,出队。



 



(3)顺序队的基本操作:SequenceQueue



        (3.1)顺序队列图解:



队列总结_legend_出队



        (3.2)顺序队列的缺陷:



        队头队尾指针只会增加不会减少,导致被出队元素的空间无法重新利用。



 



(4)顺序循环队列的基本操作:SeCycleQueue



 



        (4.1)循环队列图解:



队列总结_legend_队列_02



        (4.2)循环队列讲解:



 



                  循环队列的类型定义如下:



 



                  typedefstruct{



                  qElemTypedata[MaxSize];



                  intfront;



                  intrear;



                  }SeQueue;



 



        (4.3)循环队列要素:



                  (一)方法一:



                  (4.3.0)front,rear涵义:



 



                  front:指向队列中队头元素的前一个元素:



                  rear:指向队尾元素:



                  注: rear所指的位置以及填上元素了,所以进队时,rear先后移,然后再赋值。



                  front:指向队头的前一个位置,所以出队,front先后移,然后在赋值。



                  (4.3.1)初始化循环队列:front=rear=-1;



                  (4.3.2)队空:front==rear;



                  (4.3.3)队满:front==(rear+1)%MaxSize;



                  /*rear的下一个位置就是front*/



 



                  (4.3.4)元素elem进队:



                 rear=(rear+1)%MaxSize;



                  data[rear]=elem;



 



                  (4.3.5)出队:



                  front=(front+1)%MaxSize;



                  elem=data[front];



 



                  (4.3.6)队长:



                  len=(rear-front+MaxSize)%MaxSize;



 



                  扩展:已经知道fornt,len求rear:



                  rear=(front+len)%MaxSize;



                  front=(rear-len+MaxSize)%MaxSize;



 



                  (4.3.7)取队头:



                  index=(front+1)%MaxSize;



                  elem=data[index];



                  returnelem;



 



                  (二)方法二:



                  (4.3.0)front,rear涵义:



 



                  rear这个位置还没有填写元素;



                  front这个位置已经填写了元素;



                  所以:进队,先赋值,然后rear++;



                  出队:先赋值,然后front++;



                  (关键是入队与出队对应。其实先++与先赋值没有限定)



 



                  (4.3.1)初始化循环队列:front=rear=0;



                  (4.3.2)队空:front==rear;



                  (4.3.3)队满:front=(rear+1)%MaxSize;



                  (4.3.4)元素elem进队:



                  data[rear]=elem;rear=(rear+1)%MaxSize;



                  (4.3.5)出队:



                  elem=data[front];front=(front+1)%MaxSize;



                  (4.3.6)队长:



                  len=(rear-front+MaxSize)%MaxSize;



                  (4.3.7)取队头:elem=data[front];



 



        (4.4)循环队列基本操作代码实现:以及变形的循环队列之代码实现:



        






 



        



        (4.5)循环队列补充:



        (4.5.1)循环队列的基本操作:



    循环队列中进行出队、入队操作时,头尾指针仍要加1,朝前移动。只不过当头尾指针指向向量上界(QueueSize-1)时,其加1操作的结果是指向向量的下界0。这种循环意义下的加1操作可以描述为:



①方法一:



   if(i+1==QueueSize) //i表示front或rear



       i=0;



   else



       i++;



 



②方法二--利用"模运算"



   i=(i+1)%QueueSize;



   (4.5.2)循环队列的类型定义



 



    #define Queur Size 100  //应根据具体情况定义该值



    typedef char Queue DataType; //DataType的类型依赖于具体的应用



    typedef Sturet{              //头指针,队非空时指向队头元素



          int front;             //尾指针,队非空时指向队尾元素的下一位置



          int rear;              //计数器,记录队中元素总数



          DataType data[QueueSize]



    }CirQueue;



 



(4.5.3)循环队列的基本运算



  用第三种方法,循环队列的六种基本运算:



  ①置队空



     void InitQueue(CirQueue *Q)



     {



             Q->front=Q->rear=0;



             Q->count=0;    //计数器置0



      }



 



  ②判队空



      int QueueEmpty(CirQueue *Q)



      {



           return Q->count==0; //队列无元素为空



       }



  ③判队满



int QueueFull(CirQueue *Q)



       {



           return Q->count==QueueSize; //队中元素个数等于QueueSize时队满



        }



  ④入队



void EnQueue(CirQueuq *Q,DataType x)



        {



           if(QueueFull((Q))                  



                  Error("Queue overflow");    //队满上溢



           Q->count ++;                       //队列元素个数加1



           Q->data[Q->rear]=x;                //新元素插入队尾



           Q->rear=(Q->rear+1)%QueueSize;     //循环意义下将尾指针加1



  ⑤出队



DataType DeQueue(CirQueue *Q)



         {



             DataType temp;



             if(QueueEmpty((Q))



                  Error("Queueunderflow");    //队空下溢



             temp=Q->data[Q->front];



             Q->count--;                       //队列元素个数减1



             Q->front=(Q->front+1)&QueueSize;  //循环意义下的头指针加1



             return temp;



          }



             



  ⑥取队头元素



DataType QueueFront(CirQueue *Q)



           {



               if(QueueEmpty(Q))



                   Error("Queue ifempty.");



               return Q->data[Q->front];



           }



           



----------------------



(5)链式队的基本操作:LinkQueue



 



        (一)不带有头节点的链表:(不推荐此种)



        (5.1)链式队列图解:



队列总结_legend_出队_03



        (5.2)链式列队讲解:



        通常链式队列用不带头节点的链表表示。



 



        节点类型定义:



 



        structqNodeType{



        ElemTypedata;



        structqNodeType* next;



        }QNode;



 



        存放队头,队尾指针的链队类型:



 



        typedefstruct{



        QNode* front;



        QNode* rear;



        }LiQueue;



 



 



        (5.3)链式队列要素:



 



                  (5.3.1)初始化:



                  front=NULL;



                  rear=NULL;



                  (5.3.2)队空:front==NULL&& rear==NULL



                  (5.3.3)队满:不存在



                  (5.3.4)元素elem进队:



 



                  建立一个数据域为elem的新节点pNewNode;将pNewNode插入到队尾,并更新rear.



                  即:Q.rear->next=pNewNode;Q.rear=pNewNode;



                  注:初始时rear==NULL;



                  所以需要:



                  if(NULL==rear)         {front=rear=pNewNode;}



                  else{



                  rear->next=pNewNode;rear=pNewNode;/*更新rear*/



                  }



 



                  (5.3.5)非空出队:



                  QNode* pnode=Q.front->next;



                  ElemTypeelem=pnode->data;/*获取第一个节点的数据域*/



                  Q.front->next=pnode->next;



                  deletepnode;



 



                  注:若只有一个节点且出栈:



                  则以上语句不能保证rear=NULL;



                  所以: QNode *pnode=Q.front->next;



                  ElemTypeelem=pnode->data;/*获取第一个节点的数据域*/



                  Q.front->next=pnode->next;



                  if(pnode->next==NULL){rear=front=NULL;}/*保证只有一个节点,出队后,队空成立*/



                  deletepnode;



        -------------------------------



        (二)方法二:(推荐)



 



        (5.1)队列的基本操作:



 



                  (5.1.1)初始化:QNode*pnode=new QNode; front=rear=pnode;/*避免了初次插入时的rear=NULL的判断*/



                  (5.1.2)队空:front==rear;



                  (5.1.3)元素ele进队:rear->next=pNewNode;rear=pNewNode;



                  (5.1.4)非空出队:



                                    QNode*firstNode=front->next;



                                    ElemTypeelem=firstNode->data;



                                    front->next=firstNode->next;



                                    if(firstNode->next==NULL)



                                    {



                                    rear=front;/*当只有一个节点并且出队,保证队空*/


                                    /*因为如果不更新rear的话,rear是出队前的最后一个节点的指针,即第一个节点的指针。出队前front->next=rear;(只有一个节点时);



                                    出队后:front!=rear,显然不符合队空的定义。



                                    */



                                    }



                                    deletepnode;



 



                  (5.1.5)获取队头:



                           if(front!=rear)return front->next->data;



 



        (5.2)代码实现:




 



 



(6)双端队列:



双端队列的基本实现:





双端队列的应用:滑动窗口



 



 



(7)队列的扩展操作:



        (6.1)迷宫之最短路径问题:



        (6.2)O(1)时间内求队列中最小值:(优先队列)



        (6.3)广度优先搜索:BFS