代码随想录算法训练营第四天 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点 、面试题 02.07. 链表相交、142.环形链表II、 链表总结

382 阅读4分钟

24. 两两交换链表中的节点

代码随想录视频讲解

代码随想录文章讲解

迭代

  • 先考虑交换的两个节点,看作一组,组内交换;再考虑组间如何连接,指针如何更新
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head = ListNode(0,head)
        prev = dummy_head
        cur = head
        while cur and cur.next:
            next_node = cur.next
            
            # swap node
            cur.next = next_node.next
            next_node.next = cur
            prev.next = next_node 
            
            # update node
            prev = cur
            cur = cur.next
        return dummy_head.next
      
# another way to indicate the pointer
# 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_head = ListNode(0,head)
        
        # connect node always one node ahead of the swap nodes group
        connect = dummy_head
        while connect.next and connect.next.next:
            swap_1 = connect.next
            swap_2 = connect.next.next
            
            # swap node
            swap_1.next = swap_2.next
            swap_2.next = swap_1
            # connect to the second node in the swap nodes group (because of swapping, swap_2 is at the beginning of the nodes group)
            connect.next = swap_2 
            
            # update node
            connect = swap_1
        return dummy_head.next

19. 删除链表的倒数第 N 个结点

代码随想录视频讲解

代码随想录文章讲解

利用栈

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        node_stack = []
        dummy_head = ListNode(-1, head)
        cur = dummy_head
        while cur:
            node_stack.append(cur)
            cur = cur.next
​
        prev, next_node = None, None
        for i in range(n+1):
            cur = node_stack.pop()
            if i == n-2:
                next_node = cur
            elif i == n:
                prev = cur
        prev.next = next_node
        return dummy_head.next

两次遍历

  • 第一次遍历找到链表长度
  • 第二次遍历找到删除节点
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_head = ListNode(-1, head)
        cur = dummy_head
        length = 0
        while cur:
            cur = cur.next
            length += 1
​
        prev = dummy_head
        for i in range(length - n - 1):
            prev = prev.next
​
        next_node = prev.next.next
        prev.next = next_node
​
        return dummy_head.next

一次遍历

  • 快慢指针,慢指针在快指针出发遍历过n+1之后出发,当快指针到达链表尾时,慢指针指向的就是删除节点的前一个指针
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy_head = ListNode(-1, head)
        slow, fast = dummy_head, dummy_head
        while fast:
            if n < 0:
                slow = slow.next
            fast = fast.next
            n -= 1
        slow.next = slow.next.next
        return dummy_head.next

面试题 02.07. 链表相交

代码随想录文章讲解

暴力解法:双循环(超时)

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None# Time complexity: O(M*N)
# Space complexity: O(1)
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        cur_A = headA
        while cur_A:
            cur_B = headB
            while cur_B:
                if cur_A == cur_B:
                    return cur_A
                cur_B = cur_B.next
            cur_A = cur_A.next
        return None

利用Set(HashTable)来存储节点进行比较

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None# Time complexity: O(M+N)
# Space complexity: O(M)
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        cur_A = headA
        node_set = set()
        while cur_A:
            node_set.add(cur_A)
            cur_A = cur_A.next
        cur_B = headB
        while cur_B:
            if cur_B in node_set:
                return cur_B
            cur_B = cur_B.next
        return None

双指针

  • A,B两个链表可能存在交集的长度最长为min(len(A),len(B)),并且是从最后一个节点开始数起

  • 所以假定len(B) > len(A),我们从链表A的头节点和链表B的第len(B)-len(A)个节点开始两两对应比较,如果能找到相同的第一个节点就返回它;不然就返回None

  • 如何找到较长链表的比较起始节点?

    • 利用双指针,当较短链表遍历完整个链表时,较长链表还剩下len(B)-len(A)个节点没有遍历,此时将原本遍历较短链表的指针指向较长链表的头节点,当较长链表遍历完成时,遍历较短链表的指针将指向较长链表比较的第一个节点
    • 此时将遍历较长链表的指针指向较短链表的头节点,开始两两比较节点
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None# Time complexity: O(M+N)
# Space complexity: O(1)
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        pA = headA
        pB = headB
        while pA != pB:
            pA = headB if pA is None else pA.next
            pB = headA if pB is None else pB.next
        return pA

142. 环形链表 II

代码随想录视频讲解

代码随想录文章讲解

快慢指针找环是否存在,双指针找链表尾连接到链表中的位置

  • 快慢指针:快指针一次走两个节点,慢指针一次走一个节点,如果链表中有环,则快指针一定会在环中的某个节点追上慢指针(快指针比慢指针走得 快1个节点,即每次靠近慢指针一个节点,直到相遇)(此方法可以用于环的检测)

  • 让双指针一个指向链表头,一个指向快慢指针找到的在环中的一个节点,开始遍历链表。当两个指针相遇时,此时的节点就是链表尾连接到链表中的位置

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
    def get_intersect(self, head):
        fast, slow = head, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if slow == fast:
                return slow
        return None
​
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head:
            return None
​
        intersect = self.get_intersect(head)
        if not intersect:
            return None
​
        p_head = head
        p_intersect = intersect
        while p_head != p_intersect:
            p_intersect = p_intersect.next
            p_head = p_head.next
​
        return p_head

HashTable存储遍历过的节点

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        node_set = set()
        cur = head
​
        while cur:
            if cur in node_set:
                return cur
            node_set.add(cur)
            cur = cur.next
​
        return None

链表总结

代码随想录文章讲解

链表总结

链表经典题目解法

虚拟头节点dummy_head

使用虚拟头节点可以让对所有节点的操作一致

dummy_head = ListNode(0)
dummy_head.next = head

快慢指针

检测链表是否有环

fast = fast.next.next
slow = slow.next

双指针

可以用来找从链表末数起的第n个节点

while fast:
  if n < 0:
    slow -= slow.next
  fast = fast.next
  n -= 1