最近重新回顾了一下链表,在自己手动写了链表的生成以及相应的增删查改操作后,对链表的理解更加深刻,于是在leetcode上刷了对应的习题。

王争老师列举了一些链表必刷题,感觉有必要做一下这些习题。

链表的必刷题有:

  • 单链表反转
  • 链表中环的检测
  • 两个有序的链表合并
  • 删除链表倒数第n个结点
  • 求链表的中间结点


文章目录

  • 206. [反转链表](https://leetcode-cn.com/problems/reverse-linked-list/)
  • 递归实现:
  • 迭代实现
  • 92. [反转链表 II](https://leetcode-cn.com/problems/reverse-linked-list-ii/)
  • 剑指 Offer 22. [链表中倒数第k个节点](https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/)
  • 使用快慢指针
  • 先遍历再取节点
  • 19. [删除链表的倒数第 N 个结点](https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/)
  • 876. [链表的中间结点](https://leetcode-cn.com/problems/middle-of-the-linked-list/)
  • 快慢指针法
  • 先遍历再求节点
  • 21. [合并两个有序链表](https://leetcode-cn.com/problems/merge-two-sorted-lists/)
  • 141. [环形链表](https://leetcode-cn.com/problems/linked-list-cycle/)
  • 142. [环形链表 II](https://leetcode-cn.com/problems/linked-list-cycle-ii/)


206. 反转链表

写python自动刷课_leetcode

这道题可以用迭代或者递归实现。

递归和迭代的思路都是不断反转两个节点。

递归实现:
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # 有一个节点或者没有节点时,直接返回列表
        if not head or not head.next:
            return head

        pre, node = head, head.next
        while node:
            pre, node = self.__reverse_two(pre, node)
        
        head.next = None
        head = pre
        return head

    def __reverse_two(self, pre, node):
        # 反转两个节点
        tmp = node.next
        node.next = pre
        pre, node = node, tmp
        return pre, node
迭代实现
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        pre, node = head, head.next
        while node:
            temp = node.next
            node.next = pre
            pre = node
            node = temp
        
        head.next = None
        return pre
92. 反转链表 II

写python自动刷课_快慢指针_02

反转链表92的题目可以在反转链表206的题目上修改。

反转的逻辑可以复用。

这里主要是要找到反转的起始节点和终止节点,同时要保存剩下的节点。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def reverseBetween(self, head, left, right):
        """
        :type head: ListNode
        :type left: int
        :type right: int
        :rtype: ListNode
        """
        if not head.next:
            return head
        
        fake_head = ListNode(-1)
        fake_head.next = head
        pre = fake_head
        # 得到反转起始节点的前向节点
        for i in range(left - 1):
            pre = pre.next

        reverse_end = pre
        # 得到反转终止节点
        for i in range(right - left + 1):
            reverse_end = reverse_end.next
        
        # 剩余不反转的节点
        end_node = reverse_end.next
        # 反转起始节点
        reverse_start = pre.next
        # 反转终止节点指向None, 反转起始节点的前向节点指向None
        reverse_end.next = None
        pre.next = None

        # 反转中间部分的节点
        # 反转起始节点reverse_start, 终止是 reverse_end
        pre_, node_ = reverse_start, reverse_start.next
        while node_:
            pre_, node_ = self.__reverse(pre_, node_)
        
        pre.next = pre_
        reverse_start.next = end_node
        return fake_head.next

    # 利用递归反转,也可以使用迭代
    def __reverse(self, pre, node):
        tmp = node.next
        node.next = pre
        pre, node = node, tmp
        return pre, node
剑指 Offer 22. 链表中倒数第k个节点

写python自动刷课_链表_03

求倒数第k个节点有两种方法,第一种是使用快慢指针,第二种就是先求出链表的长度,再遍历得到对应节点

使用快慢指针

使用快慢指针可以只扫描一次。

# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getKthFromEnd(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        
        fast = slow = head
        # 快指针先走k步
        for i in range(k):
            fast = fast.next
        while fast:
            slow = slow.next
            fast = fast.next
        # 当快指针走完的时候,慢指针就位于该节点
        return slow
先遍历再取节点
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getKthFromEnd(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if not head or not head.next:
            return head

        # 先求出链表长度
        n = 0
        p = head
        while p:
            p = p.next
            n += 1
        
        # 遍历找到k的位置
        pos = 0
        p = head
        while p and k != (n - pos):
            p = p.next
            pos += 1
        return p
19. 删除链表的倒数第 N 个结点

写python自动刷课_快慢指针_04

删除倒数第n个节点也可以使用快慢指针的思路。

# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        # 没有节点或者只有一个节点时直接返回
        if not head or not head.next:
            return head
        
        fast = slow = head
        # 快指针先走n步
        for i in range(n):
            fast = fast.next

        # pre指针用于找到倒数第n个节点的前向节点
        pre = ListNode(-1)
        while fast:
            pre.next = slow
            slow = slow.next
            fast = fast.next
            pre = pre.next

        pre.next = slow.next
        return head
876. 链表的中间结点

写python自动刷课_链表_05

采用的方法是先求链表长度,再求中间节点。或者快慢指针。
感觉这道题和求倒数第n节点的思想都差不多

快慢指针法

快指针比慢指针多走两步,当快指针走完时,慢指针指向的节点就是中间节点

# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def middleNode(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow
先遍历再求节点
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def middleNode(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        n = 0
        p = head
        while p:
            p = p.next
            n += 1

        mid = n // 2
        p = head
        pos = 0
        while p and pos != mid:
            p = p.next
            pos += 1
        return p
21. 合并两个有序链表

写python自动刷课_快慢指针_06

两个链表分别用指针遍历

# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        fake_head = ListNode(-1)
        pre = fake_head
        while l1 and l2:
            if l1.val <= l2.val:
                pre.next = l1
                l1 = l1.next
            else:
                pre.next = l2
                l2 = l2.next
            pre = pre.next  # 写代码的时候遗漏了该指针的移动
        # 剩余节点
        pre.next = l1 or l2
        return fake_head.next
141. 环形链表

写python自动刷课_leetcode_07

这道题也是使用快慢指针,如果有环,快慢指针最终相遇。

快指针始终比慢指针多走一步

# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        # 加上这个判断之后时间更快了
        if not head or not head.next:
            return False

        fast = slow = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            # 快慢指针相遇说明存在环
            if slow == fast:
                return True
        return False
142. 环形链表 II

写python自动刷课_反转链表_08

这道题也使用快慢指针,只不过还需要看相遇的节点是第几个节点。

# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return None
        
        fast = slow = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                pre = head
                while pre != slow:
                    pre = pre.next
                    slow = slow.next
                return pre
        return None