代码随想录算法训练营 Day 3

2 minute read

Published:

链表基础

文章链接

Q1. LeetCode 203

解法1. 使用原链表

难点:需要考虑特殊情况:1. 头节点的值为val;2. 链表为空

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        while head and head.val == val:
            head = head.next
        
        if not head:
            return head
        
        curr = head
        while curr.next:
            temp = curr.next
            if temp.val == val:
                curr.next = curr.next.next
            else:
                curr = curr.next
        
        return head

解法2. 使用虚拟头节点(dummy head)

思路:在头节点之前创建一个虚拟节点,使得可以按照统一的方式进行操作

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        dummy_head = ListNode(next=head)
        curr = dummy_head
        
        while curr.next:
            temp = curr.next
            if temp.val == val:
                curr.next = curr.next.next
            else:
                curr = curr.next
        
        return dummy_head.next

复杂度分析

时间复杂度:O($n$) 空间复杂度:O(1)

Q2. LeetCode 707

思路:按照问题描述实现链表,用dummy_head实现可统一操作。注意各个函数中的特殊情况

class LinkNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class MyLinkedList:

    def __init__(self):
        self.dummy_head = LinkNode()
        self.size = 0

    def get(self, index: int) -> int:
        if index < 0 or index >= self.size:
            return -1
        curr = self.dummy_head
        while index >= 0:
            curr = curr.next
            index -= 1
        
        return curr.val

    def addAtHead(self, val: int) -> None:
        new_head = LinkNode(val, self.dummy_head.next)
        self.dummy_head.next = new_head
        self.size += 1

    def addAtTail(self, val: int) -> None:
        curr = self.dummy_head
        while curr.next:
            curr = curr.next
        new_tail = LinkNode(val)
        curr.next = new_tail
        self.size += 1

    def addAtIndex(self, index: int, val: int) -> None:
        # 判断标准不同,index可以为现有长度,即加在末尾
        if index < 0 or index > self.size:
            return
        
        curr = self.dummy_head
        while index:
            curr = curr.next
            index -= 1
        new_node = LinkNode(val, curr.next)
        curr.next = new_node
        self.size += 1

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return
        
        curr = self.dummy_head
        while index:
            curr = curr.next
            index -= 1
        curr.next = curr.next.next
        self.size -= 1

Q3. LeetCode 206

解法1: 双指针法

思路:使用快慢指针,慢指针代表前一个节点,快指针代表当前节点。最后快指针会指向空值,而慢指针指向新的头节点,因此最后返回慢指针

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        slow = None
        fast = head
        while fast:
            # 获取下一个节点
            temp = fast.next
            # 反转当前节点和前一个节点
            fast.next = slow
            # 更新两个节点
            slow = fast
            fast = temp
        
        return slow

解法2: 递归法

思路:类似于双指针法

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        return self.reverse(None, head)
    
    def reverse(self, prev, curr):
        if curr == None:
            return prev
        temp = curr.next
        curr.next = prev
        
        return self.reverse(curr, temp)

解法3: 虚拟头节点

双指针法的另一种写法

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head = ListNode()
        curr = head

        while curr:
            temp = curr.next
            curr.next = dummy_head.next
            dummy_head.next = curr
            curr = temp
        
        return dummy_head.next

复杂度分析

时间复杂度:O($n$) 空间复杂度:O(1)

总结

对于链表的问题,使用虚拟头节点可以统一操作,简化代码