队列

1.队列的定义
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。假设队列是q=(a1,a2,......,an),那么a1就是队头元素,而an是队尾元素。这样我们就可以删除时,总是从a1开始,而插入时,列在最后。这也比较符合我们通常生活中的习惯,排在第一个的优先出列,最后来的当然排在队伍最后。
2. 队列的抽象数据类型
同样是线性表,队列也有类似线性表的各种操作,不同的就是插入数据只能在队尾进行,删除数据只能在队头进行。
ADT 队列(Queue)
Data
    同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
Operation
    InitQueue(*Q):    初始化操作,建立一个空队列Q。
    DestroyQueue(*Q): 若队列Q存在,则销毁它。
    ClearQueue(*Q):   将队列Q清空。
    QueueEmpty(Q):    若队列Q为空,返回true,否则返回false。
    GetHead(Q, *e):   若队列Q存在且非空,用e返回队列Q的队头元素。
    EnQueue(*Q, e):   若队列Q存在,插入新元素e到队列Q中并成为队尾元素。
DeQueue(*Q, *e):  删除队列Q中队头元素,并用e返回其值。
    QueueLength(Q):   返回队列Q的元素个数
endADT

3. 循环队列
初始化时,rear = front=0,当队列不为空时,front指向队列中的第一个元素,rear指向队列中最后一个元素的下一个位置,当队列满时 rear=front,但不一定是位置0;
插入后rear+1,删除后front+1,但是,无论是删除还是插入,一旦rear或front加一超过了所分配的空间,则让指针指向这片空间的起始位置;设所分配的空间为Maxsize,一旦rear+1,或front+1 =Maxsize, 则rear或front指向起始位置;如上图,当front和rear都在位置3事,此时如果想要插入,则判断3+1=4 与Maxsize 4的关系,相等,则rear=0,这个可以通过取余实现,rear=(rear+1)%Maxsize;
因此,循环序列判空的方法是rear = front; 判满的方法是 (rear+1)%Maxsize ==front;

4. 进队列步骤

void EnQueue(Queue *Q, int key)
{

        if ( (Q->rear+1) % Q->maxsize == Q->front)                   //此时队列没有空间       取余保证,当quil=queuesize-1时,再转回0
        {
            printf("the queue has been filled full!");
        }
        else
        {
          Q->q[Q->rear] = key;

            Q-> rear =(Q->rear+1) % Q->maxsize;
        }
}

5.出队列步骤

1.判断数列是否为空; 2, 将front现在的时间表保存至temp; 3,将front指针后移一个;4. 返回temp;

int DeQueue(Queue *Q)
{
        int tmp;
        if(Q->rear== Q->front)     //判断队列不为空
        {
            printf("the queue is NULL\n");
        }
        else
        {
            tmp = Q->q[q->front];
            Q->front= (Q->front+1) % Q->maxsize;
        }
        return tmp;
}

6.队列的链式存储结构及实现

队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。为了操作上的方便,我们将队头指针指向链队列的头结点,而队尾指针指向终端结点。空队列时,front和rear都指向头结点。

链队列的结构为:

/* QElemType类型根据实际情况而定,这里假设为int */
typedef int QElemType;       
/* 结点结构 */
typedef struct QNode         
{
    QElemType data;
    struct QNode *next;
} QNode, *QueuePtr;
/* 队列的链表结构 */
typedef struct               
{
    /* 队头、队尾指针 */
    QueuePtr front, rear;    
} LinkQueue;
7.队列的链式存储结构——入队操作

/* 插入元素e为Q的新的队尾元素 */
Status EnQueue(LinkQueue *Q, QElemType e)
{
    QueuePtr s = 
(QueuePtr)malloc(sizeof(QNode));
    /* 存储分配失败 */
    if (!s)               
        exit(OVERFLOW);
    s->data = e;
    s->next = NULL;
    /* 把拥有元素e新结点s赋值给原队尾结点的后继, */
    Q->rear->next = s;    
    /* 见上图中① */
    /* 把当前的s设置为队尾结点,rear指向s,见上图中② */
    Q->rear = s;          
    return OK;
}
8.队列的链式存储结构——出队操作

/* 若队列不空,删除Q的队头元素,用e返回其值,
并返回OK,否则返回ERROR */
Status DeQueue(LinkQueue *Q, QElemType *e)
{
    QueuePtr p;
    if (Q->front == Q->rear)
        return ERROR;
    /* 将欲删除的队头结点暂存给p,见上图中① */
    p = Q->front->next;          
    /* 将欲删除的队头结点的值赋值给e */
    *e = p->data;                
    /* 将原队头结点后继p->next赋值给头结点后继, */
    Q->front->next = p->next;    
    /* 见上图中② */
    /* 若队头是队尾,则删除后将rear指向头结点,见上图中③ */
    if (Q->rear == p)            
        Q->rear = Q->front;
    free(p);
    return OK;
}
9.循环队列与链队列的比较

从时间上,其实它们的基本操作都是常数时间,即都为O(1)的,不过循环队列是事先申请好空间,使用期间不释放,而对于链队列,每次申请和释放结点也会存在一些时间开销,如果入队出队频繁,则两者还是有细微差异。对于空间上来说,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链队列更加灵活。
总的来说,在可以确定队列长度最大值的情况下,建议用循环队列,如果你无法预估队列的长度时,则用链队列。

10.总结

栈(stack)是限定仅在表尾进行插入和删除操作的线性表。
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

它们均可以用线性表的顺序存储结构来实现,但都存在着顺序存储的一些弊端。因此它们各自有各自的技巧来解决这个问题。
对于栈来说,如果是两个相同数据类型的栈,则可以用数组的两端作栈底的方法来让两个栈共享数据,这就可以最大化地利用数组的空间。

对于队列来说,为了避免数组插入和删除时需要移动数据,于是就引入了循环队列,使得队头和队尾可以在数组中循环变化。解决了移动数据的时间损耗,使得本来插入和删除是O(n)的时间复杂度变成了O(1)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值