【算法与数据结构】 C语言实现单链表队列详解1

简介: 【算法与数据结构】 C语言实现单链表队列详解

📝队列

前面我们学习了队列的顺序表的实现,本节将用单链表实现队列。

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。下面我们先复习一下队列的基本概念:

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头

🌠 数据结构设计

首先,我们需要定义队列节点的数据结构和队列的数据结构:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 定义队列节点数据结构
typedef int QDataType;//定义了一个新的数据类型 QDataType

typedef struct QueueNode 
{
    QDataType* data;//数据指针
    struct QueueNode* next;//指向下一个节点的指针。
} QNode;

当我们去定义队列的出队和入队时需要用到二级指针,这样传递指针的指针操作起来会有些繁琐,也不能更符合队列的逻辑结构,二级指针代码如下:

//入队列
void QueuePush(QNode** pphead, QNode** pptail);
//出队列
void QueuePop(QNode** pphead, QNode** pptail);

虽然使用指向指针的指针也可以实现队列的功能,但是使用结构体封装的方式更符合常见的数据结构和面向对象编程的思想,能够提高代码的可读性、可维护性和易用性。

// 定义队列数据结构
typedef struct Queue 
{
    QNode* phead;//指向队列的头节点(队首)
    QNode* ptail;//指向队列的尾节点(队尾)。
    int size;//表示队列中元素的数量。
} Queue;

对于队列这种数据结构,使用指向队列头部和尾部的指针以及队列大小的方式进行封装有以下好处:


封装性更好:


使用 Queue 结构体将队列的头部指针、尾部指针和大小封装在一起,更符合面向对象编程的思想,提高了代码的封装性和可维护性。

将队列的相关信息放在一个结构体中,使得对队列的操作更加直观和清晰,降低了出错的可能性。

操作更加直观:


在进行队列操作时,使用 Queue 结构体可以直接通过 phead 和 ptail 指针来操作队列的头部和尾部,而无需传递指向指针的指针。这样使得代码更加清晰,不易出错。

提高代码的可读性和可维护性:


使用 Queue 结构体可以将队列的相关信息集中在一起,使得代码更加清晰易读。

在函数参数中传递 Queue* 类型的指针,比传递指向指针的指针更加直观和易懂,减少了理解代码的难度。

由于队列是先进先出,并且单向的,而头节点是哨兵位,要也可以不要也可以,本文没有用多出使用一个头节点。

🌉初始化队列函数

先确保传入的队列指针 pq 不为空,将队列的头指针、尾指针置为空,大小置为0。

void QueueInit(Queue* pq)
{
  assert(pq); // 断言队列指针是否为空
  pq->phead = NULL; // 初始化头节点指针为空
  pq->ptail = NULL; // 初始化尾节点指针为空  
  pq->size = 0; // 初始化队列大小为0
}

🌠销毁队列函数

首先断言队列指针是否为空,cur从头节点开始遍历队列,保存cur下一个节点的指针,释放cur节点内存,cur移到下一个节点,遍历完整个队列后,将头尾节点指针和大小都重置为初始状态

void QueueDestroy(Queue* pq) 
{

  assert(pq); // 断言队列指针是否为空
  QNode* cur = pq->phead; // cur指向队列头节点
  while (cur) 
  {  
    QNode* next = pq->phead->next; // 保存cur下一个节点的指针
    free(cur); // 释放cur节点内存
    cur = next; // cur指针移到下一个节点
  }
  pq->phead = pq->ptail = NULL; // 重置头尾节点指针为空
  pq->size = 0; // 重置队列大小为0

}

🌉入队函数

将新的数据 x 入队,要分配一个新的节点 newnode,将数据 x 存储在节点中。根据队列是否为空,将新节点连接到队列的尾部,并更新尾指针。如果队列为空,则同时更新头指针。

增加队列的大小。

void QueuePush(Queue* pq, QDataType* x) 
{
  assert(pq); // 断言队列指针是否为空
  QNode* newnode = (QNode*)malloc(sizeof(QNode)); // 申请新节点内存
  if (newnode == NULL) 
  { // 申请失败
    perror("malloc fail"); 
    return;
  }
  newnode->data = x; // 设置新节点数据
  newnode->next = NULL; // 设置新节点next指针为空

  if (pq->ptail)
  { // 如果队列不为空
    pq->ptail->next = newnode; // 尾节点指向新节点
    pq->ptail = newnode; // 更新尾节点指针
  } 
  else 
  { // 队列为空
    pq->phead = pq->ptail = newnode; // 头尾节点同时指向新节点
  }

  pq->size++; // 队列大小增加1

}

🌠出队函数

出队,即删除队列中的队首元素。首先检查队列是否为空,如果为空则直接返回。如果队列只有一个节点,则直接释放这个节点,同时将头尾指针置为空。如果队列有多个节点,则释放队首节点,并将头指针指向下一个节点。减小队列的大小。

void QueuePop(Queue* pq) 
{

  assert(pq); // 断言队列指针是否为空
  if (pq->phead == NULL) 
        return; // 队列为空直接返回
        
  assert(pq->phead != NULL); // 断言头节点不为空
  if (pq->phead->next == NULL)
   { // 队列只有一个节点
    free(pq->phead); // 释放头节点
    pq->phead = pq->ptail = NULL; // 头尾节点同时置空
  } 
  else 
  { // 队列有多个节点
    QNode* next = pq->phead->next; // 保存头节点的下一个节点
    free(pq->phead); // 释放头节点
    pq->phead = next; // 头节点指向下一个节点
  }

  pq->size--; // 队列大小减1

}

🌉获取队首元素函数

获取队列的队首元素,得首先确保队列不为空,然后返回队首节点中存储的数据。

QDaQDataType QueueFront(Queue* pq)
 {
    assert(pq);
    assert(pq->phead != NULL);
    return pq->phead->data;
}

🌠获取队尾元素函数

获取队列的队尾元素,得首先确保队列不为空,然后返回队尾节点中存储的数据。

QDataType QueueBack(Queue* pq)
 {
    assert(pq);
    assert(pq->ptail != NULL);
    return pq->ptail->data;
}

🌉 判断队列是否为空函数

判断队列是否为空,通过检查队列的大小是否为0来确定队列是否为空。

bool QueueEmpty(Queue* pq) 
{
    assert(pq);
    return pq->size == 0;
}

🌉获取队列大小函数

获取队列的大小,即队列中元素的数量。

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

【算法与数据结构】 C语言实现单链表队列详解2:https://developer.aliyun.com/article/1474523

相关文章
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
7天前
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
29 4
|
13天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
56 16
|
9天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
50 8
|
9天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
36 7
|
13天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
62 8
|
16天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
44 4
|
16天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
37 0
|
5月前
|
机器学习/深度学习 算法 C语言
详细介绍递归算法在 C 语言中的应用,包括递归的基本概念、特点、实现方法以及实际应用案例
【6月更文挑战第15天】递归算法在C语言中是强大力量的体现,通过函数调用自身解决复杂问题。递归涉及基本概念如自调用、终止条件及栈空间管理。在C中实现递归需定义递归函数,分解问题并设定停止条件。阶乘和斐波那契数列是经典应用示例,展示了递归的优雅与效率。然而,递归可能导致栈溢出,需注意优化。学习递归深化了对“分而治之”策略的理解。**
108 7
|
6月前
|
搜索推荐 C语言 C++
【排序算法】C语言实现归并排序,包括递归和迭代两个版本
【排序算法】C语言实现归并排序,包括递归和迭代两个版本

热门文章

最新文章