题目
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入:
1->2->3->4->5->NULL, m = 2, n = 4
输出:
1->4->3->2->5->NULL
思路
找到链表[m,n],将其反转,然后将m与n + 1连接,将n与m-1连接
迭代法
反转m~n的过程类似于leetcode-24-反转链表 I:
从m开始,把当前节点cur的next指向pre,然后向后移动pre,cur(需要第三个变量事先存储cur.next),直到cur将第n节点也反转完毕,停止迭代。
除了反转m-n,还需要记录m-1节点和n+1节点,以便把整个链表完整的连接起来。
代码1.0
删删改改地写出了冗杂的第一版
需要改进的点:
1.知道需要遍历链表得到第m-1个,第m个,第n个节点,因此采用了while循环,cnt计数,然后用了if判断
实际上,已知节点位置,也就是需要遍历的个数,采用 for i in range(…) 循环比while更合适
2.遍历是由cnt=1开始计数,但由于有可能m=1,也就是m-1不存在的情况,因此将link初始化为None,并且输出时采用了分支语句
实际上,可以设定变量dummy=ListNode(0)作为head的前一个节点,这样就解决了m-1有可能不存在的问题,不再需要额外的加以判断,输出的时候一律输出dummy.next,这样不管head是否反转都没有问题。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def reverseBetween(self, head, m, n):
"""
:type head: ListNode
:type m: int
:type n: int
:rtype: ListNode
"""
## 特殊1,不需反转
if head is None or head.next is None:
return head
## 特殊2,不需反转
if m==n:
return head
cnt=1
cur=head
pre=None
link=None
while cur:
next=cur.next
if cnt==m-1:
link=cur #记录第m-1节点,如果没有的话意味着从head反转的,因此需要先初始化为None方便后面判断
if cnt==m:
tail=cur #记录第m节点,也就是反转后的尾结点
if cnt>=m and cnt<=n: # 迭代反转m-n
cur.next=pre
pre=cur
if cnt==n:
break
cur=next
cnt+=1
tail.next=next #连接m-n后面的那段,不需要分情况
if link: # 借助link值判断m-n前面的那段,需要分情况
link.next=pre #从中间某m节点开始反转
return head
else: #从head开始反转,特殊3
return pre
执行用时 :24 ms
内存消耗 :13.1 MB
代码2.0
1.定义一个变量dummyNode=ListNode(0),令dummyNode.next=head,这样就不需要单独处理m==1的情况
2.for循环遍历链表,找到m-1等节点,注意for循环内要减少不必要的操作,比如同时遍历link和cur很没必要
3.最后return的是dummy.next而不是head!因为head也可能反转,唯一不变的是dummy
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def reverseBetween(self, head, m, n):
"""
:type head: ListNode
:type m: int
:type n: int
:rtype: ListNode
"""
if head is None or head.next is None:
return head
if m == n:
return head
#引入dummy,解决了对m==1的额外判断和分支
dummy = ListNode(0)
dummy.next = head
link = dummy
# 遍历找到m-1节点,for 比 while 更合适
for i in range(m-1): # 对i的循环内可以没i,单纯是计数,[0,m-1)共m-1个数
link = link.next
cur = link.next # 此时pre==Node(m-1),cur==Node(m)
# 注,这里for循环内只遍历link,相比同时遍历link和cur可节省10s!
# 反转m-n之迭代法
prev = None # 这里的prev是用于反转的辅助变量
for i in range(n-m+1):
nex = cur.next
cur.next = prev
prev, cur = cur, nex # 此时cur==Node(n+1)
# 上面的4行可以归为1行:
#for i in range(n-m+1):
# cur.next, prev, cur = prev, cur, cur.next
# 连接m和n+1,m-1和n
link.next.next = cur #没有专门记录Node(m),因为还未操作m-1的next,所以Node_m-1.next目前还是Node_m
link.next = prev #这一步才是将m-1与n+1连在一起
return dummy.next #因为head也可能反转,唯一不变的是dummy
执行用时 :16 ms
内存消耗 :13.1 MB
附:代码1.0的小改动
思路和1.0类似,使用stack的实现,比较取巧。重点还是上面的迭代法2.0。
class Solution:
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
counts = 0
stack = []
dummy = ListNode(0)
pre = dummy
while head:
counts += 1
if counts < m:
pre.next = head
pre = pre.next
elif counts >=m and counts <=n:
stack.append(head)
else:
break
head = head.next
while stack:
pre.next = stack.pop()
pre = pre.next
pre.next = head
return dummy.next
执行用时 :16 ms
内存消耗 :13.2 MB