关于链表的一些疑惑

前言:

​ 链表想通的时候,感觉很简单,比数组还简单,只需要维护一个next指针,然后对指针进行操作即可。但有的时候也会遇到想不通的情况,这时候即便是非常简单的问题,都很难解决。所以这篇文章总结一下链表可能遇到的疑惑,下次遇到的时候查阅即可,无须每次都进行艰难的debug。

当next指向一个对象的时候,该对象发生变化,那么原本的next指向的内容会不会也发生变化?

出现这个疑惑是在一个非常简单的题目:反转一个链表
反转链表
当时就觉得很奇怪,理应是非常简单的,却感觉不会做,画了很久的图,最后还用了4个指针才完成了,代码也是略显复杂。然后一看标准答案,果然跟预期的一样,十分简洁,但当时没有深究,于是后面总是要还的。因为一个地方不明白,后面肯定还会出现,这时候只会更加麻烦。
接下来看一下当时看不懂的标准答案:
答案

当时的疑惑很简单,curr.next指向了prev,但紧接着,prev就又发生了改变,那么curr.next不会改变吗?这看起来有点违反直觉,一开始觉得应该是二者指向了相同的地址,然后地址发生了改变,二者也就同时改变,但其实并不是这样的。这里最关键的就是引用的概念了。这里实际上是两个引用,curr.next跟prev,而curr.next = prev表示的是,curr.next指向了prev引用指向的对象。之后prev指向了其他的对象,并不会对curr.next造成影响。


但是有的地方看起来是会发生改变,为什么?
首先要确定,到底改变的是对象,还是一个引用指向了其他的对象。
立刻想到的就是遍历,移除某个元素的例子:(移除所有val等于某个值的结点)

链表移除某个元素的例子

这里对current操作,最后为什么head也会改变?
首先解答一个很简单的问题,为什么要有current,而不是直接在head上操作。因为我们在中间会将current后移(current = current.next),这就导致了我们最后如果返回的是current,那么current已经移到了最后,即使前面的结果是正确的,也没有意义。
为什么要提这个问题?因为我一开始对于这个就想错了。我最开始对current引用的理解是,在current上操作,避免对head发生改变。但我们确实就是希望head发生改变啊,毕竟我们最后返回的就是改变后的head。所以我们确实就是通过current指向了head,然后改变current的中间结构,同时也成功地改变了head的中间结构,但我们要获取最终的链表,那么就需要获取头结点,所以才不对head进行操作。所以我们仅仅是保留了最初的头指针而已,如果上面的答案,是对head操作,最后返回的是current,作用也是一样的。
接下来就是问题的关键了,那么为什么会跟着改变?看起来current只是一个对象的引用罢了。这就是引用跟对象的区别了,这里的current确实是一个引用,但current.next却是一个实打实的存在于内存中的对象。所以,current.next = current.next.next,是直接在对象上发生改变,那么自然head.next(也就是现在的current.next)也会跟着改变了。而current = current.next,就是引用指向了其他的对象,也就是current引用指向了current.next。由于是引用指向了其他对象,那么显然也不会对head发生改变了。(所以关键就是确定到底是改变引用指向,还是改变对象)

-------------本文结束感谢您的阅读-------------