队列(常用数据结构之一)

简介: 队列(常用数据结构之一)

队列

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

一个队列为z=(a1,a2,...,an), 如图

1c7e5ed0bf15f28fb815f14790322b0f.png

那么a1为对头元素,an为队尾元素。最早进入队列的元素也会最早出来,只有当最先进入队列的元素都出来以后,后进入的元素才能退出。

在日常生活中,人们去银行办理业务需要排队,这就类似我们提到的队列。每一个新来办理业务的需要按照机器自动生成的编号等待办理,只有前面的人办理完毕,才能轮到排在后面的人办理业务。新来的人进入排队状态就相当于入队,前面办理完业务离开的就相当于出队。队列有两种存储表示:顺序存储和链式存储。采用顺序存储结构的队列被称为顺序队列,采用链式存储结构的队列称为链式队列。


基本运算

InitQueue()     ——初始化队列
EnQueue()        ——进队列
DeQueue()        ——出队列
IsQueueEmpty()   ——判断队列是否为空
IsQueueFull()    ——判断队列是否已满


顺序队列

由于顺序队列的底层使用的是数组,因此需预先申请一块足够大的内存空间初始化顺序队列。除此之外,为了满足顺序队列中数据从队尾进,队头出且先进先出的要求,我们还需要定义两个指针(top 和 rear)分别用于指向顺序队列中的队头元素和队尾元素。

队列为空时,队头指针front和队尾指针rear都指向下标为0的存储单元,当元素a,b,c,d,e,f,g依次进入队列后,元素a~g分别存放在数组下标为0~6的存储单元中,队头指针front指向元素a,队尾指针指rear向元素g的下一位置。


假溢出

在顺序队中,当尾指针已经到了数组的上界,不能再有入队操作,但其实数组中还有空位置,这就叫“假溢出”。解决假溢出的途径———采用循环队列。

例如在图中队列删除a和b,然后依次插入h、i和j,当插入j后,就会出现队尾指针rear越出数组的下界造成“假溢出”,如图

27306ad1e4f90e4939a26ed9a4ba2e86.png


顺序循环队列

为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。即:循环队列中进行出队、入队操作时,头尾指针仍要加1,朝前移动。只不过当头尾指针指向向量上界(QueueSize-1)时,其加1操作的结果是指向向量的下界0。


队空和队满

在循环队列中,队空和队满时队头front和队尾指针rear同时都会指向同一存储单元,即front==rear,如图所示。

c404e1e23b7d5df3fb7424d2ef21aeb5.png


队空

bdf2ffb8680c7e27ec8f1bca0e70497c.png


队满

如何区分队空和队满呢?有以下两种方法:

(1)增加一个标志位。设标志位为tag,初始时,tag=0;当入队成功,则tag=1;出队成功,tag=0。则判断队空的条件为:front==rear&&tag==0;队满的条件为:front==rear&&tag==1;

(2)少用一个存储单元。队空的判断条件为front==rear;队满的判断条件为front==(rear+1)%QueueSize。队满的状态如图。

1ada4066f4d4bda86d68faa019fa33ad.png


存储结构

#define  MAXQSIZE  5 // 存储空间的初始分配量
typedef struct {
  ElemType *base;
  int front;
  int rear;
  int maxSize;
} SqQueue;


基本运算

初始化

Status InitQueue(SqQueue &Q) {
  //分配存储空间
  Q.base = (ElemType*)malloc(MAXQSIZE * sizeof(ElemType));
  if(!Q.base) exit(OVERFLOW);
  //置Q为空队列
  Q.front = Q.rear = 0;
  Q.maxSize = MAXQSIZE;
  return OK;
}


判队列是否为空

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


入队函数

Status EnQueue(SqQueue &Q, ElemType e) {
  if((Q.rear + 1) % MAXQSIZE == Q.front)//队列已满
    return ERROR;
  Q.base[Q.rear] = e;//插入队尾
  Q.rear = (Q.rear + 1) % MAXQSIZE;//尾部指针后移,如果到最后则转到头部
  return OK;
}


出队函数

Status DeQueue(SqQueue &Q, ElemType &e) {
  if(Q.front == Q.rear)   //队列空
      return ERROR;
  //返回队头元素
  e = Q.base[Q.front];    
  //队头指针后移,如到最后转到头部
  Q.front = (Q.front + 1) % MAXQSIZE; 
  return OK;
}


输出循环队列函数

void OutQueue(SqQueue Q) {  
  ElemType e;
  if(QueueEmpty(Q)){
    printf("这是一个空队列!");
  } else {
      while(!QueueEmpty(Q)){
        DeQueue(Q, e);
        printf("%6d", e);
        }
      printf("\n");
  }
}


输出循环队列长度

Status QueueLength(SqQueue Q) {
    return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}


销毁队列

Status ClearQueue(SqQueue Q) {
    ///销毁队列Q,Q不再存在
    if(Q.base)
        free(Q.base);
    Q.base = NULL;
    Q.front = Q.rear = 0;
    return OK;
}


主函数

int main() {  
    SqQueue q;
    int cord; 
    ElemType a;
    printf("第一次使用必须初始化!\n");
    //调用初始化算法
    InitQueue(q); 
    do{
        printf("\n 主菜单 \n");
        printf(" 1 初始化循环队列 ");
        printf(" 2 进队一个元素 ");
        printf(" 3 出队一个元素 ");
        printf(" 4 队列长度 ");
        printf(" 5 销毁队列 ");
        printf(" 6 结束程序运行 ");
        printf("\n------------------------------------------------------------------\n");
        printf("请输入您的选择( 1, 2, 3, 4, 5, 6)");
        scanf("%d", &cord);
        printf("\n");
        switch(cord) {
            case 1:
                InitQueue(q); //调用初始化算法;
                OutQueue(q);
                break;
            case 2:
                printf("请输入要插入的数据元素:a=");
                scanf("%d", &a);
                EnQueue (q, a); //调用进队算法;
                printf("%d 进队之后的队列:",a);
                OutQueue(q);
                break;
            case 3:
                DeQueue (q, a); //调用出队算法;
                printf("队头元素 %d 出队之后的队列:", a);
                OutQueue(q);
                break;
            case 4:
                printf("该队列长度为: %d", QueueLength(q));
                break;
            case 5:
                ClearQueue(q);
                break;
            case 6:
                exit(0);
                }
    } while(cord <= 4);
    return 0;
}


目录
相关文章
|
16天前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
22 5
【数据结构】优先级队列(堆)从实现到应用详解
|
3天前
|
前端开发
07_用队列实现栈
07_用队列实现栈
|
3天前
|
测试技术
02_由两个栈组成的队列
02_由两个栈组成的队列
|
7天前
|
存储
|
24天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
147 3
|
25天前
|
Java
【数据结构】栈和队列的深度探索,从实现到应用详解
本文介绍了栈和队列这两种数据结构。栈是一种后进先出(LIFO)的数据结构,元素只能从栈顶进行插入和删除。栈的基本操作包括压栈、出栈、获取栈顶元素、判断是否为空及获取栈的大小。栈可以通过数组或链表实现,并可用于将递归转化为循环。队列则是一种先进先出(FIFO)的数据结构,元素只能从队尾插入,从队首移除。队列的基本操作包括入队、出队、获取队首元素、判断是否为空及获取队列大小。队列可通过双向链表或数组实现。此外,双端队列(Deque)支持两端插入和删除元素,提供了更丰富的操作。
26 0
【数据结构】栈和队列的深度探索,从实现到应用详解
|
2月前
|
机器学习/深度学习 消息中间件 缓存
栈与队列的实现
栈与队列的实现
40 0
|
2月前
|
测试技术
【初阶数据结构篇】队列的实现(赋源码)
首先队列和栈一样,不能进行遍历和随机访问,必须将队头出数据才能访问下一个,这样遍历求个数是不规范的。
|
2月前
|
算法
【数据结构与算法】优先级队列
【数据结构与算法】优先级队列
13 0
|
2月前
|
存储 算法
【数据结构与算法】队列(顺序存储)
【数据结构与算法】队列(顺序存储)
15 0