C语言链表

article/2025/9/14 12:34:51

C语言链表

  • 链表的概念及结构
    • 概念
    • 结构
  • 链表的分类
  • 单链表的实现(无头)
  • 双向链表的实现
  • 总结:链表和顺序表的区别

链表的概念及结构

概念

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

结构

  • 代码
struct Slist
{int* a;struct Slist* next;
};
  • 逻辑结构:
    在这里插入图片描述

  • 物理结构:
    在这里插入图片描述

  • 注意:

  1. 从上图可以看出,链式结构在逻辑上是连续的,但是在物理上是不一定是连续的。
  2. 这些结点一般是从堆上申请出来的。
  3. 从堆上申请的空间,是按照一定的策划来分配的,两次申请的空间可能连续,大概率是不连续的。

链表的分类

  • 实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

    1. 单向或者双向
    ①单向
    在这里插入图片描述
    ②双向
    在这里插入图片描述2.带头或者不带头
    ①带头
    在这里插入图片描述
    ②不带头
    在这里插入图片描述3.循环或者非循环
    ①循环在这里插入图片描述
    ②非循环在这里插入图片描述

  • 虽然有这么多种结构的链表,但是我们实际中最常用的只有两种结构:
    1. 无头单向非循环链表在这里插入图片描述2.带头双向循环链表在这里插入图片描述

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

单链表的实现(无头)

  • 单链表结构
typedef int SLTDateType;typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;
  • 单链表需要的功能
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestory(SListNode** pplist);
  • 功能实现

SListNode* BuySListNode(SLTDateType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){exit(-1);}newnode->data = x;return newnode;
}void SListPrint(SListNode* plist)
{if (plist == NULL){printf("NULL\n");return;}else{while (plist){printf("%d->", plist->data);plist = plist->next;}printf("NULL\n");}
}void SListPushBack(SListNode** pplist, SLTDateType x)
{SListNode* tail = *pplist;SListNode* newnode = BuySListNode(x);newnode->next = NULL;if (tail == NULL){*pplist = newnode;}else{while (tail->next){tail = tail->next;}tail->next = newnode;}
}void SListPushFront(SListNode** pplist, SLTDateType x)
{SListNode* newnode = BuySListNode(x);newnode->next = *pplist;*pplist = newnode;
}void SListPopBack(SListNode** pplist)
{assert(*pplist);SListNode* tail = *pplist;SListNode* Pretail = NULL;if (tail->next == NULL){*pplist = NULL;return;}else{while (tail->next){Pretail = tail;tail = tail->next;}free(tail);tail = NULL;Pretail->next = NULL;}
}void SListPopFront(SListNode** pplist)
{assert(*pplist);SListNode* front = *pplist;*pplist = front->next;free(front);front = NULL;
}SListNode* SListFind(SListNode* plist, SLTDateType x)
{assert(plist);SListNode* pos = plist;while (pos && pos->data != x){pos = pos->next;}return pos;
}void SListInsertAfter(SListNode* pos, SLTDateType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}void SListEraseAfter(SListNode* pos)
{assert(pos);assert(pos->next);SListNode* node = pos->next;pos->next = node->next;free(node);
}void SListDestory(SListNode** pplist)
{SListNode* node = *pplist;SListNode* PreNode = NULL;while (node){PreNode = node->next;free(node);node = PreNode;}
}

双向链表的实现

  • 双向链表的结构
typedef int LTDateType;typedef struct ListNode
{LTDateType data;struct ListNode* next;struct ListNode* prev;
}LTNode;
  • 双向链表的功能
//创建链表返回头结点
LTNode* ListInit();
// 双向链表销毁
void ListDestory(LTNode* phead);
// 双向链表打印
void ListPrint(LTNode* phead);// 双向链表尾插
void ListPushBack(LTNode* phead, LTDateType x);
// 双向链表尾删
void ListPopBack(LTNode* phead);
// 双向链表头插
void ListPushFront(LTNode* phead, LTDateType x);
// 双向链表头删
void ListPopFront(LTNode* phead);
// 双向链表查找
LTNode* ListFind(LTNode* phead, LTDateType x);
// 双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDateType x);
// 双向链表删除pos位置的节点
void ListErase(LTNode* pos);
  • 功能实现
LTNode* ListInit()
{//哨兵位头结点LTNode* phead = (LTNode*)malloc(sizeof(LTNode));if (phead == NULL){printf("开辟空间失败!!!\n");exit(-1);}phead->next = phead;phead->prev = phead;return phead;
}void ListDestory(LTNode* phead)
{assert(phead);LTNode* cur = phead;LTNode* p = NULL;LTNode* tail = phead->prev;while (cur != tail){p = cur;cur = cur->next;free(p);}free(tail);
}void ListPrint(LTNode* phead)
{assert(phead);LTNode* front = phead->next;while (front != phead){printf("%d ", front->data);front = front->next;}printf("\n");
}void ListPushBack(LTNode* phead, LTDateType x)
{assert(phead);LTNode* tail = phead->prev;LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){printf("开辟空间失败!!\n");exit(-1);}newnode->data = x;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}void ListPopBack(LTNode* phead)
{assert(phead);assert(phead != phead->next);LTNode* tail = phead->prev;LTNode* TailFront = tail->prev;TailFront->next = phead;phead->prev = TailFront;free(tail);
}void ListPushFront(LTNode* phead, LTDateType x)
{assert(phead);LTNode* next = phead->next;LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){printf("开辟空间失败!!\n");exit(-1);}newnode->data = x;phead->next = newnode;newnode->prev = phead;newnode->next = next;next->prev = newnode;
}void ListPopFront(LTNode* phead)
{assert(phead);assert(phead != phead->next);LTNode* head = phead->next;//头结点phead->next = head->next;head->next->prev = phead;free(head);
}LTNode* ListFind(LTNode* phead, LTDateType x)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}void ListInsert(LTNode* pos, LTDateType x)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){printf("开辟空间失败!!\n");exit(-1);}newnode->data = x;posPrev->next = newnode;newnode->prev = posPrev;newnode->next = pos;pos->prev = newnode;
}void ListErase(LTNode* pos)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* posNext = pos->next;posPrev->next = posNext;posNext->prev = posPrev;free(pos);
}

总结:链表和顺序表的区别

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,物理上不一定连续
随机访问支持不支持
任意位置上插入或者删除元素可能需要移动元素,效率低下只需修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁

http://chatgpt.dhexx.cn/article/MPBAPCAI.shtml

相关文章

数据结构——C语言实现链表

目录 一. 链表的概念 二. 单链表的增删查改 1.单链表的定义 2.单链表的头插与头删 3.单链表的尾插与尾删 4.单链表的中间插入删除 5.单链表的查找 三. 带头循环双向链表的增删查改 1.带头循环双向链表的定义 2.带头循环双向链表的头插与头删 3.带头循环双向链表的尾…

C语言之链表详解

目录 一、链表定义 二、链表分类 三、链表操作 四、单向链表 1.链表定义 2.插入操作 3.删除操作 4.修改操作 5.查找操作 五、双向链表 1.链表定义 2.插入操作 3.删除操作 4.修改操作 5.查找操作 一、链表定义 链表是一种基本的数据结构,它由一系列节…

C语言 链表创建及操作

C语言 链表创建及操作 第一部分构建链表,定义结构体,分别用头插法、尾插法实现,这里封装了打印函数:printf();做练习方便后续使用;对链表进行查找,并将查找到的值构建一个新的链表;链表的转置&…

Netty权威指南 读书笔记

文档太大,PDF格式的存档已上传到百度网盘: 链接: https://pan.baidu.com/s/1QnUDWujGOXXCq5iWQBIRJg 提取码: q46s

Netty权威指南(第2版) pdf百度网盘下载

欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。 链接: https://pan.baidu.com/s/1DfxG9qKU2fshi6ha1a8NkA 提取码: bmt4

Netty权威指南带目录完整版.pdf

2019独角兽企业重金招聘Python工程师标准>>> Netty权威指南带目录完整版.pdf 转载于:https://my.oschina.net/xiaojianyu/blog/3011828

Netty权威指南总结(三)

五、Netty实战技巧: (一) 多线程编程实践: 1. Netty中使用多线程的技巧: 创建两个NioEventLoopGroup,用于逻辑隔离NIO Acceptor和NIO IO线程。 尽量不要在ChannelHandler中启动用户线程(解码后用于将POJO消息派发到后端…

Netty权威指南(四)TCP粘包/拆包问题

TCP粘包/拆包问题解决之道 上一章一、介绍1.1 TCP粘包/拆包问题说明1.2 TCP粘包/拆包发生的原因1.3 粘包问题的解决策略 二、未考虑TCP粘包导致的功能异常案例2.1 TimeServerHandler的改造2.2 TimeClientHandler的改造2.3 运行 三、利用LineBasedFrameDecoder解决TCP粘包问题3.…

《Netty权威指南》

《Netty权威指南》 基本信息 作者: 李林锋 出版社:电子工业出版社 ISBN:9787121233432 上架时间:2014-5-29 出版日期:2014 年6月 开本:16开 页码:524 版次:1-1 所属分类&#xff…

Netty权威指南~第一章Java的I/O演进之路

本章内容如下: 5种网络I/O模型的介绍I/O多路复用的介绍 1、I/O基础入门 在Java1.4之前,Java对I/O的支持不完善,开发人员在开发高性能I/O的程序时,会面临以下问题: 没有数据缓冲区,I/O性能存在问题没有C…

Netty权威指南(第2版)

网站 更多书籍点击进入>> CiCi岛 下载 电子版仅供预览及学习交流使用,下载后请24小时内删除,支持正版,喜欢的请购买正版书籍 电子书下载(皮皮云盘-点击“普通下载”)购买正版 封页 编辑推荐 1、Hadoop、Storm、Spark、Facebook、…

Netty权威指南——WebSocket协议开发

一、简介 由于HTTP协议的开销,导致他们不适于用于低延迟应用,为了解决这些问题,WebSocket将网络套接字引入到了客户端和服务端,浏览器和服务器之间可以通过套接字建立持久的连接,双方随时可以互发数据给对方&#xff…

netty权威指南 微云_《Netty权威指南》(二)NIO 入门

2.1 BIO 采用 BIO 通信模型的服务器,通常由一个独立的 Acceptor 线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行处理,处理完成后,通过输出流返回应答给客户端,线程销毁。 grap…

netty 权威指南~第11章——WebSoket协议开发

本章主要学习内容如下: 1、WebSocket入门 2、Netty WebSocket协议开发 第节一:WebSocket入门 WebSocket是HTML5开始提供的一种浏览器与服务器进行全双通信的网络技术,WebSocket通信协议与2011年被IEIF定位标准RFC6455,WebsSock…

Netty权威指南 第2版

https://www.cnblogs.com/plxz/p/9910493.html 第一章  Java的I/O演进之路 1.I/O基础入门 1.Linux网络I/O模型简介 1.阻塞I/O模型:最常用的I/O模型,缺省情况下, 所有文件操作都是阻塞的 2.非阻塞I/O模型:recvform从应用层到内…

Netty权威指南总结(一)

一、为什么选择Netty: API使用简单,开发门槛低,屏蔽了NIO通信的底层细节。 功能强大,预制了很多种编解码功能,支持主流协议。 定制能力强,可以通过ChannelHandler对通信框架进行灵活地拓展。 性能高、成熟、…

Netty权威指南

Chapter1.java I/O演进之路 1.1I/O基础入门 在java 1.4之前,java程序员在开发高性能I/O程序的时候,会面临的问题主要有: 1.没有数据缓冲区,I/O性能存在问题 2.没有c或者c中的Channel概念,只有输入和输出流 3.同步…

三维视觉传感器的类型

三角法测量原理 视觉传感器的坐标系统 单一摄像机二维传感器 点结构光视觉传感器 线结构光视觉传感器 条纹结构光视觉传感器 条纹编码三维视觉传感器 彩色编码视觉传感器 被动双目视觉传感器 编码照明双目视觉传感器

传感器sensor

传感器分类 转换原理 传感器名称 典型应用 转换形式 中间参量 电 参 数 电 阻 移动电位器角点 改变电阻 电位器传感器 位移 改变电阻丝或片尺寸 电阻丝应变传感器、半导体应变传感器 微应变、力、负荷 利用电阻的温度效应 热丝传感器 气流速度、液体流量 电阻…

多任务多传感器数据融合实现3D目标检测

转载自:自动驾驶之心 01 引言 本文介绍一篇uber公司在CVPR上发表的一篇论文,即使用多种传感器(LiDAR和RGB相机)数据,以及多任务进行数据融合,实现准确高效的3D目标检测。简而言之,自动驾驶领域…