[leetcode] 92. 反转链表II

题目

反转从位置 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值