数据结构【第五天】:队列

队列

只允许在一端进行插入操作,而在另一端进行删除操作的线性表,即是一种先进先出(FIFO)的线性表。

抽象数据类型

ADT 队列(Queue)

Data

    元素具有相同的类型,相邻的元素具有前驱和后继的关系

Operation 

    InitQueue(*Q)    //初始化操作,建立一个空队列Q

    DestroyQueue(*Q)    //若队列Q存在,则销毁它

    ClearQueue(Q)    //将队列Q清空

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

    GetHead(Q,*e)    //若队列存在且非空,用e返回队列Q的队头元素

    EnQueue(*Q,e)   //若队列Q存在,插入新元素e到队列Q的队尾

    DeQueue(*Q,*e)  //删除队列Q的队尾元素,并用e返回其值

    QueueLength(Q)  //返回队列Q的元素个数

endADT


循环队列

队列顺序存储的不足

若使用数组存储队列,下标为0为开始,为n-1为末尾,为了减少操作的复杂度,定义两个指针,front指向队头的元素,rear指向队尾元素的下一个元素,当两指针相等时,队列为空。

缺点在于随着插入和删除操作,rear指针可能移动到数组之外,也称为假溢出,即front指针前面还有空位未被利用。

为解决上述为题,当后面满了,就再从头开始,也就是头尾衔接,这种头尾相接的顺序结构称为循环队列

判断队列是为空队列还是满队列

方法一:设置标志变量flag来判断

方法二:保留一个空闲单元,此时为满,即front = rear时队列为空,(rear+1)%QueueSize == front时队列为满

通用的队列长度计算公式:(rear-front+QueueSize)%QueueSize

结构代码

typedef int QElemType;

typedef struct
{
    QElemType data[MAXSIZE];  //数组
    int front;   //头指针
    int rear;    //尾指针,队列不空指向队列尾元素的下一个位置
}SqQueue;

初始化代码

Status InitQueue(SqQueue *Q)
{
    Q->front = 0;
    Q->rear = 0;
    return ok;
}

求队列长度

int QueueLength(SqQueue Q)
{
    return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}

入队列

Status EnQueue(SqQueue *Q,QElemType e)
{
    if((Q->rear+1)%MAXSIZE == Q->front)    //队列满
        return error;
    Q->data[Q->rear] = e;           //e赋给队尾
    Q->rear=(Q->rear+1)%MAXSIZE;    //指针后移一位
    return ok;
}

出队列

Status EnQueue(SqQueue *Q,QElemType *e)
{
    if(Q->rear == Q->front)    //队列空
        return error;
    *e = Q->data[Q->front];           //队头元素赋给e
    Q->front=(Q->front+1)%MAXSIZE;    //front指针后移一位
    return ok;
}


队列的链式存储结构

本质就是单链表,只是只能尾进头出,简称链队列

头指针front指向头节点,尾指针rear指向尾节点,空队列时,均指向头节点

结构定义

typedef int QElemType;

typedef struct QNode    //节点结构
{
    QElemType data;
    struct QNode *next;
}QNode,*QueuePtr;

typedef struct        //队列的链表结构
{
    QueuePtr front,rear;    //队列的头尾指针
}LinkQueue;

入队操作

Status EnQueue(SqQueue *Q,QElemType e)
{
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));    //建立新节点 
    if(!s)    //内存分配失败
        exit(OVERFLOW);
    s->data = e;      //e赋给队尾
    s->next= NULL;    
    Q->rear->next = s;    //原本节点的下一项指向新节点s
    Q->rear = s;    //把当前队尾节点指向s    
    return ok;
}

出队操作

Status DeQueue(LinkQueue *Q,QElemType *e)
{
    QueuePtr p;
    if(Q->front == Q->rear)    //空栈
        return error;    
    p = Q->front->next   //p指向队首(即头指针下一个)
    *e = p->data;        //获取数据
    if(Q->rear == p)     //若该队首也是队尾
        Q->rear = Q->front;    //尾指针也指向头节点
    free(p);        //释放队首的空间
    return ok; 
}

循环队列与链队列的出入操作均只有一个复杂度O(1),但是空间上循环必须有一个固定的长度,而链队列又有指针域会导致一些空间开销,故在队列长度最大值确定的情况下,建议使用循环队列,而无法估计最大长度时,使用链队列

猜你喜欢

转载自blog.csdn.net/KNOW_MORE/article/details/88172038