题目:输入一个长度为 链表,反转链表后,输出新链表的表头。
数据范围n≤1000
要求:空间复杂度O(1),时间复杂度O(n)。
拿到这道题的第一瞬间,小白思维直接给出了解题答案,直接把链表变成数组,逆序之后再变成链表不就好了,确实,我按照这个方法写出的代码确实能提交,也通过了。但当我打开解题区后傻眼了,我意识到自己好像是一个四肢发达头脑简单的生物QAQ,看了一天代码才理解了皮毛。
以下是题解区的大佬代码,加上自己的理解吧,防止下次看的时候又看不懂了,解释写的较为粗糙,你们可能看不懂,我肯定看得懂(哈哈哈)
常规方法
第一个,由于题解区给出的是Java代码,我只能照葫芦画瓢,可能有语法上的错误(我感觉好像没问题)
class ListNode():
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def ReverseList(self, pHead):
if pHead is None:
return None
pre = None
next = None
while pHead:
next = pHead.next # 记录pHead的下一个节点位置
pHead.next = pre # 使pHead指向pre
pre = pHead # pre向下移动一个节点
pHead = next # pHead向下移动一节点
return pre
防止看不懂,下面这些算是对代码的注释吧(这些图都拿PS做的,人都快傻了,现学现卖,不过我选的这颜色是真的好看)
pre指向当前节点的前一个节点
next指向当前节点的后一个节点
假设有四个节点,那其运行的过程如下
1.初始链表元素为A、B、C、D,进入循环前已经将pre和next定义为None
2.当A节点不为空的时候进入循环,记录A的下一个节点位置next=B,然后让A的指针指向pre,也就是A.next=pre。(此时对应循环中的next=pHead.next和pHead.next=pre),下图按代码分步骤说明:
(1)next=pHead.next,next存储pHead的下一个节点,此时pHead的节点为A,所以pHead.next为B节点
(2)pHead.next=pre,让A的指针指向pre,也就是让A.next=pre
3.让pre和pHead继续向下走。让pre指向pHead节点来实现pre的移动,因为上一步中记录了A节点的下一个节点,即next,让pHead指向next来实现头指针pHead的移动。(此时对应循环中的pre=pHead和pHead=next)。以下按代码步骤说明:
(1)pre=pHead,实现pre的向下一节点移动
(2)pHead=next,头指针pHead向下移动,此时next并未更新,所以不变,指向的仍是B节点(进入下一循环才会重新赋值)
4.接下来就重复循环,next指向pHead的下一节点C,pHead指向pre,然后pre和pHead再向下移动一节点,如下图
5.继续循环
6.最后一次循环,next指向pHead的下一个节点,但是此时的pHead为D节点,其下一节点为None,pHead指向next,因此也为None,所以退出循环。此时返回pre则是链表反转后的头指针。
变量交换
第二个的代码写的非常的巧妙,苦逼的我看不懂,到处查,然后找别人给我讲,但是写的真的很好,看完之后整个精神都升华了(痴汉脸),通过这个代码也让我对指针的结构有了新的认识和理解,也算是收获不菲吧
class ListNode():
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def ReverseList(self, pHead): # 变量交换
if pHead is None:
return None
root = None
while pHead:
pHead.next, root, pHead = root, pHead, pHead.next
return root
我本人才能看的注释
root相当于第一种方法的pre,也就是得到反转链表以后的头指针吧
反正这代码我没看懂(万年老菜鸟了),我参考了下面这大佬的解释才理解
Python 多变量赋值的机制(以反转链表为例)
下面就配上我自己的理解,加上大佬的思路,把代码运行的过程图画一画
其实把大佬的文章认真读了应该都能懂,我再画一遍加深一下印象
1.老规矩,原始链表的样子
2.把变量交换拆成三步,这样交换变化的过程更加清晰,也容易理解,对我来说 ,本身变量交换为pHead.next,root,pHead=root,pHead,pHead.next,我就拆成pHead.next=root,root=pHead,pHead=pHead.next三步
(1)pHead.next=root,先让A节点指向root
(2)root=pHead,root向下一节点移动
(3)pHead=pHead.next,前两步可能没什么问题,顺理成章,第三步看着图的话就有点懵逼,pHead.next难道不是None吗?怎么pHead指向了B节点?这个时候就不得不说我上面给出的链接有多香了。python变量交换的时候,会先将右边各项的值计算出来保存起来,计算的时候使用的都是左边给出的初始值,计算期间诞生的中间值并不会被拿去用于新的计算。在这个步骤中第一步里,pHead.next虽然已经被赋值为root,所以pHead已经被指向None,但是在计算后面pHead=pHead.next的时候pHead要等于None吗?当然不是,这样的话代码就凉了,在左边初始值中的pHead.next指向的仍是B节点,并没有受到前面变量交换的影响。(这代码巧在利用python变量交换的特点,因此不需要来存储当前节点的下一节点,也就是第一种方法省略了next变量)
3.进入下一轮循环
4.继续下一轮循环
5.最后一轮循环,pHead指向D的下一个节点,也就是None,root也成为了反转后链表的头指针
递归法
第三个采用的是递归方法,看递归代码对我来说真的是一种折磨,但代码却又写的很好,我又非常想深入了解其中的原理,顺嘴提一句,我本来想使用debug来帮助我更好的了解这个代码,结果却起了反作用,那变量监视差点让我疯掉(抓狂……)
class ListNode():
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def ReverseList(self, pHead): # 递归
if not pHead:
return None
if not pHead.next:
return pHead
headNode = self.ReverseList(pHead.next)
pHead.next.next = pHead
pHead.next = None
return headNode
递归法的解释嘛…能力有限(本人菜的很),代码理解了,但是流程图的话……有点难画,有机会的话一定补上……