【数据结构学习记录9】——双向队列

一.概述

因为队列的特殊构造,所以我们一般就像上节那样,使用链式列表。但是把,有些时候,顺序结构有顺序结构的好处,所以我们也是可以通过某种方式来用顺序表实现列表。那就是通过循环队列来实现。

二.原理实现

1.节点

因为是顺序结构,所以我们节点不需要其他的东西,只需要储存数据就可以了。

2.循环队列结构

因为循环队列的特殊机制,我们一般不考虑对队列进行扩容。那么就因该有一个固定的长度MaxSize。一个队列因该包含三个部分基址*basefront头指针tail尾指针。因为是循环的,所以我们的尾指针的值,可能会在头指针的前面。比如MaxSize=6 front=4 tail=2时,我们的队列长度因该是4 5 0 1这4个位置,计算的话,因该是(tail-front+MaxSize)%Maxsize

3.队列的初始化以及空队列判断

队列初始化,直接动态生成*base的储存空间,然后再将front=tail=0。如果是空队列,那么因该front=tail
在这里插入图片描述

4.队列队满的判断

我们队列满的时候,有两种情况,头在尾前:tail=MaxSize-1front=0;还有一种功能情况就是尾在头前:tail+1=front。那么,总结一下,它的判断因该是(tail+1)%MaxSize=front

5.队列的入队

入队的话,我们只需要把tail的地方写入我们插入的值即可,然后再++tail但是可能会出现tail+1=MaxSize的情况,所以我们的需要用一个if或者计算来修改它的值。个人推荐使用计算tail = (tail+1)%MaxSize。不过理论上来说,用if来更新也不会出错。但是有时候,我们可能会用到快慢指针的方式做一些题,所以用计算的这种方式的容错率可能会好很多。
但注意,如果我们先判断队列是否满的话会导致队列最大的存储元素个数是Maxsize-1,因为tail始终是尾元的位置,它是直接可用的,而判满是tail+1=front,所以导致了最后一个元素没法用。除非我们修改下判断队满的方式,就如下图J8实际上是上因该是没有数据的:
在这里插入图片描述

6.队列的出队

出队和入队相似,但是顺序结构我们不需要对地址做什么,只需要把头指针front+1就可以了。当然,也还有超过MaxSize的情况,所以我们还是得采用一些容错机制:front = (front+1)%MaxSize

三.代码实现

#include <stdio.h>
#include <stdlib.h>

#define ERROR       0
#define OK          1
#define OverFlow    2
#define MaxSize     5

typedef struct dataType{
    
    
    int data;
}dataType;

typedef struct cqueue{
    
    
    dataType *base;
    int front;
    int tail;
}cqueue;

cqueue* CqueueInit(void);
int CqueuePush(cqueue* q, dataType data);
int CqueueGet(cqueue *q, dataType *data);
int CqueuePop(cqueue *q);
int CqueueShow(cqueue *q);

int main()
{
    
       
    // 主函数随便改
    cqueue *q = CqueueInit();
    dataType test;
    test.data = 0;
    CqueuePush(q,test);
    test.data = 1;
    CqueuePush(q,test);
    test.data = 2;
    CqueuePush(q,test);
    test.data = 3;
    CqueuePush(q,test);
    CqueuePop(q);
    test.data = 4;
    CqueuePush(q,test);
    CqueueShow(q);
    return 0;
}

cqueue* CqueueInit(void)
{
    
    
    cqueue *q = (cqueue*)malloc(sizeof(cqueue));
    dataType *data = (dataType*)malloc(sizeof(dataType)*MaxSize);
    if (q != NULL && data != NULL)
    {
    
    
        q->base = data;
        q->front = 0;
        q->tail = 0;    // 初始化循环队列
        return q;
    }
    exit(0);
}

int CqueuePush(cqueue* q, dataType data)
{
    
    
    if ((q->tail+1)%MaxSize == q->front) return OverFlow;
    // 注意这样的写法会导致最后有一个节点存不到数据
    *(q->base+q->tail) = data;
    q->tail += 1;   
    return OK;
}

int CqueueGet(cqueue *q, dataType *data)
{
    
    
    if ((q->tail - q->front)%MaxSize == 0)
    {
    
    
        return ERROR;   
    }
    else
    {
    
    
        *data = *(q->base+q->tail);     //  返回头指针指向的值
        return OK;
    }
}

int CqueuePop(cqueue *q)
{
    
    
    q->front += 1;      // 更新一下指向的元素
    return OK;
}

int CqueueShow(cqueue *q)   // 这个函数就打印一下队列 看看效果
{
    
    
    int start,end = 0;
    int cont = 0;
    if (q->tail < q->front) // 队尾在队头的前面
    {
    
    
        end = q->tail + MaxSize;    
    }
    else
    {
    
    
        end = q->tail;  // 队头在队尾的前面
    }
    for(start = q->front; start < end; ++start)
    {
    
    
        printf("No.%d data is %d\n", cont, (q->base+start)->data);
        ++cont;
    }
}

猜你喜欢

转载自blog.csdn.net/u011017694/article/details/109459539