【力扣hot100】刷题笔记Day10

前言

  • 一鼓作气把链表给刷完!!中等题困难题冲冲冲啊啊啊!

25. K 个一组翻转链表 - 力扣(LeetCode)

  • 模拟

    • reverse返回头尾,cur和pre指向dummy,cur前进k之后进行翻转,接上头和尾
    • class Solution:
          def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
              # 翻转链表前k项
              def reverse(head, k):
                  pre, cur = None, head
                  while cur and k:
                      temp = cur.next
                      cur.next = pre
                      pre = cur
                      cur = temp
                      k -= 1
                  return pre, head  # pre为头,head为尾
              dummy = ListNode(0, head)
              pre = cur = dummy
              count = k  # 用于重复计数
              while cur.next and count:
                  count -= 1
                  cur = cur.next
                  if count == 0:
                      temp = cur.next  # 存一下段后节点
                      pre.next, cur = reverse(pre.next, k)  # 连接段前+翻转
                      cur.next = temp  # 连上段后节点
                      pre = cur  # 更新pre指针
                      count = k  # 恢复count继续遍历
              return dummy.next

 138. 随机链表的复制 - 力扣(LeetCode)

  • 路飞的题解真是太强啦!!优雅清晰简洁
  • 哈希表

    • 构建【旧节点 → 新节点】哈希表,利用键的next和random更新新链表
    • """
      # Definition for a Node.
      class Node:
          def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
              self.val = int(x)
              self.next = next
              self.random = random
      """
      
      class Solution:
          def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
              if not head: return None
              dic = {}
              # 1. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
              cur = head
              while cur:
                  dic[cur] = Node(cur.val)
                  cur = cur.next
              # 2. 构建新节点的 next 和 random 指向
              cur = head
              while cur:
                  dic[cur].next = dic.get(cur.next)
                  dic[cur].random = dic.get(cur.random)
                  cur = cur.next
              # 3. 返回新链表的头节点    
              return dic[head]
  • 拼接 + 拆分

    • 原链表中间插入新节点,构造新链表的random之后再进行拆分
    • class Solution:
          def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
              if not head: return None
              dic = {}
              # 1. 复制各节点,并构建拼接链表
              cur = head
              while cur:
                  temp = Node(cur.val)
                  temp.next = cur.next
                  cur.next = temp
                  cur = temp.next
              # 2. 构建各新节点的 random 指向
              cur = head
              while cur:
                  if cur.random:
                      cur.next.random = cur.random.next  # 关键一步
                  cur = cur.next.next
              # 3. 拆分两链表
              cur = newhead = head.next
              pre = head
              while cur.next:
                  pre.next = pre.next.next
                  cur.next = cur.next.next
                  pre = pre.next
                  cur = cur.next
              pre.next = None  # 单独处理原链表尾节点
              return newhead  # 返回新链表头节点

148. 排序链表 - 力扣(LeetCode)

  • 归并排序(顶到底递归)

    • 参考路飞题解
    • 0/1个节点直接返回,用快慢指针得到中点,分割后调用自己,再用合并升序列表的方法进行合并
    • class Solution:
          def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
              if not head or not head.next: return head
              # 快慢指针分割链表
              slow, fast = head, head.next
              while fast and fast.next:
                  fast, slow = fast.next.next, slow.next
              mid = slow.next  # 右半部分的头节点
              slow.next = None  # 断开两部分
              # 递归进行归并排序
              left = self.sortList(head)
              right = self.sortList(mid)
              # 合并左右两个链表
              dummy = cur = ListNode(0)
              while left and right:  # 根据大小依次插入新链表
                  if left.val < right.val:
                      cur.next = left
                      left = left.next
                  else:
                      cur.next = right
                      right = right.next
                  cur = cur.next
              cur.next = left if left else right  # 接上剩下的
              return dummy.next
  • 归并排序(底到顶合并) 

    • 从sublenth = 1开始合并h1和h2,注意break的时机以及需要断开和接上
    • class Solution:
          def sortList(self, head: ListNode) -> ListNode:
              # 合并两个有序链表
              def merge(head1, head2):
                  dummy = cur = ListNode(0)
                  while head1 and head2:
                      if head1.val < head2.val:
                          cur.next = head1
                          head1 = head1.next
                      else:
                          cur.next = head2
                          head2 = head2.next
                      cur = cur.next
                  cur.next = head1 if head1 else head2
                  return dummy.next
              # 如果只有一个节点直接返回head
              if not head: return head
              # 统计链表长度
              lenth = 0
              cur = head
              while cur:
                  cur = cur.next
                  lenth += 1
              # 开始循环合并
              dummy = ListNode(0, head)
              sublenth = 1
              while sublenth < lenth:
                  pre, cur = dummy, dummy.next
                  while cur:
                      head1 = cur
                      for i in range(1, sublenth):
                          if cur.next:
                              cur = cur.next
                          else:
                              break  # 如果还没找到head2说明不用合并,下一轮
                      head2 = cur.next
                      if not head2: break  # 空就不合并了
                      cur.next = None  # 断开第一段后
                      cur = head2
                      for i in range(1, sublenth):
                          if cur.next:
                              cur = cur.next
                          else:
                              break
                      temp = cur.next  
                      cur.next = None  # 断开第二段后
                      cur = temp
                      merged = merge(head1, head2)  # 合并
                      pre.next = merged
                      while pre.next:
                          pre = pre.next  # pre更新到合并后链表的最后
                      pre.next = temp  # 重新连接第二段后
                  # 下一轮合并
                  sublenth *= 2
              return dummy.next

 23. 合并 K 个升序链表 - 力扣(LeetCode)

  • 依次合并

    • 调用【合并升序链表】,依次进行合并到主链表中去
    • class Solution:
          def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
              # 合并两个有序链表
              def merge(head1, head2):
                  dummy = cur = ListNode(0)
                  while head1 and head2:
                      if head1.val < head2.val:
                          cur.next = head1
                          head1 = head1.next
                      else:
                          cur.next = head2
                          head2 = head2.next
                      cur = cur.next
                  cur.next = head1 if head1 else head2
                  return dummy.next
              lenth = len(lists)
              if lenth == 0:
                  return None
              # 每遍历一个链表就合并掉
              dummyhead = ListNode(0)
              for i in range(0, len(lists)):
                  dummyhead.next = merge(dummyhead.next, lists[i])
              return dummyhead.next
  •  分治合并

    • 利用二分递归划分,再进行左右合并
    • class Solution:
          def mergeKLists(self, lists: List[ListNode]) -> ListNode:
              # 如果输入为空,直接返回空
              if not lists:
                  return 
              # 获取链表列表的长度
              n = len(lists)
              # 调用递归函数进行合并
              return self.merge(lists, 0, n-1)
          
          def merge(self, lists, left, right):
              # 当左右指针相等时,表示只有一个链表,直接返回该链表
              if left == right:
                  return lists[left]
              # 计算中间位置
              mid = left + (right - left) // 2
              # 递归地合并左半部分和右半部分的链表
              l1 = self.merge(lists, left, mid)
              l2 = self.merge(lists, mid+1, right)
              # 调用合并两个有序链表的函数
              return self.mergeTwoLists(l1, l2)
          
          def mergeTwoLists(self, l1, l2):
              # 若其中一个链表为空,则直接返回另一个链表
              if not l1:
                  return l2
              if not l2:
                  return l1
              # 比较两个链表头结点的大小,选择较小的作为新链表的头结点
              if l1.val < l2.val:
                  l1.next = self.mergeTwoLists(l1.next, l2)
                  return l1
              else:
                  l2.next = self.mergeTwoLists(l1, l2.next)
                  return l2
  •  最小堆

    • """
      假设有3个有序链表分别是:1->4->5, 1->3->4, 2->6。
      初始时,最小堆为空。我们依次将(1,0),(1,1),(2,2)加入最小堆。
      然后不断弹出最小值(1,0),(1,1),(2,2),加入到结果链表中,
      并将对应链表的下一个节点值和索引加入最小堆,直到最小堆为空。
      最终得到的合并后的链表为1->1->2->3->4->4->5->6
      """
      class Solution:
          def mergeKLists(self, lists: List[ListNode]) -> ListNode:
              import heapq
              # 创建虚拟节点
              dummy = ListNode(0)
              p = dummy
              head = []
              # 遍历链表数组
              for i in range(len(lists)):
                  if lists[i] :
                      # 将每个链表的头结点值和索引加入到最小堆中
                      heapq.heappush(head, (lists[i].val, i))
                      lists[i] = lists[i].next
              while head:
                  # 弹出最小堆中的值和对应的索引
                  val, idx = heapq.heappop(head)
                  # 创建新节点并连接到结果链表上
                  p.next = ListNode(val)
                  p = p.next
                  # 如果该链表还有剩余节点,则将下一个节点的值和索引加入到最小堆中
                  if lists[idx]:
                      heapq.heappush(head, (lists[idx].val, idx))
                      lists[idx] = lists[idx].next
              # 返回合并后的链表
              return dummy.next

146. LRU 缓存 - 力扣(LeetCode)

  • 哈希 + 双向链表

    • 借用灵神题解的图,really good
    • class Node:
          # 提高访问属性的速度,并节省内存
          __slots__ = 'prev', 'next', 'key', 'value'
          def __init__(self, key=0, value=0):
              # self.prev = None
              # self.next = None
              self.key = key
              self.value = value
      
      class LRUCache:
          def __init__(self, capacity: int):
              self.capacity = capacity
              self.dummy = Node()  # 哨兵节点
              self.dummy.prev = self.dummy
              self.dummy.next = self.dummy
              self.key_to_node = dict()
      
          def get_node(self, key: int) -> Optional[Node]:
              if key not in self.key_to_node:  # 没有这本书
                  return None
              node = self.key_to_node[key]  # 有这本书
              self.remove(node)  # 把这本书抽出来
              self.push_front(node)  # 放在最上面
              return node
      
          def get(self, key: int) -> int:
              node = self.get_node(key)
              return node.value if node else -1
      
          def put(self, key: int, value: int) -> None:
              node = self.get_node(key)
              if node:  # 有这本书
                  node.value = value  # 更新 value
                  return
              self.key_to_node[key] = node = Node(key, value)  # 新书
              self.push_front(node)  # 放在最上面
              if len(self.key_to_node) > self.capacity:  # 书太多了
                  back_node = self.dummy.prev
                  del self.key_to_node[back_node.key]
                  self.remove(back_node)  # 去掉最后一本书
      
          # 删除一个节点(抽出一本书)
          def remove(self, x: Node) -> None:
              x.prev.next = x.next
              x.next.prev = x.prev
      
          # 在链表头添加一个节点(把一本书放在最上面)
          def push_front(self, x: Node) -> None:
              x.prev = self.dummy
              x.next = self.dummy.next
              x.prev.next = x
              x.next.prev = x
      
      # Your LRUCache object will be instantiated and called as such:
      # obj = LRUCache(capacity)
      # param_1 = obj.get(key)
      # obj.put(key,value)

后言

  • 链表终于结束了!后面这几道真是难到我了,基本都是CV写出来的,还是得多沉淀啊 !休息一下,晚上干点活了,不然新学期要被老板骂骂
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值