数据结构(C语言版)之队列

简介: 数据结构(C语言版)之队列

前言

●数据结构作为计算机专业基础课,综合性强,抽象性高,在一定程度上增加了学习难度,本次我们共同从数据结构的基础探讨,由浅入深进行数据结构的学习。

●本文只浅显的探讨了队列的基本知识,作者相信随着学习课程的深入,我们将会对数据结构有更深的理解与收获!

●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

————————————————

                                                                                                                                                                                      ——By 作者:新晓-故知

正文

————————————————

一、队列

image.gif编辑

队列是另外一种常用的线性结构,到达这种结构的元素越早,离开该结构的时间也越早,所以队列通常被称为先进先出(FIFO: First In First Out)队列

1.队列可以看作是插入删除位置操作受限的线性表,它的插入和删除分别在表的两端进行。

2.队列的删除操作只能在队首(front)进行而插入操作只能在队尾(rear)进行,从而保证了队列的先进先出特点。

3.元素从队首删除的操作,称之为出队(deQueue)。

4.元素在队尾位置插入的操作,称为进队(enQueue)。

                                                                               ——By 作者:新晓-故知 整理

image.gif编辑

image.gif编辑

1. 队列的抽象数据类型:

队列的抽象数据类型:

Data:

image.gif编辑

Operations:

image.gif编辑

image.gif编辑

队列的抽象数据类型:

ADT Queue {

数据对象:     image.gif编辑

数据关系:     image.gif编辑

基本操作:

{     (1) InitQueue (&Q)              构造空队列
      (2) DestroyQueue (&Q)           销毁队列
      (3) ClearQueue (&S)             清空队列
      (4) QueueEmpty(S)               判空. 空--TRUE,
      (5) QueueLength(Q)              取队列长度
      (6) GetHead (Q,&e)              队头元素,
      (7) EnQueue (&Q,e)              入队列
      (8) DeQueue (&Q,&e)             出队列
      (9) QueueTraverse(Q,visit())    遍历
}ADT Queue
image.gif

队列的顺序表示--用一维数组base[M]

#define M  100         最大队列长度
Typedef struct 
{
   QElemType *base;    初始化的动态分配存储空间
   int  front;         头指针   
   int  rear;          尾指针
}SqQueue;
image.gif

 image.gif编辑

image.gif编辑

front=0 rear=M时:

front=0            
rear=M 时
再入队——真溢出
front≠0
rear=M 时
再入队——假溢出
image.gif

解决的方法--循环队列:

image.gif编辑

base[0]接在base[M-1]之后
若rear+1==M
则令rear=0;
实现:利用“模”运算
入队:
  base[rear]=x;
  rear=(rear+1)%M;
出队:
  x=base[front];
  front=(front+1)%M;
image.gif

image.gif编辑

                                                                              ——By 作者:新晓-故知 整理

2. 循环队列:

#define MAXQSIZE  100       最大队列长度
Typedef struct 
{
   QElemType *base;         初始化的动态分配存储空间
   int  front;              头指针   
   int  rear;               尾指针
}SqQueue;
image.gif

循环队列初始化:

Status InitQueue (SqQueue &Q)
{
    Q.base =new QElemType[MAXQSIZE] 
   if(!Q.base) exit(OVERFLOW);
    Q.front=Q.rear=0;
    return OK;
}
image.gif

求循环队列的长度:

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

 循环队列入队:

Status EnQueue(SqQueue &Q,QElemType e)
{
    if((Q.rear+1)%MAXQSIZE==Q.front)  return ERROR;
    Q.base[Q.rear]=e;
    Q.rear=(Q.rear+1)%MAXQSIZE;
     return OK;
}
image.gif

 循环队列出队:

Status DeQueue (LinkQueue &Q,QElemType &e)
{
   if(Q.front==Q.rear) return ERROR;
   e=Q.base[Q.front];
   Q.front=(Q.front+1)%MAXQSIZE;
   return OK;
}
image.gif

                                               q                       ——By 作者:新晓-故知 整理+创作

3.队列的顺序存储及实现:

a.队列的顺序存储,用一组连续的空间存储队列中的元素及元素间关系。

b.可以使用队首指针Front和队尾指针Rear分别指示队首元素和队尾元素存放的下标地址。

c.让Front指向真正的队首元素,而Rear指向真正存放队尾元素的后一数组单元。这样队空的标志Front=Rear。

d.为了防止大批数据移动,当Rear=maxSize时,如果下标为0的空间未用,让它转回来,即Rear=Rear%MaxSize。形成循环队列。

                                                                      ——By 作者:新晓-故知 整理+创作

image.gif编辑

队列操作中的Rear和Front

队列操作中的Rear和Front

无论进队、出队,Rear和Front都向前移动,移动到右边界时向左边循环。 为了区别队空标志,当队尾还有一个空间时即告队满,浪费了一个空间做代价。

队列空的标志:Rear=Front

队列满的标志:(Rear+1)%maxSize =Front

进队操作后:Rear= (Rear+1)%maxSize

出队操作后:Front= (Front+1)%maxSize

                                                                      ——By 作者:新晓-故知 整理+创作

队列及操作的实现

#include <stdio.h>
#include <stdlib.h>
typedef int elemType;
typedef struct
{
    int Front, Rear;
    int maxSize;
    elemType *array;
}Queue;

image.gif

void initialize(Queue *que, int size);     初始化队列元素的存储空间
int isEmpty(Queue *que);                   判断队空否,空返回1,否则为0
int isFull(Queue *que);                    判断队满否,满返回1,否则为0
elemType front(Queue *que);                读取队首元素的值,队首不变
void enQueue(Queue *que, elemType x);      将x进队,成为新的队尾
void deQueue(Queue *que);                  将队首元素出队
void doubleSize(Queue *que);               扩展队队列元素的存储空间为原来的2倍
void clear(Queue *que);                    将队列中所有元素清空,为空的队列
void destroy(Queue *que);                  释放队列元素所占据的动态数组

image.gif

void initialize(Queue *que, int size)        初始化队列元素的存储空间
{    que->maxSize = size;
    //申请实际的队列存储空间
    que->array = (elemType *)malloc(sizeof(elemType)*size); 
    if (!que->array) exit(1);
    que->Front = que->Rear = 0;
}
int isEmpty(Queue *que)                      判断队空否,空返回1,否则为0
{return que->Front == que->Rear;}
int isFull(Queue *que)                       判断队满否,满返回1,否则为0
{return (que->Rear+1)%que->maxSize == que->Front;}
elemType front(Queue *que)                   读取队首元素的值,队首不变
{   if (isEmpty(que)) exit(1);
    return que->array[que->Front];
}
void enQueue(Queue *que, elemType x)          将x进队,成为新的队尾
{   if (isFull(que))  doubleSize(que);
    que->array[que->Rear]=x;
    que->Rear = (que->Rear+1)%que->maxSize;
}
 void deQueue(Queue *que)                     将队首元素出队
{   if (isEmpty(que)) exit(1);
    que->Front = (que->Front+1)%que->maxSize;
}
void clear(Queue *que) //将队列中所有元素清空,为空的队列
{ que->Front = que->Rear = 0;}
void destroy(Queue *que)                      释放队列元素所占据的动态数组
{  free(que->array);}
                                                        ——By 作者:新晓-故知 整理+创作

image.gif

 算法时间复杂度分析: 各种操作的时间复杂度都为O(1)。

4.链式队列:

image.gif编辑

typedef struct QNode{
   QElemType   data;
   struct Qnode  *next;
}Qnode, *QueuePtr;
typedef struct {
   QueuePtr  front;            队头指针   
   QueuePtr  rear;             队尾指针
}LinkQueue;

image.gif

(a) 空队列

image.gif编辑

(b) 元素x入队列

image.gif编辑

(c) 元素y入队列

image.gif编辑

(d) 元素x出队列

image.gif编辑  

                                            ——By 作者:新晓-故知  整理+创作

链队列初始化:

Status InitQueue (LinkQueue &Q)
{
   Q.front=Q.rear=(QueuePtr) malloc(sizeof(QNode)); 
    if(!Q.front) exit(OVERFLOW);
    Q.front->next=NULL;
     return OK;
}

image.gif

销毁链队列:

Status DestroyQueue (LinkQueue &Q)
{
   while(Q.front){
      Q.rear=Q.front->next;
      free(Q.front);
      Q.front=Q.rear;   }    
   return OK;
}

image.gif

判断链队列是否为空:

Status QueueEmpty (LinkQueue Q)
{
    return (Q.front==Q.rear);                             
 }

image.gif

求链队列的队头元素:

Status GetHead (LinkQueue Q, QElemType &e)
{
   if(Q.front==Q.rear) return ERROR;
   e=Q.front->next->data;
   return OK;
}

image.gif

 链队列入队:

Status EnQueue(LinkQueue &Q,QElemType e)
{
    p=(QueuePtr)malloc(sizeof(QNode));
    if(!p) exit(OVERFLOW);
    p->data=e; p->next=NULL;
    Q.rear->next=p;
    Q.rear=p;
    return OK;
}
image.gif

image.gif编辑

                                                                      ——By 作者:新晓-故知 整理+创作

链队列出队:

Status DeQueue (LinkQueue &Q,QElemType &e)
{
   if(Q.front==Q.rear) return ERROR;
   p=Q.front->next;
   e=p->data;
   Q.front->next=p->next;
   if(Q.rear==p) Q.rear=Q.front;
   free(p);
   return OK;
}
image.gif

image.gif编辑

用链表存储队列中的元素。其中队首指针 Front指向队首结点,队尾指针Rear指向队尾结点。

image.gif编辑

image.gif编辑

typedef int elemType;
typedef struct 
{
    elemType data;
    struct Node *next;
} Node;

image.gif

typedef struct 
{
    Node *Front,*Rear;
}linkQueue;

image.gif

void initialize(linkQueue *que);             初始化为一个空队
int isEmpty(linkQueue *que);                 判断队空否,空返回1,否则为0
int isFull(linkQueue *que);                  判断队满否,满返回1,否则为0
elemType front(linkQueue *que);              读取队首元素的值,队首不变
void enQueue(linkQueue *que, elemType x);    将x进队,成为新的队尾
void deQueue(linkQueue *que);                将队首元素出队
void clear(linkQueue *que);                  将队列中所有元素清空,为空的队列
void destroy(linkQueue *que);                释放队列元素所占据的动态空间
void initialize(linkQueue *que)              初始化为一个空队
{    que->Front = que->Rear = NULL;   }
void initialize(linkQueue *que);             初始化为一个空队
int isEmpty(linkQueue *que);                 判断队空否,空返回1,否则为0
int isFull(linkQueue *que);                  判断队满否,满返回1,否则为0
elemType front(linkQueue *que);              读取队首元素的值,队首不变
void enQueue(linkQueue *que, elemType x);    将x进队,成为新的队尾
void deQueue(linkQueue *que);                将队首元素出队
void clear(linkQueue *que);                  将队列中所有元素清空,为空的队列
void destroy(linkQueue *que);                释放队列元素所占据的动态空间
void initialize(linkQueue *que)              初始化为一个空队
{    que->Front = que->Rear = NULL;   }
void enQueue(linkQueue *que, elemType x)     将x进队,成为新的队尾
{   Node *tmp;
    tmp = (Node *)malloc(sizeof(Node));
    tmp->data = x;
    tmp->next = NULL;
    if (isEmpty(que))     que->Front = que->Rear = tmp;
    else
    {   que->Rear->next = tmp;
        que->Rear = tmp;
    }
}
void deQueue(linkQueue *que)                 将队首元素出队
{   Node *tmp;
    if (isEmpty(que)) exit(1);
   tmp = que->Front;
    que->Front = que->Front->next;
    free(tmp);
}
void clear(linkQueue *que)                   将队列中所有元素清空,为空的队列
{   Node *tmp;
    tmp = que->Front;}
while (tmp)
    {   que->Front = que->Front->next;
        free(tmp);
        tmp=que->Front;
    }
  que->Front = que->Rear = NULL;
}
void destroy(linkQueue *que)                  释放队列元素所占据的动态空间
{  clear(que); }
                                                      ——By 作者:新晓-故知 整理+创作

image.gif

5.优先队列

有时要求进入队列中的元素具有优先级,队列中元素优先级越高出队越早,优先级越低出队越晚。

个人手头事务的处理通常采取这样的策略,操作系统中进程的调度、管理也是采用优先队列进行控制的.

元素之间的关系是由元素的优先级决定的,这样一种队列称为优先队列。

                                                                      ——By 作者:新晓-故知 整理+创作

优先队列有多种实现方式:

采用顺序存储结构实现,是最常用的一种,称顺序优先队列

其次也可以采用链式结构

后面章节也有用堆来存储。

image.gif编辑

为了避免整个队列的后移,造成空间的浪费,当有元素出队时将队列中最后一个元素移到出队元素所在的存储位置,这样队列始终从0下标开始到某个下标终止,中间不会出现空隙。

因为队列元素始终从0下标开始,所以不需要使用循环。

队空的条件为: Rear = Front;

队满的条件为:Rear = maxSize

                                                                       ——By 作者:新晓-故知 整理+创作

算法时间复杂度分析:进队O(1), 出队O(n)。

6.另一种策略:

6.1 顺序循环队列:

元素在队列中按照优先数由小到大排列,当元素进队时需要找到合适的插入位置,移动后面的元素,将新进元素插入,时间复杂度为O(n); 当元素出队时只需删除队首元素,为了避免后面元素的移动,可以采用顺序循环队列,时间复杂度为O(1)。

6.2 链式存储优先队列:

元素优先级最高的结点就是首结点,所以结点出队即删除首结点。O(1)

进队结点首先要查找插入位置。比较从首结点开始,逐个进行,当找到第一个结点的优先数大于新结点的优先数时,将新结点插入该结点前面。O(n)

image.gif编辑

总结:

本文共同探讨了队列的相关内容,在日常生活中有极其丰富的应用,作者认为要认真对待数据结构的学习,搭建基本知识框架,随着日积月累的学习逐渐填补总结,从脚踏实地做起,祝愿大家能够熟悉掌握这门课程,并将其能够熟悉的应用!

耐心看到这里的小伙伴一定很棒!加油!路在脚下,梦在前方!

————————————————

                                                                                             ——By 作者:新晓-故知  

相关文章
|
12天前
|
存储 算法 C语言
通义灵码在考研C语言和数据结构中的应用实践 1-5
通义灵码在考研C语言和数据结构中的应用实践,体验通义灵码的强大思路。《趣学C语言和数据结构100例》精选了五个经典问题及其解决方案,包括求最大公约数和最小公倍数、统计字符类型、求特殊数列和、计算阶乘和双阶乘、以及求斐波那契数列的前20项和。通过这些实例,帮助读者掌握C语言的基本语法和常用算法,提升编程能力。
|
27天前
|
缓存 算法 调度
数据结构之 - 双端队列数据结构详解: 从基础到实现
数据结构之 - 双端队列数据结构详解: 从基础到实现
51 5
|
19小时前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
12天前
|
存储 算法 C语言
【趣学C语言和数据结构100例】
《趣学C语言和数据结构100例》精选5个编程问题,涵盖求最大公约数与最小公倍数、字符统计、特殊序列求和及阶乘计算等,通过实例讲解C语言基础与算法思维,适合初学者实践学习。
|
19天前
初步认识栈和队列
初步认识栈和队列
47 10
|
20天前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
19 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
21天前
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
26 2
|
21天前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
18 2
【数据结构】--- 栈和队列
【数据结构】--- 栈和队列
|
27天前
|
消息中间件 存储 Java
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
26 4