LeetCode 92 | 大公司常考的面试题,翻转链表当中指定部分

今天是LeetCode专题的第58篇文章,咱们一块儿来看看LeetCode 92题,翻转链表II(Reverse LInked List II)。web

这题的官方难度是Medium,2451个赞同,145个反对,经过率38.6%。从这份数据上咱们也看得出来,这题的质量很高,广受好评。也的确如此,这是一道很是经典的链表问题,不只考验咱们对于链表的理解和掌握,并且对基本功的要求也很高。面试

题意

给定一个链表和两个整数m和n,m和n分别表明链表当中的第m和第n个元素,其中m <= n。要求咱们经过一次遍历将链表当中m到n这一段元素进行翻转算法

样例

Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL

题解

这题的题意很直接,就是让咱们翻转链表当中指定的部分,而且只能经过一次遍历完成。编辑器

咱们很容易想明白,经过m和n咱们能够将链表分红三个部分:flex

分别是m左侧的,m到n之间的也就是咱们须要翻转的部分,以及n右侧的部分。固然这里可能存在一个特殊状况,m左侧和n的右侧都有可能没有元素,咱们能够先忽略,先考虑最通常的状况。url

翻转链表咱们最经常使用的方法就是先把[m, n]区间里的元素先存储起来,人工翻转了以后,再从新构建成一段链表,替换原链表对应的部分。可是很明显也能够发现,这样作是不符合题目要求的。由于咱们存储元素遍历了链表一次,咱们在构建链表的时候又遍历了一次,至少须要两次遍历才能够完成spa

那怎么样才能一次遍历完成链表的翻转呢?其实也很简单,咱们只须要倒叙插入就行了。指针

好比咱们有这样一段链表,咱们想要翻转其中二、三、4这三个节点:code

首先,咱们从1开始,1是翻转以后的起始部分。咱们先记录下1的位置,这里1指向2保持不变。对于2号节点咱们须要记录下它的后继,这里咱们用tmp记录2号节点的后继,也就是3号节点。以后咱们将2号节点的后继置为None。blog

再下一步,咱们用tmp记录3号节点的后继,将3号节点的后继指向2号节点。

最后,咱们如法炮制,将4号节点的后继指向3号节点,将1号节点的后继指向4号节点,而且将2号节点的后继指向4号节点的原后继,也就是None。这样咱们就完成了链表部分的翻转,其中的原理很简单,就是利用了链表遍历和插入时候的性质,每次将须要翻转部分元素的后继指向它们本来的前驱。这句话有些拗口,可是多读几遍也就理解了。

这个思路很是简单,可是用代码实现却不容易,很容易写错。尤为是在赋值的时候,很容易搞错指针到底应该指向哪里,到底须要哪些临时变量。关于这些内容没有太好的办法,只能增强训练,提高本身的基本功。

最后, 咱们考虑一下特殊状况。题目当中的特殊状况有两种, 第一种是m=1,第二种是n=链表末尾。其中第二种不算是特殊状况,由于在链表当中末尾的元素也有后继,就是None。可是m=1的状况须要当心,由于m=1的时候,翻转部分是没有前驱的,稍稍有些不一样。解决也很简单,咱们单独特判一下这种状况,人为建立一个前驱节点便可。

贴上代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
        if m == n:
            return head
        
        pnt = head
        # 移动m-2格,到达翻转部分的前驱节点
        for i in range(m-2):
            pnt = pnt.next
            
        
        flag = False
        # 特判m=1的状况,若是m=1那么人为制造一个节点做为前驱
        if m == 1:
            flag = True
            pnt = ListNode(0)
            pnt.next = head
            head = pnt
            
        # cur即当前待翻转的节点
        cur = pnt.next
        # pre表示cur的前驱
        pre = pnt
        # last表示最后一个翻转的元素
        last = pnt.next
            
        for i in range(m, n+1):
            # 先记录下当前节点的后继
            nxt = cur.next
            # 将当前节点的后继指向前驱
            cur.next = pre
            pnt.next = cur
            pre = cur
            cur = nxt
            
        # 将last指向翻转以后的元素,将链表串起来
        last.next = cur
        return head.next if flag else head
            
            

总结

链表的相关操做很是考验基本功,虽然算法简单,可是对链表不熟悉,逻辑思考能力稍稍弱一些的同窗想要作出这道题仍是比较困难的。也所以,不少公司在面试的时候喜欢询问链表相关的操做和算法,以此考察候选人的基本功。若是想要进入大公司的,建议好好练习一下相关的问题,必定会有帮助的。

今天的文章到这里就结束了,若是喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、转发、点赞)。

- END -

扫码关注,获取更多优质文章

相关文章
相关标签/搜索