队列

---------------队列的基本概念

               1. 队列的定义

                  队列:队列简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端

              进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。这和我们日常生活中的

              排队是一致的,最早排队的也是最早离队的。其操作的特性是先进先出(First In First Out , FIFO)

              故又称为先进先出的线性表。如下图所示:

                  

                   队头:允许删除的一端,又称为队首。

                   队尾:允许插入的一端。

                   空队列:不含任何元素的空表

              2. 队列常见的基本操作

                  InitQueue(&Q) : 初始化队列,构造一个空队列 Q

                  QueueEmpty( Q ) :  判断列空,若队列 Q 为空返回 true , 否则返回 false

                  EnQueue( &Q , x ) : 入队,若队列 Q 未满,将 x 加入,使之成为新的队尾。

                  DeQueue( &Q , &x ) : 出队,若队列 Q 非空,删除队头元素,并用 x 返回 。

                  GetHead( Q , &x ) : 读队头元素,若队列 Q 非空,则将队头元素赋值给 x 。

                需要注意的是,队列是操作受限制的线性表,所以,不是任何线性表的操作都能作为

          队列的操作。比如,不可以随意读取队列中间的某个数据。

---------------队列的顺序存储结构

               1. 队列的顺序存储

                   队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针

               front 和 rear 分别指向队头元素和队尾元素。设队头指针指向队头元素,队尾指针指向

               队尾元素的下一个位置,也可以让 rear 指向队尾元素,front 指向队头元素的前一个位

               置,对于这种设置方式,请读者根据下图思考出队和入队后两个指针的变化。

                  

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

                  队列的顺序存储类型可描述为:

                 #define  MaxSize  50                                     // 定义队列中元素的最大个数

                 typedef  struct {

                       ElemType  data[ MaxSzie ] ;                   // 存放队列元素

                       int  front , rear ;                                       // 队头指针和队尾指针

                 } SqQueue ;

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

                     初始状态(队空条件): Q.front == Q.rear == 0 ;

                     进队操作:队不满时,先送值到队尾元素,再将队尾指针加 1 。

                     出队操作:队不空时,先取队头元素值,再将队头指针加 1 。

                     上图中的(a) 所示为队列的初始状态,有 Q.front == Q.rear == 0 成立,该条件可以

              作为队列判空的条件。但能否用 Q.rear == MaxSize 作为队列满的条件呢? 显然是不能,如

             上图中的(d) 中,队列中仅有 1 个元素,但仍满足条件。这时入队出现 “ 上溢出 ” ,但这种

             溢出并不是真正的溢出,在 data 数组中依然存在可以存放元素的空位置,所以是一种

            “ 假溢出”

               2. 循环队列

                       前面已经指出了顺序队列的缺点,这里我们引入循环队列的概念。将顺序队列臆造为

                环状的空间,即把存储队列从逻辑上看成一个环,称为循环队列。当队首指针 Q.front =

                MaxSize -1 后,再前进一个位置就自动到 0 ,这可以利用除法取余运算(%)来实现。

                        初始化: Q.front == Q.rear == 0 ;

                        队首指针进1 : Q.front = ( Q.front + 1 ) % MaxSize ;

                        队尾指针进1 : Q.rear = ( Q.rear + 1 ) % MaxSize ;

                        队列长度 : ( Q.rear   + MaxSize -  Q.front ) % MaxSize ;

                   出队入队时:指针都按顺时针方向进 1 。(如下图所示)

                  

                         那么,循环队列队空和队满的判断条件是什么呢??显然,队空的条件是 Q.front == Q.rear 。

                 如果入队元素的速度快于出队元素的速度,队尾指针很快就能赶上了队首指针,如上图 ( d ) 所示,

                此时可以看出队满时 也有  Q.front == Q.rear 。

                         为了区分队空还是队满的情况,有三种处理方式:

                         (1) 、牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的

                              做法,约定以 “ 队头指针在队尾指针的下一个位置作为队满的标志 ” 如上图 ( d2 ) 所示。

                              队满条件为: ( Q.rear  + 1 ) % MaxSize == Q.front ;

                              队空条件为: Q.front == Q.rear ;

                              队列中元素的个数: ( Q.rear - Q.front + MaxSize ) % MaxSize ;

                        (2)、类型中增设表示元素个数的数据成员。这样,则队空的条件为 Q.size == 0 ;

                              队满的条件为 Q.size == MaxSize 。这两种情况都有  Q.front == Q.rear ;

                        (3)、类型中增设 tag 数据成员,以区分是队满还是队空。tag 等于 0  的情况下,若

                             因删除导致 Q.front == Q.rear ;  则队列为空; tag 等于 1 的情况下, 若因插入导致

                             Q.front == Q.rear 则队列为满。(tag == 0 ,表示进行的是删除操作 ; tag == 1 表

                            示进行的插入操作

               3. 循环队列的操作(牺牲一个单元来区分队空和队满

                  (1)初始化

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

                    void InitQueue( &Q ) {

                        Q.front = Q.rear = 0 ;                                   // 初始化队首、队尾指针

                    }

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

                  (2)判队空

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

                   bool isEmpty( Q ) {

                        if ( Q.front = Q.rear  ){                        // 队空条件

                               return true ;

                        } else {

                               return  false ; 

                        }

                   }

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

                  (3)入队

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

                   bool  EnQueue ( SqQueue  &Q , ElemType e ){

                            if ( ( Q.rear  + 1 ) % MaxSize == Q.front ){

                                        return false ;                                         // 队满

                            }

                            Q.data[ Q.rear ] = x ;

                            Q.rear = ( Q.rear  + 1 ) % MaxSize ;                  // 队尾指针加 1 取模

                            return  true ;

                   }

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

                  (4) 出队

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

                 bool DeQueue( SqQueue  &Q , ElemType &e ){

                            if ( Q.front = Q.rear ) {

                                 return false ;                              //  队空,报错

                            }

                           x = Q.data[ Q.front  ] ;

                           Q.front = ( Q.front  + 1 ) % MaxSize ;     // 队头指针加 1 取模

                           return true ;

                 }

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

---------------队列的链式存储结构

               1. 队列的链式存储

                 队列的链式表示称为链队列,它实际上是一个同时带有队头指针队尾指针的单链表。

               头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点(注意与顺序存

               储的不同)

                  队列的链式存储如下图所示:

                 

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

                队列的链式存储类型可描述为:

                     typedef  struct {                                    //  链式队列结点

                          Elemtype  data ;

                          struct LinkNode  * near ;

                    } LinkNode ;

                    typedef  struct {                                    // 链式队列

                          LinkNode  * front , *next ;                // 队列的队头和队尾指针

                   } LinkQueue ;

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

                  当  Q.front == NULL 且  Q.rear == NULL  时,链式队列为空。

               》》 出队时,首先判断队是否为空,若不空,则取出队头元素,将其从链表中摘除,并

                 让 Q.front 指向下一个结点(若该结点为最后一个结点,则置 Q.front 和 Q.rear 都为

                 NULL )。

                      入队时,建立一个新的结点,将新结点插入到链表的尾部,并让 Q.rear 指向这个

                 新插入的结点(若原队列为空,则令 Q.front 也指向该结点)。

              》》 不难看出,不设头结点的链式队列在操作上往往比较麻烦,因此,通常将链式队列

                  设计成一个带头结点的单链表,这样插入和删除操作都统一了,如下图所示:

               

                 用单链表表示的链式队列特别适合于数据元素变动比较大的情形,而且不存在队列满且

             产生溢出的问题。另外,假如程序中要使用多个队列,与多个栈的情形一样,最好使用链式

            队列,这样就不会出现存储分配不合理和 “ 溢出 ” 的问题。 

           2. 链式队列的基本操作

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

             (1)、初始化

              void InitQueue( LinkQueue &Q){

                        Q.front  = Q.rear = ( LinkNode *)malloc (sizeof ( LinkNode ) ) ;  // 建立头结点

                        Q.front -> next = NULL ;                                                        // 初始为空

              }          

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

              (2)、判队空

               bool IsEmpty( LinkQueue Q){

                       if ( Q.front == Q.rear ){

                             return true;

                       }else {

                             return false ;

                      }

               }

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

              (3)、入队

                void EnQueue( LinkQueue &Q , ElemType  x){

                       s  = ( LinkNode *)malloc (sizeof ( LinkNode ) ) ;

                       s->data = x ;                                 //创建新结点,插入到链尾

                       s->next = NULL;

                       Q.rear -> next = s ;

                      Q.rear = s ;

                }

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

               (4)、出队

                bool DeQueue ( LinkQueue &Q , ElemType  &x){

                      if ( Q.front == Q.rear ){

                               return false ;                         // 空队

                       }

                     p  = Q.front ->next ;

                     x = p -> data  ;

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

                     if ( Q.rear == p ){

                                Q.rear   = Q.front ;                         // 若原队列中只有一个结点,删除后变空

                     }

                    free ( p ) ;

                    return true ;

                }

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

---------------双端队列

         双端队列是指允许两端都可以进行入队和出队操作的队列。如下图所示,其元素的逻辑结构仍然是线性

     结构。将队列的两端分别称为 前端 和 后端 , 两端都可以入队和出队。

     

   

        在双端队列进队时: 前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中

                                     前端进的元素的后面。

       在双端队列出队时: 无论前端还是后端队列,先出的元素排列在后出的元素的前面。

      》》 输出受限的双端队列:   允许在一端进行插入和删除,但在另一端只允许插入的双端队列称为

                                     输出受限的双端队列。(如下图所示)

             

      》》 输入受限的双端队列: 允许在一端进行插入和删除,但在另一端只允许删除的双端队列称为

                                      输入受限的双端队列,如下图所示。而如果限定双端队列从某个端点插入的

                                      元素只能从该端点删除,则该双端队列就蜕变成了两个栈底相接的栈了。

                

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小达人Fighting

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值