数据结构与算法-队列

什么是队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(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 的循环队列实现代码,关键要确定好队空和队满的判定条件。

你可能感兴趣的:(数据结构与算法-队列)