链表
链表也是一个非常基本的数据结构,跟数组有很大的不同。
我们知道数组是占用连续内存的线性结构
ADD就是该数组的首地址,访问元素可以使用ADD【下标】。然而我们可以发现数组占用连续的空间,需要扩容的时候需要开辟更大的连续空间然后复制过去,并且当我们移除 插入元素时,需要遍历数组将元素进行前移补位和后移空位。数组虽然访问方便,但是对于这样的操作是在是太不友好了。
为了,出现了链表结构。该结构与数组完全不同。链表的每个元素可以占据不同的位置,而不是连续的内存。在单链表中,每个元素有一个指向下一个元素位置的指针,示意图如下:
(注意术语)链表的元素称为节点,第一个是头结点,最后一个是尾节点,指向后面的指针是后向指针。跟数组的方式相比,存储相同的元素值时的样子大概是这样:
我们的链表仅能以头结点为开始位置,根据后向指针找到下一个元素(很麻烦是不是)。但是我们的插入和移除操作却非常方便,比如我们在元素1位置插入55:
对于链表来说,我们仅需要改变两个元素的内容就可以。我们开辟新的元素空间并赋值为55(注意,前面说过,链表本来也不是线性的,每个元素的位置是随机申请的),将元素0的后向指针指向新的元素,并将新的元素的后向指针指向原本的1号元素,这就完美的将一个新的元素插入了进来。
但是对于数组就不这么容易了,我们需要将1号元素和之后的所有元素后移一位,并且实际操作中有可能触发扩容操作。当数组的长度非常大的时候,链表插入和删除操作的优势就越发的明显了。不是很确切的来说,链表是牺牲了内存空间和顺序访问的性能换来了这个优势。
双向链表
有的人可能会问,链表只能从前往后访问吗...大佬们当然考虑过这个问题,为此就出现了双向链表。同样的原理,双向链表只是多了一个前向指针,使得可以访问上一个元素
双向链表的优势显而易见。在插入好删除元素的时候,我们需要更改的指针多了两个。
前一个元素的后向指针和后一个元素的前向指针都需要指向新的元素,并且新的元素也是有前向指针和后向指针两个指针。对于学习链表,只要前后指针的关系弄明白,就一点都不乱了。如何弄明白??自己画两次插入和删除操作的示意图就明白了,有时候光看是不行的。
链表遍历
对于单向链表和双向链表都是有元素间关系的指针,我们遍历链表需要借助指针得到下一个元素。
单向链表
大概的操作时
首先头结点的地址是已知的,这是就是Head吧 节点的值为value 前向指针为pre 后向指着为next
临时值=head.next # 获取头结点的后向指针
# 如果指针空 则表示没有下一个元素
while 临时值不为空: # 后向指针不为空表示有元素
输出(临时值.value)
临时值 = 临时值.next # 获取当前节点的后向指针
链表的操作精髓就是指针,元素由指针关联在一起,形成一个完整的链条。
最后再重新思考一下,跟数组相比链表的优势是什么?? 它的精髓又在哪里??
过两篇之后我们会根据原理,简单的编程实现链表,只有自己动手干才能真正了解它的原理,学编程可不能手懒。