秋招算法备战第4天 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、42.环形链表II

24. 两两交换链表中的节点 - 力扣(LeetCode)

一次AC,重点是要画图梳理清楚交换节点的过程

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_node = ListNode(next=head)
        cur = dummy_node

        while cur.next != None and cur.next.next != None:
            # 进行交换
            temp = cur.next
            cur.next = cur.next.next
            temp.next = cur.next.next
            cur.next.next = temp

            # 更新cur
            cur = cur.next.next
        
        return dummy_node.next

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

一开始理解成顺序数第一个了,写完后发现是倒数第N个,于是在前面加了下对长度的统计

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_node = ListNode(next=head)

        cur = dummy_node
        length = 0
        while cur.next:
            length += 1
            cur = cur.next
        
        cur = dummy_node
        for _ in range(length-n):
            if cur.next:
                cur = cur.next
            else:
                break
        if cur.next:
            cur.next = cur.next.next
        
        return dummy_node.next

阅读代码随想录后发现,这题用快慢指针可以一次遍历就解决,大体思路为让slow和fast始终保持n个间距,则fast为None时则slow为倒数第n个

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        # 创建一个虚拟节点,并将其下一个指针设置为链表的头部
        dummy_head = ListNode(0, head)
        
        # 创建两个指针,慢指针和快指针,并将它们初始化为虚拟节点
        slow = fast = dummy_head
        
        # 快指针比慢指针快 n+1 步
        for i in range(n+1):
            fast = fast.next
        
        # 移动两个指针,直到快速指针到达链表的末尾
        while fast:
            slow = slow.next
            fast = fast.next
        
        # 通过更新第 (n-1) 个节点的 next 指针删除第 n 个节点
        slow.next = slow.next.next
        
        return dummy_head.next

面试题 02.07. 链表相交 - 力扣(LeetCode)

一开始想要一次扫描就搞定,但是没想出来。快速扫了下解答,发现先计算了长度,结合上题很自然想到用快慢指针,一次AC

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        lengthA = self.get_length(headA)
        lengthB = self.get_length(headB)
        delta = abs(lengthA-lengthB)

        if lengthA > lengthB:
            return self.get_joint_node(headA, headB, delta)
        else:
            return self.get_joint_node(headB, headA, delta)

    def get_length(self, head):
        dummy_head = ListNode(next=head)
        cur = dummy_head
        length = 0

        while cur.next:
            length += 1
            cur = cur.next
        
        return length

    def get_joint_node(self, head_long, head_short, delta):
        cur_short = ListNode(next=head_short)
        cur_long = ListNode(next=head_long)
        while delta > 0:
            cur_long = cur_long.next
            delta -= 1
        
        while cur_short.next and cur_long.next:
            if cur_short.next == cur_long.next:
                return cur_long.next
            else:
                cur_short = cur_short.next
                cur_long = cur_long.next
        
        return None

142. 环形链表 II - 力扣(LeetCode)

这题之前做过,貌似有数学推到,根据快慢指针计算,但是自己现在想不起来了。
这题很直观的想法就是保留遍历过的Node,如果碰到重复的Node就说明有环,一次AC,代码如下:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        history_node = []
        cur = head

        while cur:
            if cur in history_node:
                return cur
            else:
                history_node.append(cur)
                cur = cur.next

        return None

上面提到的推导在代码随想录,后面可以复习一下,下面是代码

# 快慢指针法
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None


class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        slow = head
        fast = head
        
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            
            # If there is a cycle, the slow and fast pointers will eventually meet
            if slow == fast:
                # Move one of the pointers back to the start of the list
                slow = head
                while slow != fast:
                    slow = slow.next
                    fast = fast.next
                return slow
        # If there is no cycle, return None
        return None

总结

  1. 快慢指针有很多变体
  2. 检测链表是否有闭环可以通过快慢指针转换为追击相遇问题

附录

代码随想录算法训练营第四天 | 24. 两两交换链表中的节点 、19. 删除链表的倒数第N个节点、面试题-链表相交、142.环形链表II_小蛙先森的博客-CSDN博客

你可能感兴趣的:(算法,链表,数据结构)