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

相关文章
|
5天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
64 9
|
4天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
43 16
|
4天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
32 8
|
7天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
32 4
|
8天前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
32 3
|
8天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
7天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
24 0
|
存储 缓存 C语言
数据结构——双链表(C语言)
数据结构——双链表(C语言)
|
5月前
|
存储
数据结构——双向链表(C语言版)
数据结构——双向链表(C语言版)
30 2
|
5月前
|
算法 C语言
数据结构——单向链表(C语言版)
数据结构——单向链表(C语言版)
45 2