链表相关问题

常用的方法是增加哨兵节点,快慢指针,可以简化问题

反转

力扣206 反转链表

题目描述:

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 时间复杂度O(n) 空间复杂度O(1)
        pre = None
        cur = head # 当前遍历的节点
        while cur:
            nxt = cur.next # 记录
            cur.next = pre # 反转
            pre = cur # 更新pre和cur
            cur = nxt
        return pre # 注意返回的节点是哪个

力扣92 反转链表II

题目描述:

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例:

输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

class Solution:
    def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
        # 反转链表最后的结果是pre cur cur最后指向要反转的这一段的下一个节点
        # 增加一个哨兵节点 解决left=1的特殊情况
        # 时间复杂度O(n) 空间复杂度O(1)
        dummy = ListNode(next = head)
        p0 = dummy
        for _ in range(left - 1): # 循环结束后p0指向要反转那一部分的前一个节点
            p0 = p0.next

        pre = None
        cur = p0.next
        for _ in range(right - left + 1):
            nxt = cur.next # 记录下一个节点
            cur.next = pre  # 反转
            pre = cur
            cur = nxt # 更新pre cur

        # 处理反转链表的左右
        # 这里好难理解,p0没有更新过那么应该指向反转这一段的前一个节点
        # 由于中间的链表已经反转 所以p0应该指向pre p0的下一个节点应该指向cur
        # pre可以理解了 但是cur的位置我还是搞不清楚
        p0.next.next = cur
        p0.next = pre
        return dummy.next

力扣25 K个一组翻转链表

题目描述:

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例:
 

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        # 类似于反转链表中的某一段
        # O(n) O(1)
        # 先求出链表长度
        n = 0
        cur = head
        while cur:
            n += 1
            cur = cur.next
        # 增加哨兵节点
        dummy = ListNode(next = head)
        p0 = dummy
        
        # 接下来每k个一组反转链表
        while n >= k:
            n -= k
            pre = None
            cur = p0.next
            for _ in range(k):
                nxt = cur.next # 记录当前节点的下一个节点
                cur.next = pre # 反转
                pre = cur
                cur = nxt # 更新pre cur
            nxt = p0.next
            p0.next.next = cur
            p0.next = pre
            p0 = nxt

        return dummy.next

快慢指针

力扣876 链表的中间结点

题目描述:

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例:

输入:head = [1,2,3,4,5,6]
输出:[4,5,6]
解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。

class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 快慢指针的核心思想就是两个指针slow fast每次更新走的步幅不一样
        slow = head
        fast = head
        while fast and fast.next: # fast为空或者fast.next为空就可以退出循环了
            slow = slow.next
            fast = fast.next.next
        return slow

力扣141 环形链表

题目描述:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
         # 快慢指针 每次只快一步,之后会相遇
         slow = head
         fast = head
         while fast and fast.next:
              slow = slow.next
              fast = fast.next.next
              if slow == fast:
                   return True
         return False

力扣142 环形链表II

题目描述:

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 快慢指针 
        # 相遇时慢指针一定没有走完一个环
        # 假设从头节点到入口的距离是a 从入口到相遇点的距离是b 从相遇点到入口的距离是c
        # 假设相遇时快指针比慢指针多走了k个环
        # 2*(a+b) = a + b + k*(b+c) 慢指针走的距离*2=快指针走的距离
        # a - c = (k-1)(b+c)
        # 继续走,head从头节点出发,slow从相遇点出发,他们都走了c步之后,slow到达入口,此时head与slow之间的距离一定是环长(b+c)的倍数
        # O(n) O(1)

        slow = head
        fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if fast is slow:
                while slow != head:
                    slow = slow.next
                    head = head.next
                return slow
        return None

 力扣143 重排链表

题目描述:

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

输入:head = [1,2,3,4]
输出:[1,4,2,3]

class Solution:
    # 中间节点
    def middleNode(self, head):
        slow = head
        fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow
            
    # 反转链表
    def reverseList(self, head):
        pre = None
        cur = head
        while cur:
            nxt = cur.next
            cur.next = pre
            pre = cur
            cur = nxt
        return pre


    def reorderList(self, head: Optional[ListNode]) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        # 先找到中间节点,将原链表分成两个链表,然后把后面的链表反转,最后再插入
        # O(n) O(1)

        mid = self.middleNode(head)
        head2 = self.reverseList(mid)
        while head2.next:
            nxt = head.next
            nxt2 = head2.next
            head2.next = head.next
            head.next = head2
            head = nxt
            head2 = nxt2

删除

力扣237 删除链表中的节点

题目描述:

有一个单链表的 head,我们想删除它其中的一个节点 node

给你一个需要删除的节点 node 。你将 无法访问 第一个节点  head

链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。

删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:

  • 给定节点的值不应该存在于链表中。
  • 链表中的节点数应该减少 1。
  • node 前面的所有值顺序相同。
  • node 后面的所有值顺序相同。

自定义测试:

  • 对于输入,你应该提供整个链表 head 和要给出的节点 nodenode 不应该是链表的最后一个节点,而应该是链表中的一个实际节点。
  • 我们将构建链表,并将节点传递给你的函数。
  • 输出将是调用你函数后的整个链表。

示例:

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:指定链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9

class Solution:
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        # 按照题目要求,可以将node.next.val赋值给当前节点,然后修改指针
        node.val = node.next.val
        node.next = node.next.next

力扣19 删除链表的倒数第N个节点

题目描述 :

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

# 方法一
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        # 快慢指针的思想
        # left=0 right=n 同时向前走 当right走到最后left就走到了倒数第n个
        # 关于何时需要创建dummy,需要修改头节点时
        # O(n) O(1)

        dummy = ListNode(next=head)
        right = head
        for _ in range(n):
            right = right.next
        left = dummy
        while right:
            left = left.next
            right = right.next
        left.next = left.next.next
        return dummy.next
# 方法二
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        l = 0 # 存储链表长度
        cur = head
        while cur:
            l += 1
            cur = cur.next
        count = 0
        cur = head
        while count < l-n:
            pre = cur
            cur = cur.next
            count += 1
        # 删除倒数第n个结点的特殊情况
        if l ==n:
            return head.next
        pre.next = cur.next
        return head

 力扣83 删除排序链表中的重复元素

题目描述:

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

示例:

输入:head = [1,1,2,3,3]
输出:[1,2,3]
class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # O(n) O(1)
        if not head: # 如果链表为空
            return None
        cur = head
        while cur.next:
            if cur.next.val == cur.val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return head

力扣82 删除排序链表中的重复元素II

题目描述:

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例:

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 判断当前节点的下一个和下下个,不需要两个指针,但是判断是否相等时要套循环
        dummy = ListNode(next=head)
        cur = dummy
        while cur.next and cur.next.next:
            val = cur.next.val
            if cur.next.next.val == val:
                while cur.next and cur.next.val == val:
                    cur.next = cur.next.next
            else:
                cur = cur.next
            
        return dummy.next
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值