• 数据结构(二)栈与队列---队列


    (一)定义

    是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
    与栈相反,队列是先进先出(First In First Out:FIFO)的线性表、
    与栈相同:队列也是一种重要的线性结构,实现一个队列同样需要顺序表或者链表来作为基础

    (二)结构

    例如键盘缓冲区就是队列形式接收输入输出的。

    (三)队列的抽象数据类型

    ADT 队列(Queue)
    
    Data
        同线性表。元素具有相同的类型,相邻元素具有前驱和后继的关系。
    
    Operation
        InitQueue( *Q): 初始化操作,建立一个空队列Q
        ClearQueue( *Q): 将队列清空
        QueueEmpty( Q): 若队列为空,返回true,否则返回false
        QueueLength( Q): 返回队列Q的元素个数
    
        GetHead( Q, *e): 若是队列存在且非空,用e返回Q的队头元素
        EnQueue( *Q, e):若是队列存在,则插入新的元素e入队为队尾
        DeQueue( *Q, *e):若是队列存在且非空,进行出队操作,用e接收数据
        DestroyQueue( *Q): 若是队列存在,则销毁他
    endADT

    (四)存储结构

    队列既可以使用链表实现,也可以使用顺序表来实现。跟栈相反的是,栈一般使用顺序表来实现,而队列常使用链表来实现,简称链队列
    //设置队列的数据结点
    typedef struct QNode
    {
        ElemType data;    //存放队列中的数据
        struct QNode* next;    //队列结点的指针域
    }QNode, *QNodePtr;
    
    //设置队列的结构体
    typedef struct
    {
        QNodePtr front,rear;    //队列头尾指针
    }LinkQueue;

    (五)队列的链式存储结构

    我们将个队头指针指向链队列的头结点,而队尾指针指向终端结点(注:头结点不是必须的,这里为了操作结点使用了)

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <stdlib.h>
    
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    
    typedef int ElemType;
    typedef int Status;
    
    //设置队列的数据结点
    typedef struct QNode
    {
        ElemType data;    //存放队列中的数据
        struct QNode* next;    //队列结点的指针域
    }QNode, *QNodePtr;
    
    //设置队列的结构体
    typedef struct
    {
        QNodePtr front,rear;    //队列头尾指针
    }LinkQueue;
    
    //四个基础操作
    Status InitQueue(LinkQueue *Q);    //初始化操作,建立一个空队列Q
    Status ClearQueue(LinkQueue *Q);//将队列清空
    Status QueueEmpty(LinkQueue Q);    //若队列为空,返回true,否则返回false
    int QueueLength(LinkQueue Q);    //返回队列Q的元素个数
    
    Status GetHead(LinkQueue Q, ElemType *e);    //若是队列存在且非空,用e返回Q的队头元素
    Status EnQueue(LinkQueue *Q, ElemType e);    //若是队列存在,则插入新的元素e入队为队尾
    Status DeQueue(LinkQueue *Q, ElemType *e);    //若是队列存在且非空,进行出队操作,用e接收数据
    Status DestroyQueue(LinkQueue *Q);            //若是队列存在,则销毁他
    
    int main()
    {
        LinkQueue lq;
        ElemType e;
        int i;
        //初始化一个空的队列
        InitQueue(&lq);
    
        printf("2.EnQueue 1-5
    ");
        for (i = 1; i <= 5; i++)
            EnQueue(&lq, i);
        printf("3.DeQueue number for three times
    ");
        for (i = 1; i <= 3;i++)
        {
            DeQueue(&lq, &e);
            printf("DeQueue %d: %d
    ",i, e);
        }
        GetHead(lq, &e);
        printf("4.Get Head:%d
    ",e);
        printf("5.EnQueue 6-10
    ");
        for (i = 6; i <= 10; i++)
            EnQueue(&lq, i);
        printf("6.Get queue length:%d
    ", QueueLength(lq));
        printf("7.DeQueue number for six times
    ");
        for (i = 1; i <= 6; i++)
        {
            DeQueue(&lq, &e);
            printf("DeQueue %d: %d
    ",i, e);
        }
        if (!QueueEmpty(lq))
        {
            printf("8.Queue is not Empty
    ");
            ClearQueue(&lq);
            printf("9.Queue is Clear
    ");
        }
        printf("10.Queue Empty:%d
    ", QueueEmpty(lq));
        printf("11.destroy Queue");
        DestroyQueue(&lq);
        system("pause");
        return 0;
    }
    
    //初始化操作,建立一个空队列Q
    Status InitQueue(LinkQueue *Q)
    {
        if (!Q)
            return ERROR;
        Q->front = Q->rear = (QNodePtr)malloc(sizeof(QNode));
        if (!Q->front)
            return ERROR;
        Q->front->next = Q->rear->next = NULL;
        return OK;
    }
    
    //将队列清空,保留头结点,注意队尾指针
    Status ClearQueue(LinkQueue *Q)
    {
        QNodePtr head = Q->front->next;    //获取开始结点
        QNodePtr cur;    //游标指针
        if (!Q)
            return ERROR;
        while (head)    //将数据全部释放
        {
            cur = head;
            head = head->next;
            free(cur);
        }
        Q->rear = Q->front;    //将队尾指向队头
        Q->rear->next = Q->front->next = NULL;    //记得:重点
        return OK;
    }
    
    //若队列为空,返回true,否则返回false
    Status QueueEmpty(LinkQueue Q)
    {
        if (!Q.front->next)
            return TRUE;
        return FALSE;
    }
    
    //返回队列Q的元素个数
    int QueueLength(LinkQueue Q)
    {
        QNodePtr head = Q.front;    //获取头结点
        QNodePtr end = Q.rear;    //获取终端结点
        int length = 0;
        while (head!=end)
        {
            length++;
            head = head->next;
        }
        return length;
    }
    
    //若是队列存在且非空,用e返回Q的队头元素
    Status GetHead(LinkQueue Q, ElemType *e)
    {
        QNodePtr head;
        if (!e||QueueEmpty(Q))
            return ERROR;
        head = Q.front->next;
        *e = head->data;
        return OK;
    }
    
    //注意:对于队列,我们更多关心队尾指针多余队头指针
    
    //若是队列存在,则插入新的元素e入队为队尾,注意还要考虑队尾指针
    Status EnQueue(LinkQueue *Q, ElemType e)
    {
        if (!Q)
            return ERROR;
        QNodePtr q = (QNodePtr)malloc(sizeof(QNode));
        if (!q)
            return ERROR;
        q->data = e;
        q->next = Q->rear->next;
        Q->rear->next = q;
        Q->rear = q;
        return OK;
    }
    
    //若是队列存在且非空,进行出队操作,用e接收数据,注意还要考虑队尾指针
    Status DeQueue(LinkQueue *Q, ElemType *e)
    {
        QNodePtr q;
        if (!Q || !e || QueueEmpty(*Q))
            return ERROR;
        q = Q->front->next;    //开始结点
        *e = q->data;
        Q->front->next = q->next;    //指针后移(这一步注意:重点,且易错)
        if (Q->rear == q)    //若是我们队列中只有一个结点,删除后需要修改队尾指针
            Q->rear = Q->front;
        free(q);    //释放结点
        return OK;
    }
    
    //若是队列存在,则销毁他,包含头结点
    Status DestroyQueue(LinkQueue *Q)
    {
        if (!Q)
            return OK;
        if (ClearQueue(Q))
        {
            free(Q->front);
            Q->front = Q->rear = NULL;
            return OK;
        }
        return ERROR;
    }

    (六)队列的顺序存储结构(了解思想即可)

     (1)顺序队列:队头指针不变

    上面的顺序队列:在出队时需要移动顺序队列中所有的元素,时间复杂度O(n),需要改进

     (2)顺序队列:队头指针移动

    缺点:队尾指针逐渐增加,而队头也增加,那么数组的可用空间会减少。前面的红色区域会造成空间浪费。

    (3) 顺序队列:循环队列

    成功解决上面两种情况的缺点,不足之处是对于数组溢出没有办法

    注意:和链队列相比

    链队列的队尾指针是执行最后的那个元素结点。
    而循环链表则是指向下一个空数组下标。(当然我们也可以执行最后一个有数据的下标) 

    (七)实现循环队列(可以使用堆,也可以使用数组,这里直接使用数组)

     注意:考虑对于队尾在队头后面和前面两种情况下的长度,如何使用一种方法来表示?还有判断队满是否也可以使用一种方法表示?还有判断队尾,队头下标方法是否一致?

    从上面的循环队列分析:
    长度可以使用-->(队尾下标-队头下标+数组长度)%数组长度
    判断是否队满-->(队尾下标+1)%数组长度==队头下标
    判断队尾,队头下标-->(队尾下标+1)%数组长度==新的队尾下标
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <stdlib.h>
    
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    
    #define MAXSIZE 10
    
    typedef int ElemType;
    typedef int Status;
    
    //设置队列的结构体
    typedef struct
    {
        ElemType data[MAXSIZE];
        int front;    //队头下标
        int rear;    //队尾下标,若队列不为空,指向队列元素的下一个位置
    }sqQueue;
    
    //四个基础操作
    Status InitQueue(sqQueue *Q);    //初始化操作,建立一个空队列Q
    Status ClearQueue(sqQueue *Q);//将队列清空
    Status QueueEmpty(sqQueue Q);    //若队列为空,返回true,否则返回false
    int QueueLength(sqQueue Q);    //返回队列Q的元素个数
    
    Status GetHead(sqQueue Q, ElemType *e);    //若是队列存在且非空,用e返回Q的队头元素
    Status EnQueue(sqQueue *Q, ElemType e);    //若是队列存在,则插入新的元素e入队为队尾
    Status DeQueue(sqQueue *Q, ElemType *e);    //若是队列存在且非空,进行出队操作,用e接收数据
    Status DestroyQueue(sqQueue *Q);            //若是队列存在,则销毁他
    
    int main()
    {
        sqQueue lq;
        ElemType e;
        int i;
        //初始化一个空的队列
        InitQueue(&lq);
    
        printf("2.EnQueue 1-5
    ");
        for (i = 1; i <= 5; i++)
            EnQueue(&lq, i);
        printf("3.DeQueue number for three times
    ");
        for (i = 1; i <= 3; i++)
        {
            DeQueue(&lq, &e);
            printf("DeQueue %d: %d
    ", i, e);
        }
        GetHead(lq, &e);
        printf("4.Get Head:%d
    ", e);
        printf("5.EnQueue 6-10
    ");
        for (i = 6; i <= 10; i++)
            EnQueue(&lq, i);
        printf("6.Get queue length:%d
    ", QueueLength(lq));
        printf("7.DeQueue number for six times
    ");
        for (i = 1; i <= 6; i++)
        {
            DeQueue(&lq, &e);
            printf("DeQueue %d: %d
    ", i, e);
        }
        if (!QueueEmpty(lq))
        {
            printf("8.Queue is not Empty
    ");
            ClearQueue(&lq);
            printf("9.Queue is Clear
    ");
        }
        printf("10.Queue Empty:%d
    ", QueueEmpty(lq));
        printf("11.destroy Queue");
        DestroyQueue(&lq);
        system("pause");
        return 0;
    }
    
    //初始化操作,建立一个空队列Q
    Status InitQueue(sqQueue *Q)
    {
        if (!Q)
            return ERROR;
        Q->front = Q->rear = 0;    //都指向下标0
        return OK;
    }
    
    //将队列清空,和初始化一样即可
    Status ClearQueue(sqQueue *Q)
    {
        if (!Q)
            return ERROR;
        Q->front = Q->rear = 0;    //都指向下标0
        return OK;
    }
    
    //若队列为空,返回true,否则返回false
    Status QueueEmpty(sqQueue Q)
    {
        if (Q.front == Q.rear)    //若是队头队尾指向一致,则为空
            return TRUE;
        return FALSE;
    }
    
    //返回队列Q的元素个数
    int QueueLength(sqQueue Q)
    {
        return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
    }
    
    //若是队列存在且非空,用e返回Q的队头元素
    Status GetHead(sqQueue Q, ElemType *e)
    {
        if (!e || QueueEmpty(Q))
            return ERROR;
        *e = Q.data[Q.front];
        return OK;
    }
    
    //若是队列存在,且未满,则插入新的元素e入队为队尾
    Status EnQueue(sqQueue *Q, ElemType e)
    {
        if (!Q || (Q->rear + 1) % MAXSIZE == Q->front)    //判断队满
            return ERROR;
        Q->data[Q->rear] = e;    //为队尾赋值
        Q->rear = (Q->rear + 1) % MAXSIZE;    //将队尾下标下移
        return OK;
    }
    
    //若是队列存在且非空,进行出队操作,用e接收数据
    Status DeQueue(sqQueue *Q, ElemType *e)
    {
        if (!Q || !e || QueueEmpty(*Q))
            return ERROR;
        *e = Q->data[Q->front];
        Q->front = (Q->front + 1) % MAXSIZE;
        return OK;
    }
    
    //若是队列存在,则销毁他(和初始化一致),要不再加一个将数据清空吧
    Status DestroyQueue(sqQueue *Q)
    {
        if (!Q)
            return ERROR;
        memset(Q, 0, MAXSIZE*sizeof(ElemType));
        Q->front = Q->rear = 0;    //都指向下标0
        return OK;
    }

    (八)我们为什么选用链队列

    1.若只是使用顺序存储,算法的时间性能不高,尤其是对于出队列时的相关操作。
    2.即便我们使用了循环队列,也会面临数组溢出的问题。
    总结:
    所有我们使用不需要担心队列长度的链式存储结构即可
  • 相关阅读:
    Backbone学习之todos实现
    由$(this).attr('id')引出的一点感想
    JS类的实现
    JS设计模式之接口
    JS设计模式之工厂模式
    JS设计模式之序
    JS的继承
    you think you know javascript?
    js中的null和undefined
    作用域、闭包等概念的理解
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9441765.html
Copyright © 2020-2023  润新知