数据结构笔记4线性表(中)(《大话数据结构》学习笔记)

【1】顺序存储结构不足的解决办法

思路:
我们反正也是要让相邻元素间留有足够余地,那干脆所有的元素都不要考虑相邻位置了,哪有空位就到哪里,而只是让每个元素知道包下 一个元素的位置在哪里,这样,我们可以在第一个元素时,就知道第二个元素的位置(内存地址) ,而找到它 ; 在第二个元素时,再找到第三个元素的位置(内存地址)。这样所有的元素我们就都可以通过遍历而找到。

【2】线性表链式存储结构定义

线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。这就意味着,这些数据元素可以存在内存未被占用的任意位置。如图:
在这里插入图片描述
以前在顺序结构中,每个数据元素只需要存数据元素信息就可以了。现在链式结构中 ,除了要存数据元素信息外 , 还要存储它的后继元素的存储地址。

因此, 为了表示每个数据元素 ai与其直接后继数据元素 ai+1 之间的逻辑关系 , 对数据元素来说 , 除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置 )。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域 。 指针域中存储的信息称做指针或链 。 这两部分信息组成数据元素 ai 的存储映像,称为结点 (Node) 。

n 个结点 ( ai 的存储映像) 链结成一个链表,即为线性表 ( a1 ,a2 ,… , an ) 的链式存储结构,因为此链表的每个结点中只包含一个指针域 ,所以叫做单链表 。单链表正是通过每个结点的指针域将线性表的数据元素按其逻辑次序链接在一起。

在这里插入图片描述

对于线性表来说,总得有个头有个尾,链表也不例外。我们把链表中第一个结点的存储位置叫做头指针 ,那么整个链表的存取就必须是从头指针开始进行了 。 之后的每一个结点,其实就是上一个的后继指针指向的位置。想象一下,最后一个结点,它
的指针指向哪里?

最后一个, 当然就意味着直接后继不存在了,所以我们规定,线性链表的最后一个结点指针为"空 ( 通常用 NULL或 " ^ " 符号表示 )
在这里插入图片描述

有时,我们为了更加方便地对链装进行操作,会在单链表的第一个结点前附设一个结点,称为头结点 。头结点的数据域可以不存储任何信息 ,谁叫它是第一个呢,有这个特权。也可以存储如线性表的长度等附加信息,头结点的指针域存储指向第一个结点的指针。
在这里插入图片描述

【3】头指针与头结点的异同

在这里插入图片描述

【4】线性表链式存储结构代码描述

若线性表为空表,则头结点的指针域为"空",如图:
在这里插入图片描述

带有头结点的单链表,则如图:
在这里插入图片描述
空链表如图:
在这里插入图片描述

单链表中,我们在 C 语言中可用结构指针来描述。
在这里插入图片描述
从这个结构定义中,我们也就知道,结点由存放数据元素的数据域和存放后继结点地址的指针域组成。假设 p 是指向线性表第 i 个元素的指针,则该结点 ai 的数据域我们可以用 p-> date来表示, p ->date的值是一个数据元素,结点 ai 的指针域可以用p->next 来表示, p->next 的值是一个指针。 p->next 指向谁呢? 当然是指向第 i+l 个元素,即指向 ai+1的指针 。也就是说 ,如果 p->date=ai,那么 p->next=ai+1.

在这里插入图片描述

【5】单链表的读取

在线性表的顺序存储结构中,我们要计算任意一个元素的存储位置是很容易的。

但在单链表中,由于第 i 个元素到底在哪?没办法一开始就知道,必须得从头开始找。因此,对干单链表实现获取第 i 个元素的数据的操作 GetElem ,在算法上,相对要
麻烦一些 。获得链表第 i 个数据的算法思路:
1. 声明一个结点 p 指向链表第一个结点,初始化 j 从 1开始;
2. 当 j<i 时,就遍历链表,让 p 的指针向后移动,不断指向下一结点, j 累加 1;
3. 若到链表末尾 p 为空,则说明第 i 个元素不存在;
4 . 否则查找成功,返回结点 p 的数据 。
实现代码算法如下 :

在这里插入图片描述
在这里插入图片描述
说白了,就是从头开始找,直到第 i 个元素为止 。由于这个算法的时间复杂度取决于 i 的位置,当 i=l 时,则不需遍历,第一个就取出数据了,而当 i=n 时则遍历 n-1次才可以 。 因此最坏情况的时间复杂度是 O (n)。

【6】单链表的插入与删除

6.1单链表的插入

单链表的插入。假设存储元素 e 的结点为 S ,要实现结点 p 、 p->next 和 s之间逻辑关系的变化,只需将结点 s 插入到结点 p 和 p->next 之间即可。
在这里插入图片描述

s->next=p-next;
p->next=s;

即:
在这里插入图片描述

插入后:
在这里插入图片描述

对于单链表的表头和表尾的特殊情况,操作是相同的:
在这里插入图片描述

单链表第 i 个数据插入结点的算法思路:
1. 声明 一结点 p 指向链表第一个结点,初始化 j 从 1 开始;
2. 当 j<i 时,就遍历链表,让 p 的指针向后移动,不断指向下一结点, j 累加 1;
3. 若到链衰末尾 p 为空,则说明第 i 个元素不存在 i
4. 否则查找成功,在系统中生成一个空结点 s;
5. 将数据元素 e 赋值给 s->也data ;
6. 单链表的插入标准语旬 s->next=p->next;p->next=s ;
7 . 返回成功 。
实现代码算法如下:

在这里插入图片描述

在这段算法代码中,我们用到了 C 语言的 malloc 标准函数 , 它的作用就是生成一个新的结点,其类型与 Node 是一样的,其实质就是在内存中找了 一小块空地,准备用来存放 e 数据 s 结点 。

6.2单链表的删除
现在我们再来看单链袤的删除。设存储元素 aj 的结点为 q ,要实现将结点 q 删除单链表的操作 ,其实就是将它的前继结点的指针绕过,指向它的后继结点即可。
在这里插入图片描述

q=p->next;
p->mext=q->next;

在这里插入图片描述
解读这两句代码,也就是说让 p 的后继的后继结点改成 p 的后继结点。有点拗 口呀,那我再打个形象的比方。本来是爸爸左手牵着妈妈的手 , 右手牵着宝宝的手在马路边散步。突然迎面走来一美女 ,爸爸 一下子看呆了,此情景被妈妈逮个正着,于是她生气地甩开牵着的爸爸的手 , 绕过他,扯开父子俩,拉起宝宝的左手就快步朝前走去。 此时妈妈是 p 结点,妈妈的后继是爸爸 p->next ,也可以叫 q 结点,妈妈的后继的后继是儿子 p->nex->next ,即 q->next 。当妈妈去牵儿子的手时,这个爸爸就已经与母子俩没有牵手联系了。

单链表第 i 个数据删除结点的算法思路 :
1. 声明一结点 p 指 向链表第一个结点 , 初始化 j 从 1 开始 ;
2. 当j<1时, 就遍历链表, 让 p 的指针向后移动,不断指向下一个结点 j累 加
3. 若到链表末尾 p 为空,则说明第 i 个元素不存在;
4 . 否则查找成功,将欲删除的结点 p-> next 赋值给 q ;
S . 主在链表的删除标准语句 p->next=q->next;
6 . 将 q 结点中的数据赋值给 e , 作为返回;
7 . 释放 q 结点;
8 . 返回成功。

实现代码算法如下 ;
在这里插入图片描述
这段算法代码里 ,我们又用到了另 一个 C 语言 的标准函数 free 。它的作用就是让系统回收一个 Node 结点,释煎内存。

【7】单链表的整表创建

创建单链表的过程就是一个动态、生成链表的过程。即从"空表"的初始状态起,依次建立各元素结点,并逐个插入链表。
1.声明 一结点 p 和计数器变量 i ;
2 . 初始化一空链表 L;
3. 让 L 的头结点的指针指向 NULL ,即建立一个带头结点的单链表 ;
4 . 循环 :
• 生成一新结点赋值给 p;
• 随机生成一数字赋值给 p 的数据域 p->data;
• 将 p 插入到头结点与前一新结点之间。

实现代码算法如下:
在这里插入图片描述
在这里插入图片描述
这段算法代码里,我们其实用的是插队的办法,就是始终让新结点在第一的位置。我也可以把这种算法简称为头插法,
在这里插入图片描述
可事实上,我们还是可以不这样干,为什么不把新结点都放到最后呢,这才是排队时的正常思维,所谓的先来后到。我们把每次新结点都插在终端结点的后面,这种算法称之为尾插法。
在这里插入图片描述
在这里插入图片描述

【8】单链表的整表删除

当我们不打算使用这个单链表时,我们需要把它销毁,其实也就是在内存中将它释放掉,以便于留出空间给其他程序或软件使用。
单链表整表删除的算法思路如下:
1. 声明一结点 p 和 q ;
2. 将第一个结点赋值给 p;
3 . 循环:
• 将下一结点赋值给 q;
• 释放 p;
• 将 q 赋值给 p 。
实现代码算法如下:

在这里插入图片描述

【9】单链表结构与顺序存储结构优缺点

简单地对单链裴结构和顺序存储结构做对比:
在这里插入图片描述
通过上面的对比,我们可以得出一些经验性的结论:

若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构 。若需要频繁插入和删除时,宜采用单链表结构。比如说游戏开发中,对于用户注册的个人信息,除了注册时插入数据外,绝大多数情况都是读取,所以应该考虑用顺 序存储结构 。而游戏中的玩家的武器或者装备列表,随着玩家的游戏过程中,可能会随时增加或删除,此时再用顺序存储
就不大合适了,单链表结构就可以大展拳脚。当然,这只是简单的类比,现实中的软件开发,要考虑的问题会复杂得多。

当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构 , 这样可以不需要考虑存储空间的大小问题。而如果事先知道线性表的大致长度,比如一年 12 个月,一周就是星期 一至星期日共七天,这种用顺序存储结构效率会高很多。

总之,线性表的顺序存储结构和单链表结构各有其优缺点 , 不能简单的说哪个好,哪个不好,需要根据实际情况,来综合平衡采用哪种数据结构更能满足和达到需求和性能。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值