【Leetcode算法题选编】写法、模板类(持续更新)

这一篇主要记录一些固定招式。难度不在于算法的思考,而在于写对。

模板


堆:Leetcode 23 Merge k Sorted Lists 合并K个升序链表

题目

在这里插入图片描述
在这里插入图片描述

意义

写法、模板、优先队列

随想

这个题就是个优先队列的模板题,注意优先队列的实现方式

复杂度
  • 时间
    O ( N k log ⁡ k ) \mathcal O(Nk\log k) O(Nklogk)
  • 空间
    O ( k ) \mathcal O(k) O(k)
代码
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class PQ:
    def __init__(self):
        self.array = [0]

    def comp(self, n1, n2):
        return n1.val < n2.val

    # 递归下滤
    def down(self, idx):
        left_idx = 2 * idx
        right_idx = 2 * idx + 1
        root = idx
        size = len(self.array) - 1
        if left_idx <= size and self.comp(self.array[left_idx], self.array[root]):
            root = left_idx
        if right_idx <= size and self.comp(self.array[right_idx], self.array[root]):
            root = right_idx
        if root != idx:
            self.array[idx], self.array[root] = self.array[root], self.array[idx]
            self.down(root)
    
    def build_map(self, A):
        self.array += A
        for idx in range((len(self.array) - 1) // 2, 0, -1):
            self.down(idx)
    
    def pop(self):
        res = self.array[1]
        last_node = self.array.pop(-1)
        if not self.empty():
            self.array[1] = last_node
            self.down(1)
        return res
    
    def insert(self, node):
        self.array.append(node)
        # 递归上浮
        idx = len(self.array) - 1
        root_idx = idx // 2
        while root_idx > 0 and self.comp(self.array[idx], self.array[root_idx]):
            self.array[idx], self.array[root_idx] = self.array[root_idx], self.array[idx]
            idx = root_idx
            root_idx = idx // 2
    
    def empty(self):
        return len(self.array) == 1

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        pq = PQ()
        pq.build_map([l for l in lists if l])
        
        psedo = ListNode(0)
        cur = psedo

        while not pq.empty():
            node = pq.pop()
            node_next = node.next
            if node_next:
                pq.insert(node_next)
            cur.next = node
            cur = cur.next

        return psedo.next


并查集:Leetcode 200 Number of Islands 岛屿数量

题目

在这里插入图片描述
在这里插入图片描述

意义

并查集、写法、模板

随想

这个题难度不大,这里没有用BFS、DFS的写法,采用了并查集。并查集有两个操作:find_rootjoint_two_node可以把这个题当作并查集模板
尤其注意joint_two_node要判断两个节点是否一样,否则可能陷入死循环

复杂度
  • 时间
    O ( m n ) \mathcal O(m n) O(mn)
  • 空间
    O ( m n ) \mathcal O(mn) O(mn)
代码
class Solution:
    def get_root(self, father_table, idx):
        old_idx = idx
        while father_table[idx] != -1:
            idx = father_table[idx]
        # 并查集的加速
        while old_idx != idx:
            temp = father_table[old_idx]
            father_table[old_idx] = idx
            old_idx = temp
        return idx

    # 合并两个节点
    def joint_two_node(self, father_table, n1, n2):
        r1 = self.get_root(father_table, n1)
        r2 = self.get_root(father_table, n2)
        # 这里一定要加if,否则会陷入死循环
        if (r1 != r2):
            father_table[r2] = r1

    def numIslands(self, grid: List[List[str]]) -> int:
        m = len(grid)
        if m == 0:
            return 0
        n = len(grid[0])
        father_table = [-1] * (m * n)
        for r in range(m):
            for c in range(n):
                if grid[r][c] == "0":
                    father_table[r * n + c] = -2
                    continue
                if r > 0 and grid[r - 1][c] == "1":
                    self.joint_two_node(father_table, r * n + c - n, r * n + c)
                if c > 0 and grid[r][c - 1] == "1":
                    self.joint_two_node(father_table, r * n + c - 1, r * n + c)
        result = 0
        for r in range(m):
            for c in range(n):
                if father_table[r * n + c] == -1:
                    result += 1
        return result

链表、哈希:Leetcode 146 LRU Cache LRU缓存机制

题目

在这里插入图片描述

意义

链表、哈希、写法、模板

随想

这个题吧,也不难。双向链表+Dict。Dict以key为key,以node为value.
就是写法上要注意

  • Python的链表写法. 尤其注意python中自己写函数的时候,要加self参数,很容易忘,然后疯狂报错
  • dict索引之后的那一句可能比较长,建议单独拿成变量,否则容易出bug
    node = self.node_d[key]
复杂度
  • 时间
    O ( 1 ) \mathcal O(1) O(1)
  • 空间
    O ( n ) \mathcal O(n) O(n)
代码
class DNode:
    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.pre = None
        self.nex = None

class LRUCache:

    def __init__(self, capacity: int):
        # psedo node
        self.head = DNode(None, None)
        self.tail = DNode(None, None)
        self.head.nex = self.tail
        self.tail.pre = self.head
        # dict
        self.node_d = {}
        self.remain_size = capacity

    def get(self, key: int) -> int:
        if key in self.node_d:
            node = self.node_d[key]
            self.moveToHead(node)
            return node.value
        else:
            return -1

    def put(self, key: int, value: int) -> None:
        if key in self.node_d:
            node = self.node_d[key]
            node.value = value
            self.moveToHead(node)
        else:
            node = DNode(key, value)
            self.node_d[key] = node
            self.addHead(node)
            self.remain_size -= 1
            if self.remain_size == -1:
                self.node_d.pop(self.tail.pre.key)
                self.removeNode(self.tail.pre)
                self.remain_size += 1
        
    def addHead(self, node):
        node.nex = self.head.nex
        node.pre = self.head
        node.nex.pre = node
        self.head.nex = node
    
    def removeNode(self, node):
        node.nex.pre = node.pre
        node.pre.nex = node.nex

    def moveToHead(self, node):
        self.removeNode(node)
        self.addHead(node)


# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

二叉树的前、中、后序、层次遍历,非递归、迭代器写法

意义

写法 模板

随想
  • 二叉树的非递归版本常考,这里再进一步给出迭代器的写法,从而能在迭代过程中,把除了结果以外的空间消耗降到树的高度 O ( h ) \mathcal O(h) O(h)
  • 这里后序,需要涉及到状态,0表示向右,1表示向上。状态可以看作是递归版本的指令寄存器
代码
class Solution:
	### 前序
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        def get_next(sta):
            if not sta:
                return None
            cur = sta.pop()
            if cur.right:
                sta.append(cur.right)
            if cur.left:
                sta.append(cur.left)
            return cur
        
        sta = []
        res = []
        if root:
            sta.append(root)
        while True:
            cur = get_next(sta)
            if not cur:
                break
            res.append(cur.val)
        return res
        
	### 中序
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        def add_to_stack(sta, cur):
            while cur:
                sta.append(cur)
                cur = cur.left

        def get_next(sta):
            if not sta:
                return None
            cur = sta.pop()
            add_to_stack(sta, cur.right)
            return cur

        res = []
        sta = []
        add_to_stack(sta, root)
        while True:
            cur = get_next(sta)
            if cur is None:
                break
            res.append(cur.val)
        return res

	### 后序
    def postorderTraversal(self, root: TreeNode) -> List[int]:

        def add_left(sta, cur):
            while cur: # 0 means right, 1 means up
                sta.append((cur, 0))
                cur = cur.left

        def get_next(sta):
            if not sta:
                return None
            cur, stat = sta.pop()
            if stat == 0:
                while True:
                    if not cur.right:
                        break
                    sta.append((cur, 1))
                    add_left(sta, cur.right)
                    cur, stat = sta.pop()     
            return cur

        sta = []
        res = []
        add_left(sta, root)
        while True:
            cur = get_next(sta)
            if not cur:
                break
            res.append(cur.val)
        return res

	### 层次
	def levelOrder(self, root: TreeNode) -> List[List[int]]:
        from queue import Queue
        
        def get_next(que):
            if que.empty():
                return None, None
            cur, height = que.get()
            if cur.left:
                que.put((cur.left, height + 1))
            if cur.right:
                que.put((cur.right, height + 1))
            return cur, height

        res = []
        que = Queue()
        cur_height = 1
        cur_res = []
        if root:
            que.put((root, 1))
        while True:
            cur, height = get_next(que)
            if not cur:
                break
            if height != cur_height:
                cur_height = height
                res.append(cur_res)
                cur_res = []
            cur_res.append(cur.val)
        if cur_res:
            res.append(cur_res)
        return res

写法


使用成员变量:Leetcode 236 Lowest Common Ancestor of a Binary Tree 二叉树的最近公共祖先

题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

意义

写法、DFS

随想

这个题能写对,但是想写的简洁还是不容易
最简洁的写法是一次DFS
判断左右两个分支是不是都有p或q,
或者是左右两个分支一个有,但是当前节点是p或q

复杂度
  • 时间
    O ( N ) \mathcal O(N) O(N)
  • 空间
    O ( N ) \mathcal O(N) O(N)
代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def dfs(self, node, p, q):
        if not node:
            return False
        
        left_res = self.dfs(node.left, p, q)
        right_res = self.dfs(node.right, p, q)

        if ((node is p or node is q) and (left_res or right_res)) or (left_res and right_res):
            self.res = node
        
        return left_res or right_res or node is p or node is q      
        
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        self.dfs(root, p, q)
        return self.res

使用成员变量:Leetcode 124 Binary Tree Maximum Path Sum 二叉树中的最大路径和

题目

在这里插入图片描述
在这里插入图片描述

意义

写法、递归

随想

这个题主要在于写法。其实是一个DFS,造一个类内属性self.maxSum = float("-inf")就方便了许多。如果不用类内属性,而当作返回值的话,很容易写的冗长

复杂度
  • 时间
    O ( N ) \mathcal O(N) O(N)
  • 空间
    O ( N ) \mathcal O(N) O(N)
代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def __init__(self):
        self.maxSum = float("-inf")

    def maxSinglePath(self, node):
        if not node:
            return 0
        leftSum = max(self.maxSinglePath(node.left), 0)
        rightSum = max(self.maxSinglePath(node.right), 0)
        self.maxSum = max(self.maxSum, leftSum + rightSum + node.val)
        return node.val + max(leftSum, rightSum)

    def maxPathSum(self, root: TreeNode) -> int:
        self.maxSinglePath(root)
        return self.maxSum

用辅助函数简化写法:Leetcode 25 Reverse Nodes in k-Group K 个一组翻转链表

题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

意义

写法、链表、模拟

随想

这个题属于模拟。**难在写对。**中间有很多细节的地方。可以把翻转子链表单独拿出来写成一个函数,会清晰很多

复杂度
  • 时间
    O ( n ) \mathcal O(n) O(n)
  • 空间
    O ( 1 ) \mathcal O(1) O(1)
代码
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    # return new head and new tail
    def reversePart(self, head, tail):
        cur, prev = head, tail.next
        while prev != tail:
            n = cur.next
            cur.next = prev
            prev = cur
            cur = n
        return tail, head

    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        psedo = ListNode(0)
        psedo.next = head
        head = psedo
        while True:
            right = head
            for _ in range(k):
                if not right:
                    break
                right = right.next

            if not right:
                return psedo.next

            self.reversePart(head.next, right)
            temp = head.next
            head.next = right
            head = temp

用四个局部变量写计算器:Leetcode 772 Basic Calculator III 基本计算器III

题目

在这里插入图片描述

意义

写法、指针、自动机

随想
  • 这个题属于模拟。**难在写对。**中间有很多细节的地方。用四个局部变量可以简化整个过程,一个表示当前的数字;一个表示当前的乘除项;一个表示结果;一个表示上次的运算符,以对当前数值运算
  • 遇到括号,则化为子问题
  • 参考代码启发自博客:https://blog.csdn.net/m0_46202073/article/details/114729707,这种写法代码量很少
复杂度
  • 时间
    O ( n ) \mathcal O(n) O(n)
  • 空间
    O ( n ) \mathcal O(n) O(n),最坏情况很多括号
代码
class Solution:
    def calculate_item(self):
        res = 0
        tmp = 0
        num = 0
        opt = '+'
        while self.ptr < len(self.s):
            c = self.s[self.ptr]
            self.ptr += 1
            if c == '(':
                num = self.calculate_item()
            elif c not in {')', '+', '-', '*', '/'}:
                num = num * 10 + ord(c) - ord('0')
            
            if c in {')', '+', '-', '*', '/'} or self.ptr == len(self.s):
                if opt == '+':
                    tmp += num
                elif opt == '-':
                    tmp -= num
                elif opt == '*':
                    tmp *= num
                else:  # correct division
                    if tmp * num < 0:
                        tmp = -(-tmp // num)
                    else:
                        tmp //= num
                opt = c
                num = 0
                if c in {')', '+', '-'} or self.ptr == len(self.s):
                    res += tmp
                    tmp = 0
                    if c == ')' or self.ptr == len(self.s):
                        return res
                

    def calculate(self, s: str) -> int:
        self.ptr = 0
        self.s = s
        return self.calculate_item()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值