原题:链接
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
解法一
其实刚开始看这道题也没啥特别的想法,直接迭代方法开干就行了。但是有很多细节需要处理,导致代码修改了很久才通过。这里简单地捋一下吧。设第m
个节点为start
节点,在后续节点的翻转操作之前,我们需要保存start
节点的前一个节点front
,因此为了使得程序便于处理一些边界条件,加入一个虚拟头结点vHead
来作为front
的初始值。而后利用到两个指针pre
和cur
交替移动变换来翻转链表,反转操作熟悉之后就可以轻松地解决,有兴趣可以看看自己对于反转链表的理解:
其中有迭代的方法和递归的方法。这里我们讨论迭代法,迭代需要一个临时变量t
来存下一个cur
节点,避免翻转后无法到达该节点。翻转完成之后,则直接处理头与尾的链接。此时pre
节点指向的是第n
个节点,我们把front
节点指向它,而start
节点就指向pre
节点的下一个节点,也就是cur
节点即可。
代码如下:
def reverseBetween(head):
if not head:
return head
i = 1
vHead = ListNode(-1)
vHead.next = head
temp = head
front = vHead
while i != m:
if i == m-1:
front = temp
i += 1
temp = temp.next
# 迭代反转
pre, cur = temp, temp.next
while cur and i != n:
t = cur.next
cur.next = pre
pre = cur
cur = t
i += 1
# front.next 即为 start 节点
front.next.next = cur
front.next = pre
return vHead.next
解法二
递归+回溯的思想参考的官方题解。这里仅仅做一个针对于自己理解的说明。
反转的操作也可以通过两两对换的形式来完成。正如LeeTCode 344 反转字符串一样。利用双指针法,其中i
指向头,j
指向尾,然后完成两两互换,并移动各自指针的位置,直到i == j
,代码不难写出:
def reverseString(s):
n = len(s)
i, j = 0, n-1
while i < j:
s[i], s[j] = s[j], s[i]
i += 1
j -= 1
因此本题也可以借鉴这种思想,通过交换值来达到反转的目的。但面临的问题是,数组拥有索引方便取值,而单链表只有next
指针。因此我们的想法是通过递归,使得需要指向交换头与尾的left
和right
指针齐头并进,先指到它们各自的位置,然后通过回溯完成交换与指针的移动。这里需要注意,回溯的目的,我们通过程序就可以看到,不仅完成了值的交换,也实现了right
指针往前的效果,正如反转字符串中的j -= 1
。不仅如此,我们还需要获得上一次递归结束left
指针的位置作为我们程序终止的依据,这里我们仅在内部递归函数中改变不可变类型变量left
的指向即可,因此在变量前添加关键字nonlocal
(具体进一步了解可以搜索了解一下Python变量作用域的L(local),E(enclosing),G(global),B(built-in)原则,变量名会按照局部->嵌套->全局->内置的优先级顺序进行搜索)。
最后的最后,再分析一下结束条件。考虑到链表个数为奇数或偶数,只要满足:
left == right or left == right.next
我们就可以停止交换,但是不能在此直接return
,因为返回之后将会进行错误的继续交换操作。科学的做法就是设立一个标志位stop
,当且仅当stop
为False
的时候才执行交换操作。参考代码如下:
def reverseBetween(head):
def recur_and_back(right, m, n):
nonlocal left
if n == 1:
# right 指向了正确的位置
return
right = right.next
if m > 1:
# 如果 left 没有指到其位置则继续
left = left.next
# 递归
recur_and_back(right, m-1, n-1)
if left == right or left == right.next:
# 后续停止交换了
self.stop = True
if not self.stop:
left.val, right.val = right.val, left.val
# 交换之后 left 需向后移一位
# right 则交由回溯实现向前
left = left.next
if not head:
return None
left, right = head, head
self.stop = False
recur_and_back(right, m, n)
return head
希望通过此题能加深对递归和回溯的理解。