线性表的链式表现和实现
线性表的链式表示和实现
【链式存储结构】
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
线性表的链式表示又称为非顺序映像或链式映像
用一组物理单位任意的存储单元来存放线性表的数据元素。
这组存储单元既可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上的。
链表元素的逻辑次序和物理次序不一定相同
单链表:指针域和数据域
头指针:记录第一个元素地址
单链表是由头指针唯一确定,因此单链表可以用头指针的名字来命名。
结点:数据元素的存储映像,由数据域和指针域两部分组成。
链表:n个节点由指针链组成一个链表它是线性表的链式存储映像,称为线性表的链式存储结构
链表分类:单链表、双向链表、循环链表
结点只有一个指针域的链表,称为单链表或线性链表
结点由两个指针域的链表,称为双链表
首位相接的链表称为循环链表(尾结点的后继指针域存放头节点或者首元结点的地址)
头指针:是指向链表中第一个结点的指针
首元结点:是指链表中存储第一个数据元素a1的结点
头节点:是在链表的首元结点之前附设的一个结点
【不带头结点】
头指针直接指向首元结点
【带头结点】
头指针指向头节点,头指针指针域存储首元结点地址
如何表示空表
无头结点时,头指针为空时表示空表用 ^表示
有头结点时,当头节点的指针域为空时表示空表
为什么加头节点
【1】便于处理首元结点的处理
首元结点的地址保存在头节点的指针域中,所以链表的第一个位置上的操作和其他位置一致,无须进行特殊处理.
【2】便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头节点的非空指针,因此空表和非空表的处理也就统一了。
头节点的数据域内装的是什么?
头节点的数据域可以为空,也可以放线性表长度等附加信息,但此节点不能计入链表长度值
链表的特点:
(1)结点在存储器中位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
(2)访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。
链表-》顺序存取(必须从头开始一个一个找)
【带头结点单链表】
单链表是由表头唯一确定,因此单链表可以用头指针的名字来命名,若头指针名是l,则称链表为表l
单链表初始化:
生成新结点做头结点,用头指针L指向头结点。
将头结点的指针域置空
判断链表是否为空(链表种无元素,称为空链表(头指针和头结点仍存在))
【思路】判断头结点指针域是否为空
单链表的销毁:链表销毁后不存在
【思路】从头指针开始,依次释放所有结点
清空链表
链表任然存在,但链表中无元素,成为空链表(头指针和头结点仍然在)
【算法思路】依次释放所有结点,并将头结点指针域设置为空
求单链表表长
【思路】从首元结点,依次计数所有结点
取值:取单链表种第i个元素的内容
从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存储结构
查找:按值查找:根据指定数据获取数据所在位置(地址)O(n)
按值查找:根据指定数据获取数据所在的位置序号
插入:在第i个结点前插入新节点O(1)
先创建一个结点,插入数据写在里面数据域,(先)创建结点后继指向第i个结点,(后)插入位置结点前结点后继指向创建结点。
先后顺序不能变,会丢失原来结点地址
如果要换需要借助其他结点
删除:删除第i个结点O(1)(只修改指针)
如果要在单链表中进行前插或删除操作,由于要从查找前驱结点,所耗时间复杂度O(n)
【思路】找到元素,如果删除元素有用保存
将删除元素都节点地址给删除元素前结点
p->next=p->next->next
单链表的建立:
头插法:(元素插入链表头部)O(n)
尾插法:O(n)
从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点
初始时,r同L均指向头结点。每读入一个数据元素则申请一个新节点,将新节点插入到尾结点后,r指向新结点
循环链表
尾结点指针域指向头结点
优点:从表种任一结点出发都可以找到表中其他结点
双向链表
为什么要讨论双向链表
单链表的结点->y有指示后继的指针域->找后继结点方便;
即:查找某结点的后继结点的执行时间为O(1)
无指示前驱的指针域->找前驱结点难:从表头出发查找
即:查找某结点的前驱结点的执行时间为O(n)
【解决方法】可以用双向链表来克服单链表的这种缺点
双向链表:在单链表的每个结点再增加一个指向其直接前驱的指针域prior,这样链表中就形成了由两个方向不同的链,故称为双向链表
双向链表定义三个成员(两个指针域,一个数据域 )
空表情况下头结点两个指针域都为空
头指针的前驱指针域为空,最后一个结点后继为空
双向循环链表
和单链表的循环类似,双向链表也可以有循环表
让头结点的前驱指针指向链表的最后一个结点
让最后一个结点的后继指针指向头结点
双向链表结构的对称性
p->prior->next=p=p->next->prior
在双向链表中有些操作(如:ListLength、GetElem等),因仅涉及一个方向的指针,故它们的算法与线性链表的相同。但在插入、删除时。则需要同时修改两个方向上的指针,两者的操作的时间复杂度均为O(n)
双向链表的插入
顺序表和链表的比较
链式存储结构的优点:
结点空间可以动态申请和释放
数据元素的逻辑次序靠结点的指针来指示,插入和删除时不需要移动数据元素
链式存储结构的缺点
存储密度小,每个结点的指针域需额外占用存储空间。当每个结点的数据域所占字节不多时,指针域所占存储空间的比重显得很大
存储密度=结点数据本身占用空间/结点占用的空间总量
一般,存储密度越大,存储空间的利用率就越高。显然,顺序表的存储密度为1(100%),而链表的存储密度小于1
链式存储结构式分随机存取的结构。对任一节点的操作都要从头指针依指针链查找到该结点,这增加了算法的的复杂度
线性表的应用
线性表的合并
问题描述:
假设利用两个线性表La和Lb分别表示两个集合A和B,现要求一个新的集合A=A交B
La=(7,5,3,11) Lb=(2,6,3)---->La=(7,5,3,11,2,6)
有序表的合并
问题描述:
已知线性表La和Lb中的元素按值非递减有序排列,现要求将La,Lb归并为一个新的线性表Lc,且Lc中的数据元素仍按值非递减有序排列
La=(1,7,8) Lb=(2,4,6,8,10,11)-->Lc=(1,2,4,6,7,8,8,10,11)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?