数据结构---我对数组和链表的一些理解

本文探讨了数组和链表两种数据结构的特点和优劣。数组提供快速元素定位,适合区域划分和分治算法,但插入删除操作复杂;链表通过指针链接节点,插入删除灵活,但访问效率较低。链表的变体如循环链表和双向链表各有优势。文章提到,结合数组和链表特点的数据结构能兼顾高效访问和动态修改。此外,还介绍了如何利用双指针解决链表问题,如寻找倒数第N个节点和检查环的存在。

对数组和链表的一些碎碎念

链表的存储结构常常和数组一起谈论,数组中的元素在内存中连续分布,而链表中的节点零散得散落在堆上。由数组和链表各自的存储结构的特点,他俩有着互补的优势和劣势。数组的连续分布性使得定位数组中指定位置上的元素十分方便,已知数组的起始地址和每个元素所占用的偏移长度,计算指定位置上的元素的地址就轻而易举:
指定位置上的元素地址=起始地址+偏移个数∗每个元素所占的偏移长度 指定位置上的元素地址=起始地址+偏移个数*每个元素所占的偏移长度 =+
知道所需访问的元素地址后可以轻易地访问元素的值。也正是因为内存连续性,要在数组中添加一个元素就会变得很麻烦,因为数组所在的这一“内存条”不能被你剪断,在你想插入的位置上打上“补丁”再将内存条和补丁连接这样的做法是不可能实现的。你只能将欲插入位置后面部分的每一个元素都往后挪一下,再将插入的元素放到那个位置。也即访问易,增删难

数组元素容易定位,那么对数组进行区域的划分就十分容易,我完全可以指定(index=1,index=5)这个区间为一个特定区域。而 能对一块数据的划分恰恰是分治法的基础。分治法的核心思想是划分递归,将大问题不断拆分为小问题并逐个解决。这种思路广泛应用于查找和排序中。例如我们常用的归并排序算法就用到了分治思想,此时数组的可被划分性就显得十分重要。我们已知对两个已经排序好的数组进行合并使成为一整个有序数组是容易的,只需用两个指针分别从两个数组的头或尾出发,由这俩指针取出较小或较大的一个数据依次放入空数组中。而面对一个无序数组我们也就可以将无序分为一个个有序列表,问题的最小单元是:一个单独的元素就是一个最小的有序列表。所以归并算法就是将无序数组分割为一个个单独元素,算法的return开始起作用时就会比较有序列表首元素的值的大小再从小到大合并起来,个人理解归并的归就是return的意思,归并的并就是比较大小后合并的操作。类似的,二分查找也是在应用数组的可被划分性。


链表是由散落在内存中的一块块节点用他们身上的指针串联起来的。由于散落,所以类似数组那种一下子就能计算出某个节点的位置是不大可能了,只能从某个节点开始不断next下去。由于他们使用指针连接,所以节点的相对位置不会和物理内存耦合起来,插入一个节点只需向内存申请一个节点的内存,再将要插入位置的相邻两个节点解绑,再将插入的节点和他们串起来。也即访问难,增删易

对于链表而言,还存在许多变体,如循环链表,双向链表。
链表操作在一些同学看来可能特别抽象。链表的增,删,反转等操作都强调先后顺序。如果添加的节点先成为了头节点的next,那么添加的节点就找不到之前头节点的next来作为自己的next了,就这么简单。

由链表的特点,可以发现如果是单链表,那么访问最后一个元素将非常麻烦,因为你需要使用不断next的方法才能到达末尾。对于循环链表,最后一个节点的next指向头节点,头节点的next指向第一个节点,如果设立尾指针指向最后一个节点,那么访问第一个节点和最后一个节点都变会变得容易,都为O(1)。
双向链表是节点中即保存next指针又保存prev指针的链表,弥补了单链表无法向前遍历的缺点。如果对单链表的操作十分熟悉了,那么把握双链表的操作也基本没什么困难,只需要记住链表操作强掉先后顺序,将插入的先连上后面的,再把前后两个分开,然后前面的连上当前的,增加的操作不过是prev指针的连接操作。

由此可见链表和数组的优缺点确实是互补的,如果能将数组和链表结合起来,这样的数据结构一定会非常有趣且高效。比方说我们在数组中存链表头,每个链表头都引出一段链表(代表一段信息链)。这样的结构可以快速定位到某个信息链,增删信息链上的数据节点也十分方便。(这要求信息链的个数不变)

如果一个单向链表的长度未知,要找到链表的倒数第N个节点就有点难了,因为访问指针的移动次数是未知的。你可以先访问一边链表得到链表长度L,再将访问指针移动
(L−(N−1)) (L-(N-1)) (L(N1))

这类问题包括检查链表是否有环都可以用双指针的方法解决。

找到链表的倒数第N个节点使用的是同速双指针,你可以把这个问题想象成黑夜中在甬道中行走。你和你的朋友一前一后同速行走着,一个长为N单位的竹竿被你俩一前一后扛着,你走到甬道尽头时停下,此时你的朋友感觉竹竿不动了也就停下。此时你们刚好相隔N单位。双指针法找倒数第N个节点也是一样的。front指针先移动N次,然后behind指针同时移动,每次移动一个元素,front到达最后一个节点时,behind刚好在Last-N上

对于链表是否有环则需要使用快慢指针,如果链表有环则快慢指针终有一刻会相遇。如果链表无环,则慢指针永远无法追上快指针。

有意思的是你会发现其他问题也能够转化为链表是否有环的问题。比如检查两个链表是否有相交部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值