目录
一、链表的概念
二、举例解释说明:数组、链表
1、数组
2、链表
三、如何表示链表
1、介绍链表的节点
2、java如何表示链表
四、链表为什么查找比较慢、删除插入比较快?
1、链表为什么查找比较慢?
2、链表为什么删除插入比较快
(1)链表的插入详解
(2)链表的删除详解
好了,现在大家已经对咱们这个数组,有一定的了解。详细可参见最近系列文章:
java源码系列:技术的本质?数组到底是什么?数组为何查询快插入慢?_爱折腾的华哥的博客-CSDN博客_数组插入为什么慢
java源码系列:ArrayList的底层实现原理和特性_爱折腾的华哥的博客-CSDN博客
那我想问一下,那为什么,为什么在我们这个 HashMap里面,
既然已经有数组了,为什么还要链表呢?
大家来思考一下,因为我们讲过,我们这个数据结构是用来存储数据的,
那既然我们的数组,都已经可以存储数据了,为什么还要有链表呢?
那这里呢,首先,我们去了解一下链表它的定义,它是这样定义的,
链表是一种物理存储单元,非连续非顺序的存储结构。
那刚好跟咱们这个数组是不是相反,你看我们数组它是一个连续的存储单元。
而咱们这个链表它是非连续非顺序的。
链表(Linked list)是一种常见的基础数据结构,是一种线性表,
但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
链表可分为单向链表和双向链表。
一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。
一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接。
Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。
那这个怎么去理解呢?
我经常举这样一个例子,比如像我们有100兆这样的一个MP4电影视频,
如果是说我们以数组的方式去存储的话,那如果说我们存在D盘上,
那我们这个D盘呢,必须它的容量是有100兆,要不他就存不下。
那如果说我们用这种链表的形式呢?
它就可以不用100兆,哪怕你这个D盘只有50兆,另外50兆呢,它可以存在C盘啊。
那这个链表他就是不需要连续的,但是呢咱们这个数组呢,它必须是连续的。
也就是说你这两个必须挨着。
那这就是链表和数组,他们在定义上就有这样的一个区别。
链表的每一个元素,你可以看作为一个节点,
节点包括:data域 和 next域
data域:即为存放数据的域。 next域:就是指向它的下一个节点
好,那我们来看一下链表在我们 java 中怎么去表示呢?
就是当前我创建的这个类,这个类呢,它有一个当前这个data数据,
但是这个类还有一个属性叫next,而它的这个类型,也是当前我这个类的属性,
那也就是这个next,我们叫做什么?
叫做指针,当然我们的这个Java里面叫引用,我们的这个C和C++里面叫指针。
来,咱们现在在这里呢,给大家来说一下,
咱们链表的一个代表形式,是这样的,就是我这张图,
用我们的代码是怎么去表示呢?
看下图,你看我当前的这个 head 节点,我定义为monkey,
然后呢,我这个head 的 next我指向为张三,然后我这个张三的next,我指向为刘一。
所以在这里呢,我们就可以输出monkey、张三、刘一,这三个节点的数据,
那这三个节点,在这里,其实它就是 next 的一个指针。
在链表的形式,它是这样的,当前这个monkey呢,叫做链表的头节点,
然后呢,指向张三,张三再指向 next 刘一,所以刘一,这就是我们的尾节点。
好,那我们知道链表呢,他的删除插入,时间复杂度为O(1),
在查找遍历的时候呢,它的时间复杂度呢为O(n),
所以呢,我们的链表,它的这个删除插入会比较快,查询比较慢
那我们也可以发现,咱们这个数组它的这个优缺点,刚好跟我们链表的优缺点刚好是相反的。
我们的数组,它是这个删除插入比较慢,但是它的查找比较快。
而咱们这个链表呢,刚好就是查找比较慢,但是它的删除插入比较快。
那为什么是这样一个情况呢?下面我们来看一下
那大家来思考一下,为什么他的查询比较慢呢?
链表它有一个不好的点,就是它所有的查询呢,首先需要从这个头节点开始,
因为它没有元素的下标,也就是需要从这个monkey开始,那如果我这个链表长度又比较长,
然后呢,我们要去找的这个数据又是最后一个数据,
那我们首先先要去查monkey,再去查张三,然后再去查刘一,
举个例子,比如说我们现在去查刘一,那如果要查刘一的话,
首先刘一呢,要跟monkey进行对比,然后再去发现monkey跟刘一不相等,
然后再去找到next的张三,张三再next找到刘一,也就是说我们找刘一的话,我们只需要查什么?
查3次,而我们这个列表的长度才多少,才3个吧!
所以我们可以发现,这就是他查询不利的一个地方。
因为链表不需要费时间去移动元素下标。
这又是为啥呢?因为链表不是顺序存储的。
我现在一堆链之间要插一个链进去,我是不是把链解开,
再插一个进去就可以了?对不对?
好,我们来看一下链表的节点,怎么插入,现在这个链表有1、2、3,
有3个节点,那我来了一个4,如下图:
我想把4插到1和2中间,这个4节点我们叫做p,当前1的这个节点叫做curNode,
那我怎么把它插到这个curNode的后边呢?
两件事,第一件事先干啥,注意两步,这个顺序不能反。
如果你说先把1跟4连起来,那对不起,你说错了。
你想一下,如果说你把1跟4先连起来,会怎么样?
1跟4连起来,那么1和2的这个箭头是不是没有了,也就是1它的next是不是指向4了,
那这个2它就找不着了。也就是说没有指向2的东西了,它就游失在内存里了,
一会就被销毁了。
所以第一步不要先把 1 跟 4 它俩连起来。那怎么办?
先把4跟2连起来,所以第一步是什么? 这句话 p.next 等于curNode.next
p.next = curNode.next
把4跟2连起来了,这个时候4指向2
第二步再把1跟4连起来,curNode.next等于p,
p.next = curNode.next
curNode.next = p
这个时候 1指向4,最终 1指向 4,4指向2 ,如下图:
然后这个就是一个新的链表了,这是它的插入。
好,我们看这个的插入,是不是没有任何元素移动,
它是不是就这两句话,两句话都是一个赋值,所以它的复杂度是不是很低啊,它是很快的,
不会像数组一样插入那么慢,对不对?
好,这是插入,那同样的我们来看删除。在链表里做删除,怎么删呢?
现在是之前插入之后的那个链表:1、4、2、3,我在想把4删掉,它是p,也就是我想把p删掉,
第一句话就是想把curNode后边那个节点删掉,
也就是 p等于curNode.next 想把它删掉好,怎么办?
p = curNode.next
是不是先把1和2俩连起来,4删了之后,4的前面跟他的后边是不是要连起来,
不然的话,4删了,这个2又找不着了。因为你直接把4删掉,
4指向2的这个箭头就没有了,那是不是2就找不着了,所以先把1和2它俩连起来,怎么连?
curNode.next等于p.next或者你只写curNode.next.next也行,也可以再写p.next,
curNode.next = p.next 或者 curNode.next.next
是不是1跟2连起来。然后是不是再把p删掉,
del p
所以这个时候你写一个del p或者不写,都无所谓了,4就是你不要它了,就直接没有了。
那我们这个链表是不是1直接到2到3了,4是不是链过来不管了,所以这个链表就完成了,
这个是删除操作,那同样这个操作,是不是也是一个特别快的操作。
它不像数组,需要移动其他元素的下标,所以它的时间复杂度也是很低的。
参考连接:
Java LinkedList | 菜鸟教程
59 链表的插入和删除_哔哩哔哩_bilibili