数据结构入门:队列

简介: 数据结构入门:队列

前言

       队列,作为一种重要的数据结构,在计算机科学中扮演着重要的角色,它按照先进先出(FIFO)的原则进行操作。它可以用来解决许多实际问题,队列的应用范围广泛,从操作系统中的进程调度,到网络中的数据传输,再到算法中的搜索和排序,队列无处不在。那么接下来,让我们一同开始这段关于队列的探索之旅吧!


1.队列

1.1 队列的概念及结构

队列:

       只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性。

  • 入队列:进行插入操作的一端称为队尾
  • 出队列:进行删除操作的一端称为队头

1.2 队列的实现

       队列的特性是先进先出,这里就要考虑选择顺序表还是链表来实现队列。队列需要大量的头删尾插的操作,顺序表进行头删的效率太低,所以这里我们选用链式结构来实现。

1.2.1 队列的定义

 

typedef int Datatype;
typedef struct QueueNode
{
  struct QueueNode* next;
  Datatype data;
 }QueueNode;

        和定义链表的节点一样。但是也有所不同,我们知道队列需要大量的头删和尾插,为了方便尾插和头删我们最好再创建两个指针,一个指向头,另一个指向尾。那这样岂不是需要二级指针解决吗?

       这里我将会介绍一种新的方法来解决头删问题,前边我们已经知道对链表头进行操作方法有三种:

  • 一种是使用二级指针来达到修改头节点的目的
  • 另外一种是使用函数返回类型来达到修改的目的
  • 此外我们还可以使用带头结点的链表来解决对链表头操作时需要特殊处理的情况

        进行尾插操作时,需要传递头指针和尾指针,这样使用时函数的参数变多了,调用函数时也比较费劲,那这里我们不如直接再定义一个结构体来存放队列的头指针和尾指针,以及队列的长度。

typedef int Datatype;
typedef struct QueueNode
{
  struct QueueNode* next;
  Datatype data;
 }QueueNode;
typedef struct Queue
{
  QueueNode* head;
  QueueNode* tail;
  int size;
}Que;

       这样我们传参时就只需传第二个结构体就可以进行了。通过第二个结构体找到第一个结构体,然后进行一系列头删、尾插的操作,使用时实质和二级指针类似,它效果和带头节点的链表类似,只需要一级指针就可以解决。

       虽然栈和队列的逻辑非常简单,但是它们的结构却变得更加复杂了,链表和顺序表是后续数据结构学习的基础,一定要灵活掌握。

1.2.2队列的初始化

void QueueInit(Que* pq)
{
  assert(pq);
  pq->head = pq->tail = NULL;
  pq->size = 0;
}

       初始化就非常简单了,只需要将头指针和尾指针进行初始化置为NULL,长度置为0。这里我们也不需要创建新节点,由于我们就只要一个接口需要创建新节点(尾插),所以不需要封装成函数,对节点进行初始化。

我们在调用函数时也非常简单,将第二个结构体传过去:

Que q;//创建结构体变量
QueueInit(&q);

1.2.3 入队

void QueuePush(Que* pq, Datatype x)
{
  assert(pq);
  QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
  if (newnode == NULL)
  {
    perror("malloc");
    exit(-1);
  }
  newnode->data = x;
  newnode->next = NULL;
  if (pq->tail == NULL)
  {
    pq->head = pq->tail = newnode;
  }
  else
  {
    pq->tail->next = newnode;
    pq->tail = newnode;
  }
  pq->size++;
}

        在入队操作时,我们需要创建新的节点,这里注意:创建新节点是为队列的节点申请空间,定义的第一个结构体才是队列节点的类型,所以新建节点是的类型也应该与第一个结构体对于,链表没有节点时,这时需要将头指针和尾指针指向新节点,进行特殊处理。后续再次入队就可以直接将新节点链接到尾节点的后边。入队新节点的同时注意将size++;记录队列长度。

1.2.4 判空

入队之后就是出队,但考虑到后续多个接口中需要判空,这里就提前封装成一个函数

 

bool IsEmpty(Que* pq)
{
  assert(pq);
  return (pq->head == NULL);
}

函数类型为bool类型,该类型返回值只有true(1)和false(0)两种,所以最后直接返回条件:pq->head == NULL如果为真就为true,假就为false。

1.2.5 出队

void QueuePop(Que* pq)
{
  assert(pq);
  assert(!IsEmpty(pq));
  if (pq->head->next == NULL)
  {
    free(pq->head);
    pq->head = pq->tail = NULL;
  }
  else
  {
    QueueNode* next = pq->head->next;
    free(pq->head);
    pq->head = next;
  }
  pq->size--;
}

        出队需要先判断队列是否为NULL,然后将头指针向后移动(改变结构体指针),释放掉第一个节点,然后队列长度size--,但需要考虑一下特殊情况,就是删空的情况,如果只剩下一个节点就可以直接释放掉,然后将头指针和尾指针置为NULL。

1.2.6 队头队尾数据

//队头
Datatype QueueFront(Que* pq)
{
  assert(pq);
  assert(!IsEmpty(pq));
  return pq->head->data;
}
//队尾
Datatype QueueBlack(Que* pq)
{
  assert(pq);
  assert(!IsEmpty(pq));
  return pq->tail->data;
}

        这里没什么好说的,直接返回队列头指针和尾指针指向节点的数据。但注意都需要进行判空操作。

1.2.7 队列长度

int QueueSize(Que* pq)
{
  assert(pq);
  return pq->size;
}

        直接返回队列的size即可。

1.2.8 队列销毁

 

void DestoryQueue(Que* pq)
{
  assert(pq);
  QueueNode* cur = pq->head;
  while (cur)
  {
    QueueNode* next = cur->next;
    free(cur);
    cur = next;
  }
  pq->head = pq->tail = NULL;
  pq->size = 0;
}

        像链表一样,遍历队列一个一个的销毁节点,最后将指针置为NULL,队列长度size置为0。

       在链表学习以来部分同学会不会有这样的疑惑:销毁空间是不是不允许部分销毁吗?那一个一个节点的销毁不就可以部分销毁?那是因为标准C规定的是,释放空间时,一个malloc申请的空间必须全部释放,不可以将一个malloc申请的空间部分释放,而链表是每次增加节点就申请一次空间(malloc一次),每个节点都是相对独立的空间,所以需要一个个的遍历逐个销毁。


总结

       希望通过这篇博客,可以使你对队列有了更深入的了解,并能够应用这一概念解决实际问题。无论是在计算机科学中还是在日常生活中,队列都是一种重要的工具和思维方式,它能够帮助我们更好地组织和管理事务。最后,感谢阅读!      

相关文章
|
4月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
400 9
|
2月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
161 77
|
13天前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
26 11
|
27天前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
2月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
58 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
2月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
53 9
|
2月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
48 7
|
4月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
108 5
|
4月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
5月前
【数据结构】-- 栈和队列
【数据结构】-- 栈和队列
32 0