数据结构(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 作者:新晓-故知  

相关文章
|
28天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
45 1
|
1月前
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
49 4
|
1月前
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
45 4
|
1月前
|
存储 算法 数据处理
【趣学C语言和数据结构100例】81-85
本文介绍了五个经典算法问题及其C语言实现,涵盖图论与树结构的基础知识。包括使用BFS求解单源最短路径、统计有向图中入度或出度为0的点数、统计无向无权图各顶点的度、折半查找及二叉排序树的查找。这些算法不仅理论意义重大,且在实际应用中极为广泛,有助于提升编程能力和数据结构理解。
45 4
|
1月前
|
算法 数据可视化 数据建模
【趣学C语言和数据结构100例】76-80
本文介绍了五种图论算法的C语言实现,涵盖二叉树的层次遍历及广度优先搜索(BFS)和深度优先搜索(DFS)的邻接表与邻接矩阵实现。层次遍历使用队列按层访问二叉树节点;BFS利用队列从源节点逐层遍历图节点,适用于最短路径等问题;DFS通过递归或栈深入图的分支,适合拓扑排序等场景。这些算法是数据结构和算法学习的基础,对提升编程能力和解决实际问题至关重要。
52 4
|
1月前
|
存储 算法 vr&ar
【趣学C语言和数据结构100例】71-75
本文介绍了五个C语言数据结构问题及其实现,涵盖链表与二叉树操作,包括按奇偶分解链表、交换二叉树左右子树、查找节点的双亲节点、计算二叉树深度及求最大关键值。通过递归和遍历等方法,解决了理论与实际应用中的常见问题,有助于提升编程能力和数据结构理解。
43 4
|
1月前
|
存储 算法 C语言
【趣学C语言和数据结构100例】66-70
本书《趣学C语言和数据结构100例》精选了5个典型的数据结构问题及C语言实现,涵盖链表与数组操作,如有序集合的集合运算、有序序列表的合并、数组中两顺序表位置互换、三递增序列公共元素查找及奇偶数重排。通过详细解析与代码示例,帮助读者深入理解数据结构与算法设计的核心思想,提升编程技能。
36 4
|
29天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
55 5
|
28天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
62 1
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
218 9