美文网首页
数据结构与算法-队列

数据结构与算法-队列

作者: 恍然如梦_b700 | 来源:发表于2020-04-14 23:13 被阅读0次

    什么是队列

    队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

    队列这个概念非常好理解。你可以把它想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。先进者先出,这就是典型的“队列”。

    我们知道,CPU 资源是有限的,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致 CPU 频繁切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。当我们向固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池如何处理这个请求?是拒绝请求还是排队请求?各种处理策略又是怎么实现的呢?

    看完下面队列C语言实现,相信你会多少有些了解

    队列只支持两个基本操作:入队 enqueue(),放一个数据到队列尾部;出队 dequeue(),从队列头部取一个元素。
    队列跟栈一样,也是一种操作受限的线性表数据结构。

    顺序队列和链式队列

    队列跟栈一样,也是一种抽象的数据结构。它具有先进先出的特性,支持在队尾插入元素,在队头删除元素。
    跟栈一样,队列可以用数组来实现,也可以用链表来实现。用数组实现的栈叫作顺序栈,用链表实现的栈叫作链式栈。同样,用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列。


    image.png

    随着不停地进行入队、出队操作, front 和 rear 都会持续往后移动。当 rear 移动到最右边,即使数组中还有空闲空间,也无法继续往队列中添加数据了。同时也不好判断队满条件,可以使用循环队列来实现

    循环队列

    循环队列,顾名思义,它长得像一个环。原本数组是有头有尾的,是一条直线。现在我们把首尾相连,扳成了一个环。


    image.png image.png

    经过推算,可以发现:
    队空 Q.rear==Q.front。
    队满 (Q.rear+1)%MAXSIZE==Q.front。

    注意,当队列满时,图中的 Q.rear 指向的位置实际上是没有存储数据的。所以,循环队列会浪费一个数组的存储空间。

    循环队列实现

    队列结构设计
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    #define MAXSIZE 20 /* 存储空间初始分配量 */
    
    typedef int Status;
    typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
    
    /* 循环队列的顺序存储结构 */
    typedef struct {
        QElemType data[MAXSIZE];
        int front;//头指针(记录位置,非指针变量)
        int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置,即待入队位置
        
    }SqQueue;
    
    

    1.初始化空队列

    /* 1.初始化空队列 */
    Status initQueue(SqQueue *Q) {
        Q->front = 0;
        Q->rear = 0;
        return OK;
    }
    

    2.清空队列

    /* 2.清空队列 */
    Status clearQueue(SqQueue *Q) {
        Q->front = 0;
        Q->rear = 0;
        return OK;
    }
    /* 3.判断队空 */
    int QueueEmpty(SqQueue Q) {
        if (Q.front == Q.rear)
            return TRUE;
        else
            return FALSE;
    }
    

    4.判断队满

    /* 4.判断队满 */
    int QueueFull(SqQueue Q) {
        if ((Q.rear+1)%MAXSIZE == Q.front)
            return TRUE;
        return FALSE;
    }
    

    5.入队

    /* 5.入队 */
    Status enQueue(SqQueue *Q, QElemType e) {
        //判断队满
        if (QueueFull(*Q))
            return ERROR;
        Q->data[Q->rear] = e;//将元素e赋值给队尾
        //rear向后移动一位,%保证循环
        Q->rear = (Q->rear+1)%MAXSIZE;
        return OK;
    }
    

    6.出队

    /* 6.出队 */
    Status deQueue(SqQueue *Q, QElemType *e) {
        //判断队空
        if (QueueEmpty(*Q))
            return ERROR;
        //将队头元素赋值给e
        *e = Q->data[Q->front];
        //front 指针向后移动一位,若到最后则转到数组头部
        Q->front = (Q->front+1)%MAXSIZE;
        return OK;
    }
    

    7.获取队列当前元素个数

    /* 7.队列当前元素个数 */
    int QueueLength(SqQueue Q){
        return (Q.rear - Q.front + MAXSIZE)%MAXSIZE;
    }
    

    8.若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR

    /* 8.若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR; */
    Status GetHead(SqQueue Q,QElemType *e){
        if (QueueEmpty(Q))
            return ERROR;
        
        *e = Q.data[Q.front];
        return OK;
        
    }
    

    9.从队头到队尾依次对队列的每个元素数组

    /* 9.从队头到队尾依次对队列的每个元素数组 */
    Status QueueTraverse(SqQueue Q){
        int i;
        i = Q.front;
        while (i != Q.rear) {
            printf("%d   ",Q.data[i]);
            i = (i+1)%MAXSIZE;
        }
        printf("\n");
        return OK;
    }
    

    10.主函数中验证

    int main(int argc, const char * argv[]) {
        printf("链队列的表示与操作!\n");
        
        Status iStatus;
        QElemType d;
        LinkQueue q;
        
        //1.初始化队列q
        iStatus = InitQueue(&q);
        
        //2. 判断是否创建成
        if (iStatus) {
            printf("成功地构造了一个空队列\n");
        }
        
        //3.判断队列是否为空
        printf("是否为空队列?%d (1:是 0:否)\n",QueueEmpty(q));
        
        //4.获取队列的长度
        printf("队列的长度为%d\n",QueueLength(q));
        
        //5.插入元素到队列中
        enQueue(&q, -3);
        enQueue(&q, 6);
        enQueue(&q, 12);
        
        printf("队列的长度为%d\n",QueueLength(q));
        printf("是否为空队列?%d (1:是 0:否)\n",QueueEmpty(q));
        
        //6.遍历队列
        printf("队列中的元素如下:\n");
        QueueTraverse(q);
    
        //7.获取队列头元素
        iStatus = getHeadElem(q, &d);
        if (iStatus == OK) {
            printf("队头元素是:%d\n",d);
        }
        
        //8.删除队头元素
        iStatus = deQueue(&q, &d);
        if (iStatus == OK) {
            printf("删除了的队头元素为:%d\n",d);
        }
        
        //9.获取队头元素
        iStatus = getHeadElem(q, &d);
        if (iStatus == OK) {
            printf("新的队头元素为:%d\n",d);
        }
        
        //10.清空队列
        clearQueue(&q);
        
        //11.销毁队列
        DestoryQueue(&q);
        return 0;
    }
    

    输出结果

    001--顺序队列表示与操作实现
    初始化队列后,队列空否?1(1:空 0:否)
    入队:
    0   1   2   3   4   5   6   7   8   9   
    队列长度为: 10
    现在队列空否?0(1:空 0:否)
    出队:
    出队的元素:1
    2   3   4   5   6   7   8   9   
    现在队头元素为: 2
    清空队列后, 队列空否?1(1:空 0:否)
    Program ended with exit code: 0
    

    链式队列

    image.png

    实现

    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    #define MAXSIZE 20 /* 存储空间初始分配量 */
    
    typedef int Status;
    typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
    
    /* 节点 */
    typedef struct QNode{
        QElemType data;
        struct QNode *next;
        
    }QNode,*QueuePtr;
    
    /* 定义一个链式队列 */
    typedef struct {
        QueuePtr front, rear;
    }LinkQueue;
    

    1.初始化

    Status InitQueue(LinkQueue *Q) {
        //创建一个头结点
        Q->front = (QueuePtr)malloc(sizeof(QNode));
        if (Q->front == NULL)
            return ERROR;
        //首尾指针指向头结点
        Q->rear = Q->front;
        //头结点的next指针指向NULL;
        Q->front->next = NULL;
        return OK;
    }
    

    2.销毁

    Status DestoryQueue(LinkQueue *Q) {
        QueuePtr p = Q->front;
        QueuePtr q = p;//因为销毁后Q->rear是无用的,这里可以用Q->rear代替q,Q->front代替p。
        while (p) {
            q = p;
            p = p->next;
            free(q);
        }
        
        return OK;
    }
    

    3.置空

    Status clearQueue(LinkQueue *Q) {
        QueuePtr p,q;
        p = Q->front;
        Q->rear = Q->front;
        Q->front->next = NULL;
        while (p) {
            q = p->next;
            p->next = q->next;
            free(q);
        }
       
        return OK;
    }
    

    4.判断队列是否为空

    Status QueueEmpty(LinkQueue Q){
        if (Q.front == Q.rear)
            return TRUE;
        else
            return FALSE;
    }
    

    5.获取元素个数

    int QueueLength(LinkQueue Q){
        QueuePtr p;
        p = Q.front;
        int i = 0;
        while (p != Q.rear) {
            p = p->next;
            i++;
        }
        return i;
    }
    

    6.入队

    Status enQueue(LinkQueue *Q, QElemType e){
        
        //创建节点
        QueuePtr temp = (QueuePtr)malloc(sizeof(QNode));
        if (!temp) {
            return ERROR;
        }
        //赋值
        temp->data = e;
        temp->next = NULL;
        //队尾的next指向新节点
        Q->rear->next = temp;
        //队尾指针指向新节点
        Q->rear = temp;
     
        return OK;
    }
    

    7.出队

    Status deQueue(LinkQueue *Q, QElemType *e){
        //判断队空
        if (Q->front == Q->rear) {
            return ERROR;
        }
        
        QueuePtr p;//待删除节点
        p = Q->front->next;
        *e = p->data;
        //将原队列头结点的后继p->next 赋值给头结点后继
        Q->front->next = p->next;
        //若出队的数据已是队尾
        if (p == Q->rear) {
            //将队列置空,rear指向front
            Q->rear = Q->front;
        }
        
        free(p);
        return OK;
    }
    

    8.获取队头元素

    Status getHeadElem(LinkQueue Q, QElemType *e) {
        //判断队空
        if (Q.front == Q.rear) {
            return ERROR;
        }
        *e = Q.front->next->data;
        return OK;
    }
    

    9.遍历队列

    Status QueueTraverse(LinkQueue Q){
        
        QueuePtr p;
        p = Q.front->next;
        while (p) {
            printf("%d ",p->data);
            p = p->next;
        }
        printf("\n");
        return OK;
    }
    

    验证

    int main(int argc, const char * argv[]) {
    
        Status iStatus;
        QElemType d;
        LinkQueue q;
        
        //1.初始化队列q
        iStatus = InitQueue(&q);
        
        //2. 判断是否创建成
        if (iStatus) {
            printf("成功地构造了一个空队列\n");
        }
        
        //3.判断队列是否为空
        printf("是否为空队列?%d (1:是 0:否)\n",QueueEmpty(q));
        
        //4.获取队列的长度
        printf("队列的长度为%d\n",QueueLength(q));
        
        //5.插入元素到队列中
        enQueue(&q, -3);
        enQueue(&q, 6);
        enQueue(&q, 12);
        
        printf("队列的长度为%d\n",QueueLength(q));
        printf("是否为空队列?%d (1:是 0:否)\n",QueueEmpty(q));
        
        //6.遍历队列
        printf("队列中的元素如下:\n");
        QueueTraverse(q);
    
        //7.获取队列头元素
        iStatus = getHeadElem(q, &d);
        if (iStatus == OK) {
            printf("队头元素是:%d\n",d);
        }
        
        //8.删除队头元素
        iStatus = deQueue(&q, &d);
        if (iStatus == OK) {
            printf("删除了的队头元素为:%d\n",d);
        }
        
        //9.获取队头元素
        iStatus = getHeadElem(q, &d);
        if (iStatus == OK) {
            printf("新的队头元素为:%d\n",d);
        }
        
        //10.清空队列
        clearQueue(&q);
        
        //11.销毁队列
        DestoryQueue(&q);
        return 0;
    }
    
    

    输出

    链队列的表示与操作!
    成功地构造了一个空队列
    是否为空队列?1 (1:是 0:否)
    队列的长度为0
    队列的长度为3
    是否为空队列?0 (1:是 0:否)
    队列中的元素如下:
    -3 6 12 
    队头元素是:-3
    删除了的队头元素为:-3
    新的队头元素为:6
    Program ended with exit code: 0
    

    要想写出没有 bug 的循环队列实现代码,关键要确定好队空和队满的判定条件。

    相关文章

      网友评论

          本文标题:数据结构与算法-队列

          本文链接:https://www.haomeiwen.com/subject/zbinmhtx.html