数据结构--线性表

本文深入解析线性表的定义、基本操作及存储结构,包括顺序表、单链表、双链表、循环链表和静态链表,对比分析顺序表与链表的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线性表的定义和基本操作

1.1、线性表的定义

线性表是具有相同的数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n=0时,该线性表是一个空表。若用L命名线性表,则其一般表示为:

L = (a1,a2,...,ai,ai+1,...,an)

式中,a1是唯一的“第一个”数据元素,又称为表头元素;an是唯一的“最后一个”数据元素,又称表尾元素。除第一个元素外,每个元素有且仅有一个直接前驱。除最后一个元素外,每个元素有且仅有一个直接后继。由此,可以得出线性表的特点如下:

1) 表中元素的个数有限
2) 表中元素具有逻辑上的顺序性,在序列中各元素排序有其先后次序
3) 表中元素都是数据元素,每个元素都是单个元素
4) 表中元素的数据类型都相同,这意味着每个元素占有相同大小的存储空间
5) 表中元素具有抽象性,即仅讨论元素键的逻辑关系,而不考虑元素究竟表示什么内容

1.2、线性表的基本操作

一个数据结构的基本操作是指其最核心、最基本的操作。其他较复杂的操作可通过调用其基本操作来实现。线性表的主要操作如下:

InitList(&L):初始化表。构造一个空的线性表。
Length(L):求表长。返回线性表L的长度,即L中数据元素的个数。
LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。
ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。
ListDelete(%L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。
PrintList(L):输出操作。按前后顺序输出线性表L第i个位置的元素,并用e返回删除元素的值。
Empty(L):判空操作。若L为空表,则返回true,否则返回false。
DestryList(&L):销毁操作。销毁线性表,并释放线性表L所占用的内存空间。

二、线性表的顺序表示

线性表的顺序存储又称顺序表。它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。第1个元素存储在线性表的起始位置,第i个元素的存储位置后面紧接着存储的是第i+1个元素。因此,顺序表的特点是表中元素的逻辑顺序与其物理顺序相同。
假设线性表L存储的起始位置为LOC(A),sizeof(Elemtype)是每个数据元素所占用的存储空间的大小,则表L所对应的顺序存储如下图所示:
在这里插入图片描述

注意:线性表中元素的位序是从1开始的,而数组中元素的下标是从0开始的。

三、线性表的链式表示

由于顺序表的插入、删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储。链式存储线性表时,不需要使用地址连续的存储单元,即它不要求逻辑上相邻的两个元素在物理位置上也相邻,它通过“链”建立起数据元素之间的逻辑关系,因此对线性表的插入、删除不需要移动元素,而只需要修改指针。

3.1、单链表的定义

线性表的链式存储又称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据元素之间的线性关系,对每个链表结点,除存放元素自身的信息外,还需要一个存放一个指向其后继的指针。单链表结点结构如下图所示:
在这里插入图片描述
其中data为数据域,存放数据元素;next为指针域,存放其后继结点的地址。
利用单链表可以解决顺序表需要大量连续存储空间的缺点,但单链表附加指针域,也存在浪费存储空间的缺点。由于单链表的元素是离散地分布在存储空间中的,所以单链表是非随机存取的存储结构,即不能直接找到某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。
通常用头指针来标识一个单链表,如单链表L,头指针为NULL时表示一个空表。此外,为了操作上的方便,在单链表第一个结点之前附加一个结点,称为头结点。头结点的数据域可以不设任何信息,也可以记录表长等相关信息。头结点的指针域指向线性表的第一个元素结点,如下图示:
在这里插入图片描述
头结点和头指针的区分:不管带不带头结点,头指针始终指向链表的第一个结点,而头结点是带头结点的链表中的第一个结点,结点内通常不存储信息。
引入头结点后,可以带来两个优点:
① 由于开始结点的位置被存储在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其它位置上的操作一直致,无需进行特殊处理。
② 无论链表是否为空,其头指针都指向头结点的非空指针(空表中国头结点的指针域为空),因此空表和非空表的处理也就得到了统一。

3.2、双链表

单链表结点中只有一个指向其后继的指针,使得单链表只能从头结点依次顺序地向后遍历。要访问某个结点的前驱结点(插入、删除操作时),只能从头开始遍历,访问后继结点的时间复杂度为O(1),访问前驱结点的时间复杂度为O(n)。
为了克服单链表的上述缺点,引入了双链表,双链表结点汇总有两个指针prior和next,分别指向其前驱结点和后继结点,如下图所示:
在这里插入图片描述
双链表仅在单链表的结点汇总增加了一个指向其前驱的prior指针,因此在双链表中执行按值查找和按位查找的操作与在单链表中的相同。但双链表在插入和删除操作的实现上,与单链表有着较大的不同。这是因为“链”变化是也需要对prioi指针做出修改,其关键是保证在修改的过程中不断链。此外,双链表可以很方便地找到其前驱结点,因此,插入、删除结点算法的时间复杂度仅为O(1)。

3.3、循环链表

1. 循环单链表
循环单链表和单链表的区别在于,表中最后一个结点的指针不是NULL,而改为指向头结点,从而整个链表形成一个环。
在循环单链表中,表尾结点*r的next域指向L,如表中没有指针域为NULL的结点,因此,循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针。
循环单链表的插入、删除算法与单链表几乎一样,所不同的是若操作在表尾进行,则执行的操作不同,以让单链表继续保持循环的性质。当然,正是因为循环单链表是一个“环”,因此在任何一个位置上的插入和删除操作都是等价的,无须判断是否是表尾。
在单链表中只能从表头结点开始往后顺序遍历整个链表,而循环单链表可以从表中任意一个结点开始遍历整个链表。有时对单链表常做的操作时在表头和表尾进行的,此时对循环单链表不设头指针而设尾指针,从而使得操作效率更高。其原因是,若设的是头指针,对表尾进行操作需要O(n)的时间复杂度,若设的是尾指针r,r->next即为头指针,对于表头和表尾进行操作都只需要O(1)的时间复杂度。

2. 循环双链表
循环双链表就是在双链表的基础上,将尾结点的后继指针指向头结点,头结点的前驱指针指向尾结点。在循环双链表中,某结点*p为尾结点时,p->next==L;当循环双链表为空表时,其头结点的prior域和next域都等于L。
在这里插入图片描述

3.4、静态链表

静态链表借助数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,前面所讲的链表中的指针不同的是,这里的指针是结点的相对地址(数组下标),又称游标。和顺序表一样,静态链表也要预先分配一块连续的内存空间。静态链表和单链表的对应关系如下图所示:
在这里插入图片描述
静态链表以next==-1作为其结束的标志。静态链表的插入、删除操作与动态链表的相同,只需要修改指针,而不需要移动元素。总体来说,静态链表没有单链表使用起来方便,但在一些不支持指针的高级语言(如Basic)中,这是一种非常巧妙的设计方法。

四、顺序表和链表的比较

1. 存取方式
顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序存取元素

2. 逻辑结构与物理结构
采用顺序存储时,逻辑上相邻的元素,其对应的物理存储位置也相邻。而采用链式存储时,逻辑上相邻的元素,其物理存储位置不一定相邻,其对应的逻辑关系是通过指针链接来表示的。

3. 查找、插入和删除操作
对于按值查找,顺序表无序时,两者的时间复杂度均为O(n);顺序表有序时,可采用折半查找法,此时的时间复杂度为O(log2n)。
对于按序号查找,顺序表支持随机访问,时间复杂度仅为O(1),而链表的平均时间复杂度为O(n)。顺序表的插入、删除操作,平均需要移动半个表长的元素。链表的插入、删除操作。只需修改相关结点的指针域即可。由于链表的每个结点都带有指针域,因而在存储空间上要比顺序存储付出的代价大,而存储密度不够大。

4. 空间分配
顺序存储在静态存储分配情形下,一旦存储空间装满就不能扩充,若再加入新元素,则会出现内存溢出,因此需要预先分配足够大的存储空间。预先分配过大,可能会导致顺序表后部大量闲置;预分配过小,又会造成溢出。动态存储分配虽然存储空间可以扩充,但需要移动大量元素,导致操作效率降低,而且若内存中没有更大块的连续存储空间,则会导致分配失败。链式存储的结点空间只在需要是申请分配,只要内存有空间就可以分配,操作灵活、高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值