深入浅出链表

2024-08-23 07:36
文章标签 链表 深入浅出

本文主要是介绍深入浅出链表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.链表的基本概念及结构

1.1基本概念

1.2结构

2.链表的分类

3.链表的实现(循环链表增删查改实现)

1.动态申请节点(结点)​编辑

2.单链表打印

3.单链表尾插

4.单链表头插

5.单链表尾删

6.单链表头删

7.单链表查找

8.在指定位置之前插入数据

9.在指定位置之后插入数据

10.删除pos节点

11.删除pos之后的节点

12.销毁链表


1.链表的基本概念及结构

        链表是一种常见的基础数据结构,它由一系列节点(Node)组成,每个节点包含数据和到下一个节点的引用(指针)。以下是链表的基本概念及其结构:

1.1基本概念

  1. 节点(Node):链表的基本单元,每个节点包含两部分,一部分是存储数据的域(称为数据域),另一部分是存储下一个节点地址的域(称为指针域或链接域)。

  2. 头节点(Head):链表的第一个节点,通常用于表示整个链表。

  3. 尾节点(Tail):链表的最后一个节点,其指针域通常指向NULL,表示链表的结束。

  4. 链表的长度:链表中节点的数量。

  5. 链表的遍历:按照节点间的链接顺序访问链表中的每个节点。

  6. 动态性:链表的大小不是固定的,可以在运行时动态地增加或减少节点。

1.2结构

        在C语言中,链表节点通常使用结构体(struct)来定义。以下是链表节点的一个基本结构:

链表的结构就像是火车一样一节一节的连在一起。

但是每个节点的地址并不像顺序表一样是连续的,而是随机存储的,因此每个节点才需要下一个节点的地址来找到下一节点。

2.链表的分类

  1. 单向链表(Singly Linked List):

    • 每个节点包含一个数据域和一个指向下一个节点的指针。
    • 遍历链表只能从头节点开始,并且只能向一个方向进行。
  2. 双向链表(Doubly Linked List):

    • 每个节点包含一个数据域、一个指向前一个节点的指针和一个指向下一个节点的指针。
    • 可以从两个方向遍历链表。
  3. 循环链表(Circular Linked List):

    • 单向链表的变种,最后一个节点的指针指向头节点,形成一个环。
    • 可以从任意节点开始遍历整个链表。
  4. 双向循环链表(Doubly Circular Linked List):

    • 双向链表的变种,头节点的前一个指针指向最后一个节点,最后一个节点的下一个指针指向头节点,形成一个环。
    • 可以从任意节点开始,向前或向后遍历整个链表。

3.链表的实现(循环链表增删查改实现)

1.动态申请节点(结点)

  • SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));:使用malloc函数动态分配一个SLTNode大小的内存块,并将其强制转换为SLTNode*类型的指针。sizeof(SLTNode)获取SLTNode结构体的大小。

  • if (newnode == NULL):检查malloc是否成功分配了内存。如果newnodeNULL,说明内存分配失败。

  • perror("malloc fail!");:如果内存分配失败,使用perror函数打印错误消息。

  • exit(1);:如果内存分配失败,终止程序执行,返回错误代码1

  • newnode->data = x;:将传入的参数x赋值给新节点的数据域。

  • newnode->next = NULL;:将新节点的指针域初始化为NULL,表示当前节点后面没有其他节点。

  • return newnode;:返回新创建的节点。

2.单链表打印

3.单链表尾插

  • assert(pphead);:使用assert宏来确保传入的头节点指针的指针不是NULL。如果ppheadNULL,程序将终止执行。

  • SLTNode* newnode = SLTBuyNode(x);:调用之前定义的SLTBuyNode函数来创建一个新的节点,并将数据x存储在新节点的数据域。

  • if (*pphead == NULL):检查链表是否为空。如果链表为空(即头节点指针为NULL),则将新节点设置为头节点。

  • else:如果链表不为空,则需要找到链表的最后一个节点。

  • SLTNode* ptail = *pphead;:初始化一个指针ptail,指向头节点。

  • while (ptail->next):遍历链表,直到找到最后一个节点(即节点的next指针为NULL)。

  • ptail = ptail->next;:在循环中,将ptail移动到下一个节点。

  • ptail->next = newnode;:当找到最后一个节点时,将其next指针指向新创建的节点,从而将新节点添加到链表的末尾。

4.单链表头插

  • assert(pphead);:使用assert宏来确保传入的pphead不是NULL。如果ppheadNULL,程序将在这里终止。

  • SLTNode* newnode = SLTBuyNode(x);:调用SLTBuyNode函数创建一个新的节点,并将数据x存储在新节点的数据域中。

  • newnode->next = *pphead;:将新节点的next指针指向原来的头节点。这一步是为了将新节点插入到链表的头部。

  • *pphead = newnode;:更新头节点指针,使其指向新创建的节点。这样,新节点就成为了链表的新头部。

5.单链表尾删

这个接口的实现一开始先要判断链表是否为单节点链表,如果是则直接释放头节点,不是则进行相应的操作。

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • if ((*pphead)->next == NULL):检查链表是否只有一个节点。如果是,直接释放这个节点,并将头节点指针设置为NULL

  • else:如果链表有多个节点,需要遍历链表找到最后一个节点。

  • while (ptail->next):遍历链表直到ptail指向最后一个节点。

  • free(ptail);:释放最后一个节点的内存。

  • ptail = NULL;:将ptail指针设置为NULL,防止野指针。

  • prev->next = NULL;:将倒数第二个节点的next指针设置为NULL,断开与已删除节点的连接。

6.单链表头删

这里的(*pphead)->next不能写成*pphead->next 因为->预算符优先级是高于*的。

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • SLTNode* next = (*pphead)->next;:保存头节点的下一个节点指针。这是因为一旦释放头节点,我们就无法访问链表的其余部分。

  • free(*pphead);:释放头节点的内存。

  • *pphead = next;:更新头节点指针,使其指向原来的第二个节点(现在成为新的头节点)。

7.单链表查找

  • assert(phead);:使用assert宏来确保传入的phead不是NULL。如果pheadNULL,程序将在这里终止。

  • SLTNode* plist = phead;:初始化一个指针plist,使其指向头节点。

  • while (plist):开始循环,当plist不为NULL时继续执行。

  • if (plist->data == x):检查当前节点的数据是否与要查找的值x匹配。

  • return plist;:如果找到匹配的节点,返回该节点。

  • plist = plist->next;:如果没有找到匹配的节点,移动plist到下一个节点。

  • return NULL;:如果在链表中没有找到匹配的节点,返回NULL

8.在指定位置之前插入数据

9.在指定位置之后插入数据

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • assert(pos);:使用assert宏来确保传入的pos不是NULL。如果posNULL,程序将在这里终止。

  • SLTNode* newnode = SLTBuyNode(x);:调用SLTBuyNode函数创建一个新的节点,并将数据x存储在新节点的数据域中。

  • if (pos == *pphead):检查插入的位置是否是头节点。如果是,使用SLTPushFront函数在头节点位置插入新节点。

  • else:如果插入的位置不是头节点,需要找到pos的前一个节点。

  • SLTNode* prev = *pphead;:初始化prev指针指向头节点。

  • while (prev->next != pos):遍历链表直到找到pos的前一个节点。

  • newnode->next = pos;:将新节点的next指针指向pos

  • prev->next = newnode;:将pos的前一个节点的next指针指向新节点。

10.删除pos节点

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • assert(pos);:使用assert宏来确保传入的pos不是NULL。如果posNULL,程序将在这里终止。

  • if (pos == *pphead):检查要删除的节点是否是头节点。如果是,使用SLTPopFront函数执行头删操作。

  • else:如果要删除的节点不是头节点,需要找到该节点的前一个节点。

  • SLTNode* prev = *pphead;:初始化prev指针指向头节点。

  • while (prev->next != pos):遍历链表直到找到pos的前一个节点。

  • prev->next = pos->next;:将pos的前一个节点的next指针指向pos的下一个节点,完成删除操作。

  • free(pos);:释放pos节点的内存。

  • pos = NULL;:将pos指针设置为NULL,防止野指针。

11.删除pos之后的节点

  • assert(pos);:使用assert宏来确保传入的pos不是NULL。如果posNULL,程序将在这里终止。

  • SLTNode* newnode = SLTBuyNode(x);:调用SLTBuyNode函数创建一个新的节点,并将数据x存储在新节点的数据域中。

  • newnode->next = pos->next;:将新节点的next指针指向pos的下一个节点,这样新节点就位于pos的后面。

  • pos->next = newnode;:将posnext指针指向新节点,完成新节点的插入操作。

12.销毁链表

  • assert(pphead && *pphead);:使用assert宏来确保传入的pphead不是NULL,且链表不为空。如果这些条件不满足,程序将在这里终止。

  • SLTNode* pcur = *pphead;:初始化一个指针pcur,使其指向头节点。

  • while (pcur):开始循环,当pcur不为NULL时继续执行。

  • SLTNode* next = pcur->next;:保存pcur的下一个节点的指针,因为pcur将被释放。

  • free(pcur);:释放pcur节点的内存。

  • pcur = next;:移动pcur指针到下一个节点。

  • *pphead = NULL;:循环结束后,将头指针设置为NULL,表示链表已被销毁。

这篇关于深入浅出链表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1098755

相关文章

csu1329(双向链表)

题意:给n个盒子,编号为1到n,四个操作:1、将x盒子移到y的左边;2、将x盒子移到y的右边;3、交换x和y盒子的位置;4、将所有的盒子反过来放。 思路分析:用双向链表解决。每个操作的时间复杂度为O(1),用数组来模拟链表,下面的代码是参考刘老师的标程写的。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#

深入手撕链表

链表 分类概念单链表增尾插头插插入 删尾删头删删除 查完整实现带头不带头 双向链表初始化增尾插头插插入 删查完整代码 数组 分类 #mermaid-svg-qKD178fTiiaYeKjl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-

建立升序链表

题目1181:遍历链表 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2744 解决:1186 题目描述: 建立一个升序链表并遍历输出。 输入: 输入的每个案例中第一行包括1个整数:n(1<=n<=1000),接下来的一行包括n个整数。 输出: 可能有多组测试数据,对于每组数据, 将n个整数建立升序链表,之后遍历链表并输出。 样例输

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237

总结,删除链表节点问题使用到列表,哈希表,递归比较容易超时,我觉得使用计数排序比较稳,处理起来也不是很难。 1. 力扣3217:从链表中移除在数组中的节点 1.1 题目: 给你一个整数数组 nums 和一个链表的头节点 head。从链表中移除所有存在于 nums 中的节点后,返回修改后的链表的头节点。 示例 1: 输入: nums = [1,2,3], head = [1,2,3,

c++ 链表详细介绍

链表是数据结构的一种,由节点组成,每个节点包含数据和指向下一个节点的指针。链表在C++中的实现可以是单链表、双链表或循环链表。以下是链表的详细介绍: 1. 单链表 结构: 节点(Node):每个节点包含数据和一个指针(next),指向链表中的下一个节点。 示例结构: struct Node {int data;Node* next;Node(int d) : data(d), next(

带头结点的线性链表的基本操作

持续了好久,终于有了这篇博客,链表的操作需要借助图像模型进行反复学习,这里尽可能的整理并记录下自己的思考,以备后面复习,和大家分享。需要说明的是,我们从实际应用角度出发重新定义了线性表。 一. 定义 从上一篇文章可以看到,由于链表在空间的合理利用上和插入、删除时不需要移动等优点,因此在很多场合下,它是线性表的首选存储结构。然而,它也存在某些实现的缺点,如求线性表的长度时不如顺序存储结构的

数据结构基础(栈,队列,数组,链表,树)

栈:后进先出,先进后出 队列:先进先出,后进后出 数组:查询速度快,通过地址值和索引定位,查询任意数据消耗时长相同,在内存中是连续存储的,删除效率低,要将原始数据删除,然后后面的数据前移,添加效率低,添加索引位置的元素,剩下的都需要向前后移动 链表:节点的存储位置(地址)里面存储本身的数据值,和下一个节点的地址值,链表中的节点是独立对象,在内存中是不连续的。查询速度慢,无论查询哪个数据都要从

(六十四)第 10 章 内部排序(静态链表的插入排序)

示例代码 staticLinkList.h // 静态链表的插入排序实现头文件#ifndef STATIC_LINK_LIST_H#define STATIC_LINK_LIST_H#include "errorRecord.h"#define SIZE 100#define NUM 8typedef int InfoType;typedef int KeyType;ty