代码随想录算法训练营Day3 | 203.移除链表元素 | 707.设计链表 | 206.反转链表

文章目录

  • 链表(linked list)
    • 双链表
    • 首尾链表
    • 代码实现
    • 应用
  • 203.移除链表元素
    • 更简洁的解法(只需一个指针)
  • 707.设计链表
  • 206.反转链表
    • Iterative reverse (自己的解法)
    • Recursive reverse (自己的解法)
    • 双指针(推荐解法)
    • 双指针 recursion

链表(linked list)

链表不像数组一样提供天然的 index,想要访问特定元素,就必须遍历链表。

代码随想录算法训练营Day3 | 203.移除链表元素 | 707.设计链表 | 206.反转链表_第1张图片
结构特征:

  • 链表由节点(node)组成,每个节点包括
    • value:当前节点包含的数据
    • next:当前节点指向的下个节点(指针)
  • 链表的入口称为 head
  • 链表的最后一个 node 指向 Null

双链表

每个节点可以同时指向自己的上一个节点与下一个节点,每个节点包括了 value,prev,next

代码随想录算法训练营Day3 | 203.移除链表元素 | 707.设计链表 | 206.反转链表_第2张图片

首尾链表

首尾相连的单链表

代码随想录算法训练营Day3 | 203.移除链表元素 | 707.设计链表 | 206.反转链表_第3张图片

代码实现

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

应用

Insert/Delete 复杂度 使用场景
数组 O ( n ) O(n) O(n) O ( 1 ) O(1) O(1) 数据固定,较少删改,较多查询
链表 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) 数据易变,较多删改,较少查询

注意,插入/删除仅仅指本身的操作,实际中的插入/删除总是需要先通过查询来定位,所以总的复杂度还是 O ( n ) O(n) O(n)

虚拟头节点的简洁写法:virtual_head = LinkedNode(next=head) # head is the actual first node

采用虚拟头节点的时候,一定要return virtual_head.next!!

203.移除链表元素

题目链接 | 解题思路

对于链表元素进行操作时,第一个 node 通常是一个特殊情况。在这道题中,删除 head node 和别的 node 是不一样的。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        curr_node = head
        temp_head = ListNode()      # make a virtual head whose next is the actual head
        temp_head.next = head
        prev_node = temp_head
        
        while curr_node != None:
            if curr_node.val != val:        # nothing to skip
                prev_node = curr_node
            else:
                prev_node.next = curr_node.next
            curr_node = curr_node.next

        return temp_head.next

更简洁的解法(只需一个指针)

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        virtual_head = ListNode(next=head)
        curr_node = virtual_head

        while (curr_node.next != None):
            if curr_node.next.val == val:
                curr_node.next = curr_node.next.next
            else:
                curr_node = curr_node.next

        return virtual_head.next

707.设计链表

题目链接 | 解题思路

操作题,考验对链表的基础理解、操作。

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

class MyLinkedList:
    def __init__(self):
        self.virtual_head = LinkedNode()

    def get(self, index: int) -> int:
        count = 0
        curr_node = self.virtual_head.next
        while (curr_node != None):
            if count == index:
                return curr_node.val
            curr_node = curr_node.next
            count += 1
        return -1

    def addAtHead(self, val: int) -> None:
        insert_node = LinkedNode(val=val)       # the node of "val" to be inserted
        insert_node.next = self.virtual_head.next
        self.virtual_head.next = insert_node

    def addAtTail(self, val: int) -> None:
        curr_node = self.virtual_head
        while (curr_node.next != None):
            curr_node = curr_node.next
        # now curr_node is the last node in the linked list
        curr_node.next = LinkedNode(val=val)

    def addAtIndex(self, index: int, val: int) -> None:
        # here, we can take virtual node as an existing node
        # then, we can insert "val" after the index-th node
        curr_node = self.virtual_head
        count = 0
        while (curr_node != None):
            if count == index:          # insert after curr_node
                insert_node = LinkedNode(val=val)
                insert_node.next = curr_node.next
                curr_node.next = insert_node
            curr_node = curr_node.next
            count += 1

    def deleteAtIndex(self, index: int) -> None:
        # here, we can take virtual node as an existing node
        # then, we can delete the "next node" when we reach "index"
        curr_node = self.virtual_head
        count = 0
        while (curr_node != None):
            if count == index:
                if curr_node.next == None:
                    curr_node.next = None
                else:
                    curr_node.next = curr_node.next.next
            curr_node = curr_node.next
            count += 1

206.反转链表

题目链接 | 解题思路

同样可以使用双指针进行 in-place 的反转!双指针真是无处不在啊。

Iterative reverse (自己的解法)

创建新链表,遍历进行反转,属于基础解法,但是占用了不少空间。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        virtual_head = ListNode()
        curr_node = head
        while (curr_node != None):
            new_node = ListNode(val=curr_node.val)
            new_node.next = virtual_head.next
            virtual_head.next = new_node
            curr_node = curr_node.next
        return virtual_head.next

Recursive reverse (自己的解法)

创建新链表的 recursion,同时还有超高的时间复杂度,不推荐。唯一的优势是比较容易想到。
时间复杂度: O ( n 2 ) O(n^2) O(n2)

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # base case: 0 or 1 node
        if head == None or head.next == None:
            return head
        # base case: 2 nodes in the list
        if head.next.next == None:
            new_head = ListNode(val=head.next.val)
            new_head.next = ListNode(val=head.val, next=None)
            return new_head
        
        # recursive case: more than 2 nodes
        temp_head = self.reverseList(head.next)
        curr_node = temp_head
        while (curr_node.next != None):
            curr_node = curr_node.next
        curr_node.next = ListNode(val=head.val)
        return temp_head

双指针(推荐解法)

快指针探路,慢指针保存答案,但是注意在 linked list 中需要额外的变量来保存原本的 next。所以要清楚认识到不同变量的含义。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
# iterative reverse
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        prev_node = None        # record the previous node in the original linked list
        curr_node = head        
        temp_node = None        # temporarily keep the value in the middle
        # update prev_node iteratively so it records the final results

        while (curr_node != None):
            temp_node = curr_node.next          # keep the link to next_node
            curr_node.next = prev_node
            prev_node = curr_node
            curr_node = temp_node
        
        return prev_node

双指针 recursion

比双指针更简单,直接依靠 recursion 处理反转方向,但也对变量含义的理解有更高的要求。注意这个方法的空间复杂度更高,因为每层 recursion 都要创建一个新的中间变量。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
# recursive reverse
class Solution:
    def reverse(self, results: Optional[ListNode], rest: Optional[ListNode]) -> Optional[ListNode]:
        # base case: no more node to reverse
        if rest == None:
            return results
        # recursive
        temp_node = rest.next
        rest.next = results
        return self.reverse(rest, temp_node)
        
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        return self.reverse(None, head)

你可能感兴趣的:(代码随想录算法训练营一刷,算法,链表,python)