├── readme ├── 15-动态规划 ├── EASY-杨辉三角.py ├── EASY-爬楼梯.py ├── 完全平方数.py ├── 最长有效括号.py ├── 零钱兑换.py ├── 分割等和子集.py ├── 乘积最大子数组.py ├── 最长递增子序列.py ├── 单词拆分.py └── 打家劫舍.py ├── 17-技巧 ├── 多数元素.py ├── 只出现一次的数字.py ├── 寻找重复数.py └── 颜色分类.py ├── 13-堆 ├── 数组中的第K个最大元素.py ├── 前K个高频元素.py └── 数据流的中位数.py ├── 6-矩阵 ├── 螺旋矩阵.py ├── 旋转图像.py ├── 搜索二维矩阵II.py └── 矩阵置零.py ├── 1-哈希 ├── 字母异位词分组.py ├── 最长连续序列.py └── EASY-两数之和.py ├── 2-双指针 ├── 盛最多水的容器.py ├── EASY-移动零.py ├── 接雨水.py └── 三数之和.py ├── 16-多维动态规划 ├── 不同路径.py ├── 最小路径和.py ├── 最长公共子序列.py ├── 编译距离.py └── 最长回文子串.py ├── 8-二叉树 ├── EASY-翻转二叉树.py ├── EASY-对称二叉树.py ├── 验证二叉搜索树.py ├── 二叉树的右视图.py ├── EASY-二叉树的最大深度.py ├── EASY-二叉树的中序遍历.py ├── 二叉树的最近公共祖先.py ├── 从前序与中序遍历序列构造二叉树.py ├── EASY-将有序数组转成平衡二叉搜索树.py ├── 二叉树的层序遍历.py ├── 二叉树中的最大路径和.py ├── EASY-二叉树的直径.py ├── 二叉搜索树中的第K小的元素.py ├── 路径总和III.py └── 二叉树展开为链表.py ├── 12-栈 ├── EASY-有效的括号.py ├── 每日温度.py └── 字符串解码.py ├── 10-回溯 ├── 全排列.py ├── 电话号码的字母组合.py ├── 子集.py ├── 分割回文串.py ├── 括号生成.py ├── N皇后.py ├── 单词搜索.py └── 组合总和.py ├── 7-链表 ├── EASY-回文链表.py ├── EASY-相交链表.py ├── EASY-反转链表.py ├── EASY-环形链表.py ├── EASY-合并两个有序链表.py ├── 删除链表的倒数第N个结点.py ├── 环形链表II.py ├── 两两交换链表中的节点.py ├── K个一组翻转列表.py ├── 排序链表.py ├── 两数相加.py ├── 随机链表的复制.py ├── 合并k个升序链表.py └── LRU缓存.py ├── 5-普通数组 ├── 最大子数组和.py ├── 除自身以外数组的乘积.py ├── 合并区间.py ├── 缺失的第一个正数.py └── 轮转数组.py ├── 14-贪心算法 ├── EASY-买卖股票的最佳时机.py ├── 跳跃游戏.py ├── 跳跃游戏ll.py └── 划分字母区间.py ├── 11-二分查找 ├── EASY-搜索插入位置.py ├── 搜索二维矩阵.py ├── 寻找旋转排序数组中的最小值.py ├── 在排序数组中查找元素的第一个和最后一个位置.py └── 搜索旋转排序数组.py ├── 4-子串 ├── 和为k的子数组.py ├── 滑动窗口最大值.py └── 最小覆盖子串.py ├── 3-滑动窗口 ├── 无重复字符的最长子串.py └── 找到字符串中所有字母异位词.py ├── 9-图论 ├── 腐烂的橘子.py ├── 课程表.py └── 岛屿数量.py └── readme.txt /readme: -------------------------------------------------------------------------------- 1 | 做题思路: 2 | 3 | 1.直接看题解 4 | 2.本地快速实现 5 | 3.写一遍 6 | 7 | 8 | 106版本: 9 | 先过一遍,整体过一遍,不细看 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /15-动态规划/EASY-杨辉三角.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 3 | """ 4 | from typing import List 5 | 6 | 7 | class Solution: 8 | def generate(self, numRows: int) -> List[List[int]]: 9 | c = [[1] * (i + 1) for i in range(numRows)] 10 | 11 | for i in range(2, numRows): 12 | for j in range(1, i): 13 | c[i][j] = c[i - 1][j - 1] + c[i - 1][j] 14 | 15 | return c 16 | 17 | 18 | if __name__ == '__main__': 19 | print(Solution().generate(5)) 20 | -------------------------------------------------------------------------------- /17-技巧/多数元素.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 3 | 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 4 | 5 | 示例 1: 6 | 输入:nums = [3,2,3] 7 | 输出:3 8 | 9 | 示例 2: 10 | 输入:nums = [2,2,1,1,1,2,2] 11 | 输出:2 12 | """ 13 | from typing import List 14 | 15 | 16 | class Solution: 17 | def majorityElement(self, nums: List[int]) -> int: 18 | nums.sort() 19 | return nums[len(nums) // 2] 20 | 21 | 22 | if __name__ == '__main__': 23 | nums = [2, 2, 1, 1, 1, 2, 2] 24 | print(Solution().majorityElement(nums)) 25 | -------------------------------------------------------------------------------- /13-堆/数组中的第K个最大元素.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 3 | 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 4 | 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 5 | 6 | 示例 1: 7 | 输入: [3,2,1,5,6,4], k = 2 8 | 输出: 5 9 | 10 | 示例 2: 11 | 输入: [3,2,3,1,2,4,5,5,6], k = 4 12 | 输出: 4 13 | """ 14 | from typing import List 15 | 16 | 17 | class Solution: 18 | def findKthLargest(self, nums: List[int], k: int) -> int: 19 | return sorted(nums)[len(nums) - k] 20 | 21 | 22 | if __name__ == '__main__': 23 | nums = [3, 2, 1, 5, 6, 4] 24 | k = 2 25 | print(Solution().findKthLargest(nums, k)) 26 | -------------------------------------------------------------------------------- /6-矩阵/螺旋矩阵.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 3 | """ 4 | from typing import List 5 | 6 | 7 | # https://leetcode.cn/problems/spiral-matrix/solutions/2057738/python-zip-by-zhuzhzzz-tk7a 8 | class Solution: 9 | def spiralOrder(self, matrix: List[List[int]]) -> List[int]: 10 | result = [] 11 | while matrix: 12 | result += matrix.pop(0) # 取矩阵第一行并删除 13 | matrix = list(zip(*matrix))[::-1] # 旋转矩阵 14 | return result 15 | 16 | 17 | if __name__ == "__main__": 18 | Solution().spiralOrder([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) 19 | -------------------------------------------------------------------------------- /17-技巧/只出现一次的数字.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 3 | 你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。 4 | 5 | 示例 1 : 6 | 输入:nums = [2,2,1] 7 | 输出:1 8 | 9 | 示例 2 : 10 | 输入:nums = [4,1,2,1,2] 11 | 输出:4 12 | 13 | 示例 3 : 14 | 输入:nums = [1] 15 | 输出:1 16 | """ 17 | from typing import List 18 | from functools import reduce 19 | 20 | xor = lambda x, y: x ^ y 21 | 22 | 23 | class Solution: 24 | def singleNumber(self, nums: List[int]) -> int: 25 | return reduce(xor, nums) 26 | 27 | 28 | if __name__ == '__main__': 29 | print(Solution().singleNumber([2, 2, 1])) 30 | -------------------------------------------------------------------------------- /13-堆/前K个高频元素.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 3 | 4 | 示例 1: 5 | 输入: nums = [1,1,1,2,2,3], k = 2 6 | 输出: [1,2] 7 | 8 | 示例 2: 9 | 输入: nums = [1], k = 1 10 | 输出: [1] 11 | """ 12 | import collections 13 | from typing import List 14 | 15 | 16 | class Solution: 17 | def topKFrequent(self, nums: List[int], k: int) -> List[int]: 18 | counts = collections.Counter(nums) 19 | return [item[0] for item in counts.most_common(k)] 20 | 21 | 22 | if __name__ == '__main__': 23 | nums = [1, 1, 1, 2, 2, 3] 24 | k = 2 25 | print(Solution().topKFrequent(nums, k)) 26 | -------------------------------------------------------------------------------- /1-哈希/字母异位词分组.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 3 | 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 4 | 5 | 示例 1: 6 | 输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 7 | 输出: [["bat"],["nat","tan"],["ate","eat","tea"]] 8 | """ 9 | from typing import List 10 | from collections import defaultdict 11 | 12 | 13 | class Solution: 14 | def groupAnagrams(self, strs: List[str]) -> List[List[str]]: 15 | d = defaultdict(list) 16 | for s in strs: 17 | d["".join(sorted(s))].append(s) 18 | return list(d.values()) 19 | 20 | 21 | if __name__ == '__main__': 22 | print(Solution().groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) 23 | -------------------------------------------------------------------------------- /15-动态规划/EASY-爬楼梯.py: -------------------------------------------------------------------------------- 1 | """ 2 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 3 | 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 4 | 5 | 示例 1: 6 | 输入:n = 2 7 | 输出:2 8 | 解释:有两种方法可以爬到楼顶。 9 | 1. 1 阶 + 1 阶 10 | 2. 2 阶 11 | 12 | 示例 2: 13 | 输入:n = 3 14 | 输出:3 15 | 解释:有三种方法可以爬到楼顶。 16 | 1. 1 阶 + 1 阶 + 1 阶 17 | 2. 1 阶 + 2 阶 18 | 3. 2 阶 + 1 阶 19 | 20 | """ 21 | from functools import cache 22 | 23 | 24 | class Solution: 25 | def climbStairs(self, n: int) -> int: 26 | @cache 27 | def dfs(i): 28 | if i <= 1: 29 | return 1 30 | return dfs(i - 1) + dfs(i - 2) 31 | 32 | return dfs(n) 33 | 34 | 35 | if __name__ == '__main__': 36 | print(Solution().climbStairs(n=2)) 37 | -------------------------------------------------------------------------------- /2-双指针/盛最多水的容器.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 3 | 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 4 | 返回容器可以储存的最大水量。 5 | 6 | 说明:你不能倾斜容器。 7 | """ 8 | from typing import List 9 | 10 | 11 | class Solution: 12 | def maxArea(self, height: List[int]) -> int: 13 | i, j, res = 0, len(height) - 1, 0 14 | while i < j: 15 | if height[i] < height[j]: 16 | res = max(res, height[i] * (j - i)) 17 | i += 1 18 | else: 19 | res = max(res, height[j] * (j - i)) 20 | j -= 1 21 | return res 22 | 23 | 24 | if __name__ == '__main__': 25 | print(Solution().maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7])) 26 | -------------------------------------------------------------------------------- /16-多维动态规划/不同路径.py: -------------------------------------------------------------------------------- 1 | """ 2 | 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 3 | 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 4 | 问总共有多少条不同的路径? 5 | 6 | """ 7 | 8 | 9 | class Solution: 10 | def uniquePaths(self, m: int, n: int) -> int: 11 | # 初始化为1,避免多余的赋值,外层不使用*,否则浅拷贝内层可能会在赋值时候出现问题 12 | dp = [[1] * n for _ in range(m)] 13 | for i in range(1, m): 14 | for j in range(1, n): 15 | dp[i][j] = dp[i][j - 1] + dp[i - 1][j] 16 | 17 | return dp[-1][-1] 18 | 19 | 20 | # https: // leetcode.cn / problems / unique - paths / solutions / 55536 / dong - tai - gui - hua - duo - yu - yan - by - wf0312 / 21 | 22 | if __name__ == '__main__': 23 | print(Solution().uniquePaths(3, 7)) 24 | -------------------------------------------------------------------------------- /17-技巧/寻找重复数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。 3 | 假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。 4 | 你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。 5 | 6 | 示例 1: 7 | 输入:nums = [1,3,4,2,2] 8 | 输出:2 9 | 10 | 示例 2: 11 | 输入:nums = [3,1,3,4,2] 12 | 输出:3 13 | 14 | 示例 3 : 15 | 输入:nums = [3,3,3,3,3] 16 | 输出:3 17 | 18 | """ 19 | from typing import List 20 | 21 | 22 | class Solution: 23 | def findDuplicate(self, nums: List[int]) -> int: 24 | hmeap = set() 25 | for num in nums: 26 | if num in hmeap: 27 | return num 28 | hmeap.add(num) 29 | return -1 30 | 31 | 32 | if __name__ == '__main__': 33 | s = Solution() 34 | print(s.findDuplicate([1, 3, 4, 2, 2])) 35 | -------------------------------------------------------------------------------- /8-二叉树/EASY-翻转二叉树.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 3 | """ 4 | 5 | from typing import Optional 6 | 7 | 8 | # Definition for a binary tree node. 9 | class TreeNode: 10 | def __init__(self, val=0, left=None, right=None): 11 | self.val = val 12 | self.left = left 13 | self.right = right 14 | 15 | 16 | # https://leetcode.cn/problems/invert-binary-tree/solutions/2713610/shi-pin-shen-ru-li-jie-di-gui-pythonjava-zhqh 17 | class Solution: 18 | def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: 19 | if root is None: 20 | return 21 | 22 | left = self.invertTree(root.left) 23 | right = self.invertTree(root.right) 24 | root.left = right 25 | root.right = left 26 | return root 27 | -------------------------------------------------------------------------------- /6-矩阵/旋转图像.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 3 | """ 4 | from typing import List 5 | 6 | 7 | # https://leetcode.cn/problems/rotate-image/solutions/1228078/48-xuan-zhuan-tu-xiang-fu-zhu-ju-zhen-yu-jobi 8 | class Solution: 9 | def rotate(self, matrix: List[List[int]]) -> None: 10 | n = len(matrix) 11 | for i in range(n // 2): 12 | for j in range((n + 1) // 2): 13 | tmp = matrix[i][j] 14 | matrix[i][j] = matrix[n - 1 - j][i] 15 | matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j] 16 | matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i] 17 | matrix[j][n - 1 - i] = tmp 18 | 19 | 20 | if __name__ == "__main__": 21 | Solution().rotate([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) 22 | -------------------------------------------------------------------------------- /8-二叉树/EASY-对称二叉树.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个二叉树的根节点 root , 检查它是否轴对称。 3 | """ 4 | 5 | from typing import Optional 6 | 7 | 8 | # https://leetcode.cn/problems/symmetric-tree/solutions/2015063/ru-he-ling-huo-yun-yong-di-gui-lai-kan-s-6dq5 9 | 10 | # Definition for a binary tree node. 11 | class TreeNode: 12 | def __init__(self, val=0, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | class Solution: 19 | def isSymmetric(self, root: Optional[TreeNode]) -> bool: 20 | return self.treeSame(root.left, root.right) 21 | 22 | def treeSame(self, p, q): 23 | if p is None or q is None: 24 | return p is q 25 | 26 | return p.val == q.val and self.treeSame(p.left, q.right) and self.treeSame(p.right, q.left) 27 | -------------------------------------------------------------------------------- /2-双指针/EASY-移动零.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 3 | 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 4 | 5 | 示例 1: 6 | 输入: nums = [0,1,0,3,12] 7 | 输出: [1,3,12,0,0] 8 | 9 | 示例 2: 10 | 输入: nums = [0] 11 | 输出: [0] 12 | """ 13 | from typing import List 14 | 15 | 16 | class Solution: 17 | def moveZeroes(self, nums: List[int]) -> None: 18 | """ 19 | Do not return anything, modify nums in-place instead. 20 | """ 21 | write = 0 22 | for index, i in enumerate(nums): 23 | if i != 0: 24 | nums[write] = i 25 | write += 1 26 | 27 | while (write < len(nums)): 28 | nums[write] = 0 29 | write += 1 30 | 31 | 32 | if __name__ == '__main__': 33 | nums = [0, 1, 0, 3, 12] 34 | Solution().moveZeroes(nums) 35 | -------------------------------------------------------------------------------- /12-栈/EASY-有效的括号.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。 3 | 有效字符串需满足: 4 | 左括号必须用相同类型的右括号闭合。 5 | 左括号必须以正确的顺序闭合。 6 | 每个右括号都有一个对应的相同类型的左括号。 7 | 8 | 示例 1: 9 | 输入:s = "()" 10 | 输出:true 11 | 12 | 示例 2: 13 | 输入:s = "()[]{}" 14 | 输出:true 15 | 16 | 示例 3: 17 | 输入:s = "(]" 18 | 输出:false 19 | 20 | 示例 4: 21 | 输入:s = "([])" 22 | 输出:true 23 | """ 24 | 25 | 26 | class Solution: 27 | def isValid(self, s: str) -> bool: 28 | dic = {"(": ")", "{": "}", "[": "]", "?": "?"} 29 | stack = ["?"] 30 | 31 | for c in s: 32 | if c in dic: 33 | stack.append(c) 34 | elif dic[stack.pop()] != c: 35 | return False 36 | return len(stack) == 1 37 | 38 | 39 | if __name__ == '__main__': 40 | s = "([])" 41 | print(Solution().isValid(s)) 42 | -------------------------------------------------------------------------------- /10-回溯/全排列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 3 | 4 | 示例 1: 5 | 输入:nums = [1,2,3] 6 | 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 7 | 8 | 示例 2: 9 | 输入:nums = [0,1] 10 | 输出:[[0,1],[1,0]] 11 | 12 | 示例 3: 13 | 输入:nums = [1] 14 | 输出:[[1]] 15 | 16 | """ 17 | from typing import List 18 | 19 | 20 | class Solution: 21 | def permute(self, nums: List[int]) -> List[List[int]]: 22 | n = len(nums) 23 | path = [0] * n 24 | ans = [] 25 | 26 | def dfs(i, s): 27 | if i == n: 28 | ans.append(path.copy()) 29 | for x in s: 30 | path[i] = x 31 | dfs(i + 1, s - {x}) 32 | 33 | dfs(0, set(nums)) 34 | return ans 35 | 36 | 37 | if __name__ == '__main__': 38 | print(Solution().permute([1, 2, 3])) 39 | -------------------------------------------------------------------------------- /15-动态规划/完全平方数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。 3 | 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。 4 | 5 | 示例 1: 6 | 输入:n = 12 7 | 输出:3 8 | 解释:12 = 4 + 4 + 4 9 | 10 | 示例 2: 11 | 输入:n = 13 12 | 输出:2 13 | 解释:13 = 4 + 9 14 | 15 | """ 16 | from cmath import inf 17 | from functools import cache 18 | from math import isqrt 19 | 20 | 21 | @cache 22 | def dfs(i, j): 23 | if i == 0: 24 | return inf if j else 0 25 | 26 | if j < i * i: 27 | return dfs(i - 1, j) 28 | return min(dfs(i - 1, j), dfs(i, j - i * i) + 1) 29 | 30 | 31 | class Solution: 32 | def numSquares(self, n: int) -> int: 33 | return dfs(isqrt(n), n) 34 | 35 | 36 | if __name__ == '__main__': 37 | n = 12 38 | print(Solution().numSquares(n)) 39 | 40 | # 状态转移方程:dp[i] = min(dp[i], dp[i - j * j] + 1) 41 | -------------------------------------------------------------------------------- /7-链表/EASY-回文链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个单链表的头节点 head ,请你判断该链表是否为 3 | 回文链表。如果是,返回 true ;否则,返回 false 。 4 | 5 | 输入:head = [1,2,2,1] 6 | 输出:true 7 | """ 8 | 9 | 10 | class ListNode: 11 | def __init__(self, val=0, next=None): 12 | self.val = val 13 | self.next = next 14 | 15 | 16 | class Solution: 17 | def isPalindrome(self, head: ListNode) -> bool: 18 | vals = [] 19 | current_node = head 20 | while current_node is not None: 21 | vals.append(current_node.val) 22 | current_node = current_node.next 23 | return vals == vals[::-1] 24 | 25 | 26 | if __name__ == '__main__': 27 | head = ListNode(1) 28 | head.next = ListNode(2) 29 | head.next.next = ListNode(2) 30 | head.next.next.next = ListNode(1) 31 | result = Solution().isPalindrome(head) 32 | print(result) 33 | -------------------------------------------------------------------------------- /10-回溯/电话号码的字母组合.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 3 | 4 | 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 5 | """ 6 | from typing import List 7 | 8 | MAPPING = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] 9 | 10 | 11 | class Solution: 12 | def letterCombinations(self, digits: str) -> List[str]: 13 | n = len(digits) 14 | path = [""] * n 15 | ans = [] 16 | if n == 0: 17 | return [] 18 | 19 | def dfs(i): 20 | if i == n: 21 | ans.append("".join(path.copy())) 22 | return 23 | 24 | for x in MAPPING[int(digits[i])]: 25 | path[i] = x 26 | dfs(i + 1) 27 | 28 | dfs(0) 29 | return ans 30 | 31 | if __name__ == '__main__': 32 | print(Solution().letterCombinations("23")) -------------------------------------------------------------------------------- /10-回溯/子集.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的 3 | 子集(幂集)。 4 | 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 5 | 6 | 示例 1: 7 | 输入:nums = [1,2,3] 8 | 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] 9 | 10 | 示例 2: 11 | 输入:nums = [0] 12 | 输出:[[],[0]] 13 | """ 14 | 15 | from typing import List 16 | 17 | 18 | class Solution: 19 | def subsets(self, nums: List[int]) -> List[List[int]]: 20 | path = [] 21 | ans = [] 22 | n = len(nums) 23 | 24 | def dfs(i): 25 | if i == n: 26 | ans.append(path.copy()) 27 | return 28 | 29 | dfs(i + 1) 30 | 31 | path.append(nums[i]) 32 | dfs(i + 1) 33 | path.pop() 34 | 35 | dfs(0) 36 | return ans 37 | 38 | 39 | if __name__ == '__main__': 40 | nums = [1, 2, 3] 41 | print(Solution().subsets(nums)) 42 | -------------------------------------------------------------------------------- /5-普通数组/最大子数组和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 3 | 子数组是数组中的一个连续部分。 4 | 5 | 示例 1: 6 | 输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 7 | 输出:6 8 | 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。 9 | 10 | 示例 2: 11 | 输入:nums = [1] 12 | 输出:1 13 | 14 | 示例 3: 15 | 输入:nums = [5,4,-1,7,8] 16 | 输出:23 17 | """ 18 | 19 | from typing import List 20 | from math import inf 21 | 22 | 23 | class Solution: 24 | def maxSubArray(self, nums: List[int]) -> int: 25 | ans = -inf 26 | min_pre_sum = pre_sum = 0 27 | for x in nums: 28 | pre_sum += x # 当前的前缀和 29 | ans = max(ans, pre_sum - min_pre_sum) # 减去前缀和的最小值 30 | min_pre_sum = min(min_pre_sum, pre_sum) # 维护前缀和的最小值 31 | return ans 32 | 33 | 34 | if __name__ == "__main__": 35 | Solution().maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4]) 36 | # 当前前缀和-最小前缀和的最小值 37 | -------------------------------------------------------------------------------- /5-普通数组/除自身以外数组的乘积.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 3 | 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 4 | 请 不要使用除法,且在 O(n) 时间复杂度内完成此题。 5 | 6 | 示例 1: 7 | 输入: nums = [1,2,3,4] 8 | 输出: [24,12,8,6] 9 | 10 | 示例 2: 11 | 输入: nums = [-1,1,0,-3,3] 12 | 输出: [0,0,9,0,0] 13 | """ 14 | 15 | from typing import List 16 | 17 | 18 | class Solution: 19 | def productExceptSelf(self, nums: List[int]) -> List[int]: 20 | n = len(nums) 21 | pre = [1] * n 22 | for i in range(1, n): 23 | pre[i] = pre[i - 1] * nums[i - 1] 24 | 25 | suf = [1] * n 26 | for i in range(n - 2, -1, -1): 27 | suf[i] = suf[i + 1] * nums[i + 1] 28 | 29 | return [p * s for p, s in zip(pre, suf)] 30 | 31 | 32 | if __name__ == "__main__": 33 | Solution().productExceptSelf([1, 2, 3, 4]) 34 | -------------------------------------------------------------------------------- /14-贪心算法/EASY-买卖股票的最佳时机.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 3 | 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 4 | 返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。 5 | 6 | 示例 1: 7 | 输入:[7,1,5,3,6,4] 8 | 输出:5 9 | 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 10 | 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 11 | 12 | 示例 2: 13 | 输入:prices = [7,6,4,3,1] 14 | 输出:0 15 | 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。 16 | """ 17 | from typing import List 18 | 19 | 20 | class Solution: 21 | def maxProfit(self, prices: List[int]) -> int: 22 | min_price = prices[0] 23 | ans = 0 24 | 25 | for p in prices: 26 | ans = max(ans, p - min_price) 27 | min_price = min(min_price, p) 28 | return ans 29 | 30 | 31 | if __name__ == '__main__': 32 | print(Solution().maxProfit([7, 1, 5, 3, 6, 4])) 33 | -------------------------------------------------------------------------------- /6-矩阵/搜索二维矩阵II.py: -------------------------------------------------------------------------------- 1 | """ 2 | 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 3 | 4 | 每行的元素从左到右升序排列。 5 | 每列的元素从上到下升序排列。 6 | 7 | """ 8 | from typing import List 9 | 10 | 11 | class Solution: 12 | def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: 13 | m, n = len(matrix), len(matrix[0]) 14 | i, j = 0, n - 1 # 从右上角开始 15 | while i < m and j >= 0: # 还有剩余元素 16 | if matrix[i][j] == target: 17 | return True # 找到 target 18 | if matrix[i][j] < target: 19 | i += 1 # 这一行剩余元素全部小于 target,排除 20 | else: 21 | j -= 1 # 这一列剩余元素全部大于 target,排除 22 | return False 23 | 24 | 25 | if __name__ == "__main__": 26 | Solution().searchMatrix( 27 | [[1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30]], 5) 28 | -------------------------------------------------------------------------------- /11-二分查找/EASY-搜索插入位置.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 3 | 请必须使用时间复杂度为 O(log n) 的算法。 4 | 5 | 示例 1: 6 | 输入: nums = [1,3,5,6], target = 5 7 | 输出: 2 8 | 9 | 示例 2: 10 | 输入: nums = [1,3,5,6], target = 2 11 | 输出: 1 12 | 13 | 示例 3: 14 | 输入: nums = [1,3,5,6], target = 7 15 | 输出: 4 16 | """ 17 | 18 | from typing import List 19 | 20 | 21 | class Solution: 22 | def searchInsert(self, nums: List[int], target: int) -> int: 23 | left = 0 24 | right = len(nums) - 1 25 | 26 | while left <= right: 27 | mid = (left + right) // 2 28 | if nums[mid] < target: 29 | left = mid + 1 30 | else: 31 | right = mid - 1 32 | return left 33 | 34 | 35 | if __name__ == '__main__': 36 | nums = [1, 3, 5, 6] 37 | target = 5 38 | print(Solution().searchInsert(nums, target)) 39 | -------------------------------------------------------------------------------- /10-回溯/分割回文串.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 3 | 回文串。返回 s 所有可能的分割方案。 4 | 5 | 示例 1: 6 | 输入:s = "aab" 7 | 输出:[["a","a","b"],["aa","b"]] 8 | 9 | 示例 2: 10 | 输入:s = "a" 11 | 输出:[["a"]] 12 | """ 13 | 14 | from typing import List 15 | 16 | 17 | class Solution: 18 | def partition(self, s: str) -> List[List[str]]: 19 | ans = [] 20 | path = [] 21 | n = len(s) 22 | 23 | def dfs(i): 24 | if i == n: 25 | ans.append(path.copy()) 26 | return 27 | 28 | for j in range(i, n): 29 | t = s[i:j + 1] 30 | if t == t[::-1]: 31 | path.append(t) 32 | dfs(j + 1) 33 | path.pop() 34 | 35 | dfs(0) 36 | return ans 37 | 38 | 39 | if __name__ == '__main__': 40 | s = "aab" 41 | print(Solution().partition(s)) 42 | -------------------------------------------------------------------------------- /10-回溯/括号生成.py: -------------------------------------------------------------------------------- 1 | """ 2 | 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 3 | 4 | 示例 1: 5 | 输入:n = 3 6 | 输出:["((()))","(()())","(())()","()(())","()()()"] 7 | 8 | 示例 2: 9 | 输入:n = 1 10 | 输出:["()"] 11 | 12 | """ 13 | from typing import List 14 | 15 | 16 | class Solution: 17 | def generateParenthesis(self, n: int) -> List[str]: 18 | m = 2 * n 19 | path = [""] * m 20 | ans = [] 21 | 22 | def dfs(i, left): 23 | if i == m: 24 | ans.append("".join(path.copy())) 25 | return 26 | 27 | if left < n: 28 | path[i] = "(" 29 | dfs(i + 1, left + 1) 30 | if i - left < left: 31 | path[i] = ")" 32 | dfs(i + 1, left) 33 | 34 | dfs(0, 0) 35 | return ans 36 | 37 | 38 | if __name__ == '__main__': 39 | print(Solution().generateParenthesis(3)) 40 | -------------------------------------------------------------------------------- /8-二叉树/验证二叉搜索树.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 3 | 4 | 有效 二叉搜索树定义如下: 5 | 6 | 节点的左 7 | 子树 8 | 只包含 小于 当前节点的数。 9 | 节点的右子树只包含 大于 当前节点的数。 10 | 所有左子树和右子树自身必须也是二叉搜索树。 11 | """ 12 | 13 | from math import inf 14 | from typing import Optional 15 | 16 | 17 | # Definition for a binary tree node. 18 | class TreeNode: 19 | def __init__(self, val=0, left=None, right=None): 20 | self.val = val 21 | self.left = left 22 | self.right = right 23 | 24 | 25 | # https://leetcode.cn/problems/validate-binary-search-tree/solutions/2020306/qian-xu-zhong-xu-hou-xu-san-chong-fang-f-yxvh 26 | class Solution: 27 | def isValidBST(self, root: Optional[TreeNode], left=-inf, right=inf) -> bool: 28 | if root is None: 29 | return True 30 | x = root.val 31 | return left < x < right and self.isValidBST(root.left, left, x) and self.isValidBST(root.right, x, right) 32 | -------------------------------------------------------------------------------- /15-动态规划/最长有效括号.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号 3 | 子串的长度。 4 | 5 | 示例 1: 6 | 输入:s = "(()" 7 | 输出:2 8 | 解释:最长有效括号子串是 "()" 9 | 10 | 示例 2: 11 | 输入:s = ")()())" 12 | 输出:4 13 | 解释:最长有效括号子串是 "()()" 14 | 15 | 示例 3: 16 | 输入:s = "" 17 | 输出:0 18 | """ 19 | 20 | 21 | class Solution: 22 | def longestValidParentheses(self, s: str) -> int: 23 | stack = [] 24 | res = 0 25 | for i in range(len(s)): 26 | if not stack or s[i] == '(' or s[stack[-1]] == ')': 27 | stack.append(i) 28 | else: 29 | stack.pop() 30 | res = max(res, i - (stack[-1] if stack else - 1)) 31 | return res 32 | 33 | 34 | # https://leetcode.cn/problems/longest-valid-parentheses/solutions/762279/32-zui-chang-you-xiao-gua-hao-fu-zhu-zha-1cqq/ 35 | 36 | 37 | if __name__ == "__main__": 38 | print(Solution().longestValidParentheses("(()")) 39 | -------------------------------------------------------------------------------- /8-二叉树/二叉树的右视图.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 3 | 4 | """ 5 | 6 | # https://leetcode.cn/problems/binary-tree-right-side-view/solutions/2015061/ru-he-ling-huo-yun-yong-di-gui-lai-kan-s-r1nc 7 | 8 | from typing import Optional,List 9 | 10 | # Definition for a binary tree node. 11 | class TreeNode: 12 | def __init__(self, val=0, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | class Solution: 18 | def rightSideView(self, root: Optional[TreeNode]) -> List[int]: 19 | ans = [] 20 | 21 | def dfs(root, depth): 22 | if root is None: 23 | return 24 | if depth == len(ans): 25 | ans.append(root.val) 26 | dfs(root.right, depth + 1) 27 | dfs(root.left, depth + 1) 28 | 29 | dfs(root, 0) 30 | return ans 31 | 32 | -------------------------------------------------------------------------------- /2-双指针/接雨水.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 3 | """ 4 | 5 | from typing import List 6 | 7 | 8 | # https://leetcode.cn/problems/trapping-rain-water/solutions/1974340/zuo-liao-nbian-huan-bu-hui-yi-ge-shi-pin-ukwm 9 | class Solution: 10 | def trap(self, height: List[int]) -> int: 11 | ans = 0 12 | left, right = 0, len(height) - 1 13 | leftMax = rightMax = 0 14 | 15 | while left < right: 16 | leftMax = max(leftMax, height[left]) 17 | rightMax = max(rightMax, height[right]) 18 | if height[left] < height[right]: 19 | ans += leftMax - height[left] 20 | left += 1 21 | else: 22 | ans += rightMax - height[right] 23 | right -= 1 24 | 25 | return ans 26 | 27 | 28 | if __name__ == "__main__": 29 | Solution().trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) 30 | -------------------------------------------------------------------------------- /14-贪心算法/跳跃游戏.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 3 | 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。 4 | 5 | 示例 1: 6 | 输入:nums = [2,3,1,1,4] 7 | 输出:true 8 | 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。 9 | 10 | 示例 2: 11 | 输入:nums = [3,2,1,0,4] 12 | 输出:false 13 | 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。 14 | 15 | """ 16 | # https://leetcode.cn/problems/jump-game/solutions/2798996/liang-chong-li-jie-fang-shi-wei-hu-zui-y-q67s/?envType=study-plan-v2&envId=top-100-liked 17 | 18 | from typing import List 19 | 20 | 21 | class Solution: 22 | def canJump(self, nums: List[int]) -> bool: 23 | mx = 0 24 | for i, jump in enumerate(nums): 25 | if i > mx: 26 | return False 27 | mx = max(mx, i + jump) 28 | return True 29 | 30 | 31 | if __name__ == "__main__": 32 | nums = [2, 3, 1, 1, 4] 33 | print(Solution().canJump(nums)) 34 | -------------------------------------------------------------------------------- /8-二叉树/EASY-二叉树的最大深度.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树 root ,返回其最大深度。 3 | 4 | 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 5 | 6 | """ 7 | 8 | # https://leetcode.cn/problems/maximum-depth-of-binary-tree/solutions/2010612/kan-wan-zhe-ge-shi-pin-rang-ni-dui-di-gu-44uz 9 | from typing import Optional 10 | 11 | 12 | # Definition for a binary tree node. 13 | class TreeNode: 14 | def __init__(self, val=0, left=None, right=None): 15 | self.val = val 16 | self.left = left 17 | self.right = right 18 | 19 | 20 | class Solution: 21 | def maxDepth(self, root: Optional[TreeNode]) -> int: 22 | if root is None: 23 | return 0 24 | 25 | left = self.maxDepth(root.left) 26 | right = self.maxDepth(root.right) 27 | return max(left, right) + 1 28 | 29 | 30 | if __name__ == '__main__': 31 | s = Solution() 32 | print(s.maxDepth(TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7))))) 33 | -------------------------------------------------------------------------------- /8-二叉树/EASY-二叉树的中序遍历.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 3 | """ 4 | 5 | # 6 | from typing import Optional, List 7 | 8 | 9 | # Definition for a binary tree node. 10 | class TreeNode: 11 | def __init__(self, val=0, left=None, right=None): 12 | self.val = val 13 | self.left = left 14 | self.right = right 15 | 16 | 17 | class Solution: 18 | 19 | def order(self, root, res): 20 | if root == None: 21 | return 22 | self.order(root.left, res) 23 | res.append(root.val) 24 | self.order(root.right, res) 25 | 26 | def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 27 | res = [] 28 | self.order(root, res) 29 | return res 30 | 31 | 32 | if __name__ == '__main__': 33 | root = TreeNode(1) 34 | root.right = TreeNode(2) 35 | root.right.left = TreeNode(3) 36 | result = Solution().inorderTraversal(root) 37 | print(result) 38 | -------------------------------------------------------------------------------- /5-普通数组/合并区间.py: -------------------------------------------------------------------------------- 1 | """ 2 | 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 3 | 4 | 示例 1: 5 | 输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 6 | 输出:[[1,6],[8,10],[15,18]] 7 | 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 8 | 9 | 示例 2: 10 | 输入:intervals = [[1,4],[4,5]] 11 | 输出:[[1,5]] 12 | 解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。 13 | """ 14 | 15 | from typing import List 16 | 17 | 18 | class Solution: 19 | def merge(self, intervals: List[List[int]]) -> List[List[int]]: 20 | intervals.sort(key=lambda p: p[0]) # 按照左端点从小到大排序 21 | ans = [] 22 | for p in intervals: 23 | if ans and p[0] <= ans[-1][1]: # 可以合并 24 | ans[-1][1] = max(ans[-1][1], p[1]) # 更新右端点最大值 25 | else: # 不相交,无法合并 26 | ans.append(p) # 新的合并区间 27 | return ans 28 | 29 | 30 | if __name__ == "__main__": 31 | Solution().merge([[1, 3], [2, 6], [8, 10], [15, 18]]) 32 | -------------------------------------------------------------------------------- /7-链表/EASY-相交链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 3 | """ 4 | 5 | 6 | # Definition for singly-linked list. 7 | class ListNode: 8 | def __init__(self, x): 9 | self.val = x 10 | self.next = None 11 | 12 | 13 | # https://leetcode.cn/problems/intersection-of-two-linked-lists/solutions/12624/intersection-of-two-linked-lists-shuang-zhi-zhen-l 14 | class Solution: 15 | def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode: 16 | A, B = headA, headB 17 | while A != B: # 总会相交 18 | A = A.next if A else headB 19 | B = B.next if B else headA 20 | return A 21 | 22 | 23 | if __name__ == "__main__": 24 | headA = ListNode([4, 1, 8, 4, 5]) 25 | headB = ListNode([5, 6, 1, 8, 4, 5]) 26 | result = Solution().getIntersectionNode(headA, headB) 27 | while result: 28 | print(result.val) 29 | result = result.next 30 | -------------------------------------------------------------------------------- /8-二叉树/二叉树的最近公共祖先.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 3 | 4 | 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” 5 | 6 | """ 7 | 8 | # https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/solutions/2023872/fen-lei-tao-lun-luan-ru-ma-yi-ge-shi-pin-2r95 9 | # Definition for a binary tree node. 10 | class TreeNode: 11 | def __init__(self, x): 12 | self.val = x 13 | self.left = None 14 | self.right = None 15 | 16 | class Solution: 17 | def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': 18 | if root is None or root is q or root is p: 19 | return root 20 | 21 | left = self.lowestCommonAncestor(root.left, p, q) 22 | right = self.lowestCommonAncestor(root.right, p, q) 23 | if left and right: 24 | return root 25 | 26 | if left: 27 | return left 28 | return right 29 | -------------------------------------------------------------------------------- /7-链表/EASY-反转链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 3 | """ 4 | 5 | # https://leetcode.cn/problems/reverse-linked-list/solutions/1992225/you-xie-cuo-liao-yi-ge-shi-pin-jiang-tou-o5zy 6 | from typing import Optional 7 | 8 | 9 | # Definition for singly-linked list. 10 | class ListNode: 11 | def __init__(self, val=0, next=None): 12 | self.val = val 13 | self.next = next 14 | 15 | 16 | class Solution: 17 | def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: 18 | cur, pre = head, None 19 | while cur: 20 | tmp = cur.next 21 | cur.next = pre 22 | 23 | pre = cur 24 | cur = tmp 25 | return pre 26 | 27 | # tmp给cur,cur.next给pre 28 | 29 | if __name__ == '__main__': 30 | s = Solution() 31 | result = s.reverseList(ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5)))))) 32 | while result: 33 | print(result.val) 34 | result = result.next -------------------------------------------------------------------------------- /7-链表/EASY-环形链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个链表的头节点 head ,判断链表中是否有环。 3 | 4 | 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。 5 | 6 | 如果链表中存在环 ,则返回 true 。 否则,返回 false 。 7 | 8 | """ 9 | 10 | from typing import Optional 11 | 12 | 13 | # https://www.bilibili.com/video/BV1KG4y1G7cu/?vd_source=4aed82e35f26bb600bc5b46e65e25c22 14 | # Definition for singly-linked list. 15 | class ListNode: 16 | def __init__(self, x): 17 | self.val = x 18 | self.next = None 19 | 20 | 21 | class Solution: 22 | def hasCycle(self, head: Optional[ListNode]) -> bool: 23 | fast = slow = head 24 | while fast and fast.next: 25 | slow = slow.next 26 | fast = fast.next.next 27 | if fast is slow: 28 | return True 29 | return False 30 | 31 | 32 | if __name__ == '__main__': 33 | s = Solution() 34 | print(s.hasCycle(ListNode(1))) 35 | 36 | 37 | -------------------------------------------------------------------------------- /5-普通数组/缺失的第一个正数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 3 | 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 4 | 5 | 示例 1: 6 | 输入:nums = [1,2,0] 7 | 输出:3 8 | 解释:范围 [1,2] 中的数字都在数组中。 9 | 10 | 示例 2: 11 | 输入:nums = [3,4,-1,1] 12 | 输出:2 13 | 解释:1 在数组中,但 2 没有。 14 | 15 | 示例 3: 16 | 输入:nums = [7,8,9,11,12] 17 | 输出:1 18 | 解释:最小的正数 1 没有出现。 19 | """ 20 | 21 | from typing import List 22 | 23 | 24 | class Solution: 25 | def firstMissingPositive(self, nums: List[int]) -> int: 26 | for a in nums: # 遍历每个座位,记当前坐着a号乘客 27 | while 0 < a <= len(nums) and a != nums[a - 1]: # 乘客a是正票但坐错了! 其座位被 ta=nums[a-1]占了 28 | nums[a - 1], a = a, nums[a - 1] # a和ta两人互换则a对号入座。此后ta相当于新的a,去找自己的座位(循环执行) 29 | 30 | for i in range(len(nums)): 31 | if i + 1 != nums[i]: 32 | return i + 1 # 找到首个没有对号入座的nums[i]!=i+1 33 | return len(nums) + 1 # 满座,返回N+1 34 | 35 | 36 | if __name__ == "__main__": 37 | Solution().firstMissingPositive([1, 2, 0]) 38 | -------------------------------------------------------------------------------- /5-普通数组/轮转数组.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 3 | 4 | 示例 1: 5 | 输入: nums = [1,2,3,4,5,6,7], k = 3 6 | 输出: [5,6,7,1,2,3,4] 7 | 解释: 8 | 向右轮转 1 步: [7,1,2,3,4,5,6] 9 | 向右轮转 2 步: [6,7,1,2,3,4,5] 10 | 向右轮转 3 步: [5,6,7,1,2,3,4] 11 | 12 | 示例 2: 13 | 输入:nums = [-1,-100,3,99], k = 2 14 | 输出:[3,99,-1,-100] 15 | 解释: 16 | 向右轮转 1 步: [99,-1,-100,3] 17 | 向右轮转 2 步: [3,99,-1,-100] 18 | """ 19 | 20 | from typing import List 21 | 22 | 23 | # 注:请勿使用切片,会产生额外空间 24 | class Solution: 25 | def rotate(self, nums: List[int], k: int) -> None: 26 | def reverse(i: int, j: int) -> None: 27 | while i < j: 28 | nums[i], nums[j] = nums[j], nums[i] 29 | i += 1 30 | j -= 1 31 | 32 | n = len(nums) 33 | k %= n # 轮转 k 次等于轮转 k%n 次 34 | reverse(0, n - 1) 35 | reverse(0, k - 1) 36 | reverse(k, n - 1) 37 | 38 | 39 | if __name__ == "__main__": 40 | Solution().rotate([1, 2, 3, 4, 5, 6, 7], 3) 41 | -------------------------------------------------------------------------------- /6-矩阵/矩阵置零.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 3 | """ 4 | from typing import List 5 | 6 | 7 | # https://leetcode.cn/problems/set-matrix-zeroes/solutions/2461030/xian-gai-xing-hou-gai-lie-ga-ga-by-rin-o-0g60 8 | class Solution: 9 | def setZeroes(self, matrix: List[List[int]]) -> None: 10 | """ 11 | Do not return anything, modify matrix in-place instead. 12 | """ 13 | zero_pos = [] # 记录改列位置 14 | for ind, row in enumerate(matrix): 15 | for idx, val in enumerate(row): 16 | if val == 0: 17 | zero_pos.append(idx) 18 | matrix[ind] = [0] * len(row) # 先改行 19 | else: 20 | continue 21 | for pos in set(zero_pos): 22 | for row in range(len(matrix)): 23 | matrix[row][pos] = 0 # 后改列 24 | 25 | 26 | if __name__ == "__main__": 27 | Solution().setZeroes([[1, 1, 1], [1, 0, 1], [1, 1, 1]]) 28 | -------------------------------------------------------------------------------- /1-哈希/最长连续序列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 3 | 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 4 | 5 | 示例 1: 6 | 输入:nums = [100,4,200,1,3,2] 7 | 输出:4 8 | 解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。 9 | 10 | 示例 2: 11 | 输入:nums = [0,3,7,2,5,8,4,6,0,1] 12 | 输出:9 13 | """ 14 | 15 | 16 | from typing import List 17 | 18 | 19 | class Solution: 20 | def longestConsecutive(self, nums: List[int]) -> int: 21 | res = 0 # 记录最长连续序列的长度 22 | num_set = set(nums) # 记录nums中的所有数值 23 | for num in num_set: 24 | # 如果当前的数是一个连续序列的起点,统计这个连续序列的长度 25 | if (num - 1) not in num_set: 26 | seq_len = 1 # 连续序列的长度,初始为1 27 | while (num + 1) in num_set: 28 | seq_len += 1 29 | num += 1 # 不断查找连续序列,直到num的下一个数不存在于数组中 30 | res = max(res, seq_len) # 更新最长连续序列长度 31 | return res 32 | 33 | 34 | if __name__ == "__main__": 35 | Solution().longestConsecutive([100, 4, 200, 1, 3, 2]) 36 | -------------------------------------------------------------------------------- /1-哈希/EASY-两数之和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 3 | 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 4 | 你可以按任意顺序返回答案。 5 | 6 | 示例 1: 7 | 输入:nums = [2,7,11,15], target = 9 8 | 输出:[0,1] 9 | 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。 10 | 11 | 示例 2: 12 | 输入:nums = [3,2,4], target = 6 13 | 输出:[1,2] 14 | 15 | 示例 3: 16 | 输入:nums = [3,3], target = 6 17 | 输出:[0,1] 18 | """ 19 | 20 | from typing import List 21 | 22 | 23 | # https://leetcode.cn/problems/two-sum/solutions/2326193/dong-hua-cong-liang-shu-zhi-he-zhong-wo-0yvmj 24 | class Solution: 25 | def twoSum(self, nums: List[int], target: int) -> List[int]: 26 | value = {} 27 | for index, i in enumerate(nums): 28 | if value.get(target - i) is not None: 29 | return [index, value.get(target - i)] 30 | value[i] = index 31 | 32 | 33 | if __name__ == "__main__": 34 | nums = [2, 7, 11, 15] 35 | target = 9 36 | 37 | print(Solution().twoSum(nums, target)) 38 | -------------------------------------------------------------------------------- /8-二叉树/从前序与中序遍历序列构造二叉树.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。 3 | 4 | """ 5 | from typing import List, Optional 6 | 7 | 8 | # https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solutions/2646359/tu-jie-cong-on2-dao-onpythonjavacgojsrus-aob8 9 | 10 | # Definition for a binary tree node. 11 | class TreeNode: 12 | def __init__(self, val=0, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | class Solution: 19 | def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: 20 | if not preorder: 21 | return None 22 | 23 | left_size = inorder.index(preorder[0]) 24 | left = self.buildTree(preorder[1:1 + left_size], inorder[:left_size]) 25 | right = self.buildTree(preorder[1 + left_size:], inorder[1 + left_size:]) 26 | return TreeNode(preorder[0], left, right) 27 | -------------------------------------------------------------------------------- /8-二叉树/EASY-将有序数组转成平衡二叉搜索树.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡二叉搜索树。 3 | """ 4 | 5 | from typing import List, Optional 6 | 7 | 8 | # Definition for a binary tree node. 9 | class TreeNode: 10 | def __init__(self, val=0, left=None, right=None): 11 | self.val = val 12 | self.left = left 13 | self.right = right 14 | 15 | # https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/solutions/313814/yi-wen-du-dong-shi-yao-shi-er-cha-sou-suo-shu-bst- 16 | # 题解就一句话,中间拉起来,两边递归成树 17 | class Solution: 18 | def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]: 19 | def makeTree(start, end): 20 | if start > end: 21 | return 22 | 23 | mid = (start + end) // 2 24 | midTree = TreeNode(nums[mid]) 25 | midTree.left = makeTree(start, mid - 1) 26 | midTree.right = makeTree(mid + 1, end) 27 | return midTree 28 | 29 | return makeTree(0, len(nums) - 1) 30 | -------------------------------------------------------------------------------- /15-动态规划/零钱兑换.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 3 | 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 4 | 你可以认为每种硬币的数量是无限的。 5 | 6 | 示例 1: 7 | 输入:coins = [1, 2, 5], amount = 11 8 | 输出:3 9 | 解释:11 = 5 + 5 + 1 10 | 11 | 示例 2: 12 | 输入:coins = [2], amount = 3 13 | 输出:-1 14 | 15 | 示例 3: 16 | 输入:coins = [1], amount = 0 17 | 输出:0 18 | """ 19 | from functools import cache 20 | from typing import List 21 | 22 | inf = float("inf") 23 | 24 | 25 | class Solution: 26 | def coinChange(self, coins: List[int], amount: int) -> int: 27 | n = len(coins) 28 | 29 | @cache 30 | def dfs(i, c): 31 | if i < 0: 32 | return 0 if c == 0 else inf 33 | if c < coins[i]: 34 | return dfs(i - 1, c) 35 | return min(dfs(i - 1, c), dfs(i, c - coins[i]) + 1) 36 | 37 | ans = dfs(n - 1, amount) 38 | 39 | return ans if ans < inf else -1 40 | 41 | 42 | if __name__ == '__main__': 43 | print(Solution().coinChange([1, 2, 5], 11)) 44 | -------------------------------------------------------------------------------- /16-多维动态规划/最小路径和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 3 | 说明:每次只能向下或者向右移动一步。 4 | 5 | 输入:grid = [[1,3,1],[1,5,1],[4,2,1]] 6 | 输出:7 7 | 解释:因为路径 1→3→1→1→1 的总和最小。 8 | 9 | 示例 2: 10 | 输入:grid = [[1,2,3],[4,5,6]] 11 | 输出:12 12 | """ 13 | 14 | from typing import List 15 | 16 | 17 | class Solution: 18 | def minPathSum(self, grid: List[List[int]]) -> int: 19 | m = len(grid) 20 | n = len(grid[0]) 21 | 22 | for i in range(m): 23 | for j in range(n): 24 | if i == j == 0: 25 | continue 26 | elif i == 0: 27 | grid[i][j] = grid[i][j - 1] + grid[i][j] 28 | elif j == 0: 29 | grid[i][j] = grid[i - 1][j] + grid[i][j] 30 | else: 31 | grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j] 32 | 33 | return grid[-1][-1] 34 | 35 | 36 | if __name__ == "__main__": 37 | print(Solution().minPathSum([[1, 3, 1], [1, 5, 1], [4, 2, 1]])) 38 | -------------------------------------------------------------------------------- /4-子串/和为k的子数组.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。 3 | 子数组是数组中元素的连续非空序列。 4 | 5 | 示例 1: 6 | 输入:nums = [1,1,1], k = 2 7 | 输出:2 8 | 9 | 示例 2: 10 | 输入:nums = [1,2,3], k = 3 11 | 输出:2 12 | """ 13 | 14 | from typing import List 15 | import collections 16 | 17 | 18 | # https://leetcode.cn/problems/subarray-sum-equals-k/solutions/2781031/qian-zhui-he-ha-xi-biao-cong-liang-ci-bi-4mwr 19 | class Solution: 20 | def subarraySum(self, nums: List[int], k: int) -> int: 21 | # 要求的连续子数组 22 | count = 0 23 | n = len(nums) 24 | preSums = collections.defaultdict(int) 25 | preSums[0] = 1 26 | 27 | presum = 0 28 | for i in range(n): 29 | presum += nums[i] 30 | 31 | # if preSums[presum - k] != 0: 32 | count += preSums[presum - k] # 利用defaultdict的特性,当presum-k不存在时,返回的是0。这样避免了判断 33 | 34 | preSums[presum] += 1 # 给前缀和为presum的个数加1 35 | 36 | return count 37 | 38 | 39 | if __name__ == "__main__": 40 | Solution().subarraySum([1, 1, 1], 2) 41 | -------------------------------------------------------------------------------- /15-动态规划/分割等和子集.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 3 | 4 | 示例 1: 5 | 输入:nums = [1,5,11,5] 6 | 输出:true 7 | 解释:数组可以分割成 [1, 5, 5] 和 [11] 。 8 | 9 | 示例 2: 10 | 输入:nums = [1,2,3,5] 11 | 输出:false 12 | 解释:数组不能分割成两个元素和相等的子集。 13 | """ 14 | from typing import List 15 | from functools import cache 16 | 17 | 18 | class Solution: 19 | def canPartition(self, nums: List[int]) -> bool: 20 | # @cache 21 | # def dfs(i,c): 22 | # if i<0: 23 | # return c==0 24 | # return c>=nums[i] and dfs(i-1,c-nums[i]) or dfs(i-i,c) 25 | 26 | @cache # 缓存装饰器,避免重复计算 dfs 的结果(记忆化) 27 | def dfs(i: int, j: int) -> bool: 28 | if i < 0: 29 | return j == 0 30 | return j >= nums[i] and dfs(i - 1, j - nums[i]) or dfs(i - 1, j) 31 | 32 | s = sum(nums) 33 | return s % 2 == 0 and dfs(len(nums) - 1, s // 2) 34 | 35 | 36 | if __name__ == '__main__': 37 | nums = [1, 5, 11, 5] 38 | print("\n") 39 | print(Solution().canPartition(nums)) 40 | -------------------------------------------------------------------------------- /10-回溯/N皇后.py: -------------------------------------------------------------------------------- 1 | """ 2 | 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 3 | n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 4 | 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。 5 | 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。 6 | """ 7 | from typing import List 8 | 9 | 10 | class Solution: 11 | def solveNQueens(self, n: int) -> List[List[str]]: 12 | ans = [] 13 | col = [0] * n 14 | 15 | def valid(r, c): 16 | for R in range(r): 17 | C = col[R] 18 | if R + C == r + c or R - C == r - c: 19 | return False 20 | return True 21 | 22 | def dfs(r, s): 23 | if r == n: 24 | ans.append(["." * c + "Q" + "." * (n - c - 1) for c in col]) 25 | return 26 | 27 | for c in s: 28 | if valid(r, c): 29 | col[r] = c 30 | dfs(r + 1, s - {c}) 31 | 32 | dfs(0, set(range(n))) 33 | return ans 34 | 35 | 36 | if __name__ == '__main__': 37 | print(Solution().solveNQueens(4)) 38 | -------------------------------------------------------------------------------- /8-二叉树/二叉树的层序遍历.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 3 | 4 | """ 5 | 6 | from typing import Optional, List 7 | 8 | 9 | # Definition for a binary tree node. 10 | class TreeNode: 11 | def __init__(self, val=0, left=None, right=None): 12 | self.val = val 13 | self.left = left 14 | self.right = right 15 | 16 | 17 | # https://leetcode.cn/problems/binary-tree-level-order-traversal/solutions/2049807/bfs-wei-shi-yao-yao-yong-dui-lie-yi-ge-s-xlpz 18 | class Solution: 19 | def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: 20 | if root is None: 21 | return [] 22 | 23 | ans = [] 24 | cur = [root] 25 | while cur: 26 | nxt = [] 27 | vals = [] 28 | for node in cur: 29 | vals.append(node.val) 30 | if node.left: 31 | nxt.append(node.left) 32 | if node.right: 33 | nxt.append(node.right) 34 | cur = nxt 35 | ans.append(vals) 36 | return ans 37 | -------------------------------------------------------------------------------- /15-动态规划/乘积最大子数组.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 3 | 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 4 | 测试用例的答案是一个 32-位 整数。 5 | 6 | 示例 1: 7 | 输入: nums = [2,3,-2,4] 8 | 输出: 6 9 | 解释: 子数组 [2,3] 有最大乘积 6。 10 | 11 | 示例 2: 12 | 输入: nums = [-2,0,-1] 13 | 输出: 0 14 | 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 15 | 16 | """ 17 | from typing import List 18 | 19 | 20 | class Solution: 21 | def maxProduct(self, nums: List[int]) -> int: 22 | if not nums: 23 | return 24 | 25 | res = nums[0] 26 | pre_max = nums[0] 27 | pre_min = nums[0] 28 | 29 | for num in nums[1:]: 30 | cur_max = max(pre_max * num, pre_min * num, num) 31 | cur_min = min(pre_max * num, pre_min * num, num) 32 | res = max(res, cur_max) 33 | pre_max = cur_max 34 | pre_min = cur_min 35 | 36 | return res 37 | 38 | 39 | # https://leetcode.cn/problems/maximum-product-subarray/solutions/17709/duo-chong-si-lu-qiu-jie-by-powcai-3/ 40 | 41 | if __name__ == '__main__': 42 | nums = [2, 3, -2, 4] 43 | print(Solution().maxProduct(nums)) 44 | -------------------------------------------------------------------------------- /8-二叉树/二叉树中的最大路径和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。 3 | 4 | 路径和 是路径中各节点值的总和。 5 | 6 | 给你一个二叉树的根节点 root ,返回其 最大路径和 。 7 | 8 | 输入:root = [-10,9,20,null,null,15,7] 9 | 输出:42 10 | 解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42 11 | """ 12 | 13 | from typing import Optional 14 | from math import inf 15 | 16 | 17 | # Definition for a binary tree node. 18 | class TreeNode: 19 | def __init__(self, val=0, left=None, right=None): 20 | self.val = val 21 | self.left = left 22 | self.right = right 23 | 24 | 25 | class Solution: 26 | def maxPathSum(self, root: Optional[TreeNode]) -> int: 27 | ans = -inf 28 | 29 | def dfs(root): 30 | if root is None: 31 | return 0 32 | left_value = dfs(root.left) 33 | right_value = dfs(root.right) 34 | nonlocal ans 35 | ans = max(ans, left_value + right_value + root.val) 36 | return max(max(left_value, right_value) + root.val, 0) 37 | 38 | dfs(root) 39 | return ans 40 | -------------------------------------------------------------------------------- /14-贪心算法/跳跃游戏ll.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 3 | 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处: 4 | 0 <= j <= nums[i] 5 | i + j < n 6 | 返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。 7 | 8 | 示例 1: 9 | 输入: nums = [2,3,1,1,4] 10 | 输出: 2 11 | 解释: 跳到最后一个位置的最小跳跃数是 2。 12 | 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 13 | 14 | 示例 2: 15 | 输入: nums = [2,3,0,1,4] 16 | 输出: 2 17 | 18 | """ 19 | # https://leetcode.cn/problems/jump-game-ii/solutions/2926993/tu-jie-yi-zhang-tu-miao-dong-tiao-yue-yo-h2d4/ 20 | 21 | from typing import List 22 | 23 | 24 | class Solution: 25 | def jump(self, nums: List[int]) -> int: 26 | next_right = 0 27 | cur_right = 0 28 | ans = 0 29 | 30 | for i in range(len(nums) - 1): 31 | jump = nums[i] 32 | next_right = max(next_right, i + jump) 33 | if i == cur_right: 34 | cur_right = next_right 35 | ans += 1 36 | return ans 37 | 38 | 39 | if __name__ == '__main__': 40 | nums = [2, 3, 1, 1, 4] 41 | print(Solution().jump(nums)) 42 | -------------------------------------------------------------------------------- /17-技巧/颜色分类.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 3 | 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 4 | 必须在不使用库内置的 sort 函数的情况下解决这个问题。 5 | 6 | 示例 1: 7 | 输入:nums = [2,0,2,1,1,0] 8 | 输出:[0,0,1,1,2,2] 9 | 10 | 示例 2: 11 | 输入:nums = [2,0,1] 12 | 输出:[0,1,2] 13 | 14 | """ 15 | 16 | 17 | class Solution: 18 | def sortColors(self, nums): 19 | """ 20 | Do not return anything, modify nums in-place instead. 21 | """ 22 | left, point, right = 0, 0, len(nums) - 1 23 | while point <= right: 24 | if nums[point] == 2: 25 | nums[right], nums[point] = nums[point], nums[right] 26 | right -= 1 27 | continue 28 | if nums[point] == 0: 29 | nums[left], nums[point] = nums[point], nums[left] 30 | left += 1 31 | point += 1 32 | 33 | 34 | # 链接:https://leetcode.cn/problems/sort-colors/solutions/672825/75-yan-se-fen-lei-pythonzhi-zhen-yi-bian-0vct/ 35 | 36 | if __name__ == '__main__': 37 | nums = [2, 0, 2, 1, 1, 0] 38 | print(Solution().sortColors(nums)) 39 | -------------------------------------------------------------------------------- /11-二分查找/搜索二维矩阵.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个满足下述两条属性的 m x n 整数矩阵: 3 | 4 | 每行中的整数从左到右按非严格递增顺序排列。 5 | 每行的第一个整数大于前一行的最后一个整数。 6 | 给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。 7 | 8 | """ 9 | 10 | # https://leetcode.cn/problems/search-a-2d-matrix/solutions/2783931/liang-chong-fang-fa-er-fen-cha-zhao-pai-39d74/?envType=study-plan-v2&envId=top-100-liked 11 | 12 | from typing import List 13 | 14 | 15 | class Solution: 16 | def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: 17 | m = len(matrix) 18 | n = len(matrix[0]) 19 | left = -1 20 | right = m * n 21 | 22 | while left + 1 < right: 23 | mid = (left + right) // 2 24 | num = matrix[mid // n][mid % n] 25 | if num == target: 26 | return True 27 | if num < target: 28 | left = mid 29 | else: 30 | right = mid 31 | 32 | return False 33 | 34 | 35 | if __name__ == '__main__': 36 | matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]] 37 | target = 3 38 | print(Solution().searchMatrix(matrix, target)) 39 | -------------------------------------------------------------------------------- /14-贪心算法/划分字母区间.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。 3 | 注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。 4 | 返回一个表示每个字符串片段的长度的列表。 5 | 6 | 示例 1: 7 | 输入:s = "ababcbacadefegdehijhklij" 8 | 输出:[9,7,8] 9 | 解释: 10 | 划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。 11 | 每个字母最多出现在一个片段中。 12 | 像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。 13 | 14 | 示例 2: 15 | 输入:s = "eccbbbbdec" 16 | 输出:[10] 17 | """ 18 | 19 | from typing import List 20 | 21 | 22 | # https://leetcode.cn/problems/partition-labels/solutions/2806706/ben-zhi-shi-he-bing-qu-jian-jian-ji-xie-ygsn8/?envType=study-plan-v2&envId=top-100-liked 23 | 24 | class Solution: 25 | def partitionLabels(self, s: str) -> List[int]: 26 | temp = {c: i for i, c in enumerate(s)} 27 | start = end = 0 28 | ans = [] 29 | 30 | for i, c in enumerate(s): 31 | end = max(end, temp[c]) 32 | if i == end: 33 | ans.append(end - start + 1) 34 | start = i + 1 35 | return ans 36 | 37 | 38 | if __name__ == '__main__': 39 | s = "ababcbacadefegdehijhklij" 40 | print(Solution().partitionLabels(s)) 41 | -------------------------------------------------------------------------------- /15-动态规划/最长递增子序列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 3 | 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的 4 | 子序列。 5 | 6 | 示例 1: 7 | 输入:nums = [10,9,2,5,3,7,101,18] 8 | 输出:4 9 | 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。 10 | 11 | 示例 2: 12 | 输入:nums = [0,1,0,3,2,3] 13 | 输出:4 14 | 15 | 示例 3: 16 | 输入:nums = [7,7,7,7,7,7,7] 17 | 输出:1 18 | """ 19 | from functools import cache 20 | from typing import List 21 | 22 | 23 | class Solution: 24 | def lengthOfLIS(self, nums: List[int]) -> int: 25 | # ans = 0 26 | 27 | @cache 28 | def dfs(i): 29 | res = 0 30 | for j in range(i): 31 | if nums[j] < nums[i]: 32 | res = max(res, dfs(j)) 33 | # res +=1 34 | return res + 1 35 | 36 | # for i in range(len(nums)): 37 | # ans = max(ans,dfs(i)) 38 | # return ans 39 | return max(dfs(i) for i in range(len(nums))) 40 | 41 | 42 | if __name__ == '__main__': 43 | nums = [10, 9, 2, 5, 3, 7, 101, 18] 44 | print(Solution().lengthOfLIS(nums)) 45 | 46 | # 状态转移方程:dp[i] = max(dp[j]) + 1 47 | 48 | -------------------------------------------------------------------------------- /12-栈/每日温度.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。 3 | 4 | 示例 1: 5 | 输入: temperatures = [73,74,75,71,69,72,76,73] 6 | 输出: [1,1,4,2,1,1,0,0] 7 | 8 | 示例 2: 9 | 输入: temperatures = [30,40,50,60] 10 | 输出: [1,1,1,0] 11 | 12 | 示例 3: 13 | 输入: temperatures = [30,60,90] 14 | 输出: [1,1,0] 15 | """ 16 | 17 | # https://leetcode.cn/problems/daily-temperatures/solutions/2470179/shi-pin-jiang-qing-chu-wei-shi-yao-yao-y-k0ks/?envType=study-plan-v2&envId=top-100-liked 18 | 19 | from typing import List 20 | 21 | 22 | class Solution: 23 | def dailyTemperatures(self, temperatures: List[int]) -> List[int]: 24 | n = len(temperatures) 25 | st = [] 26 | ans = [0] * n 27 | 28 | for i in range(n - 1, -1, -1): 29 | t = temperatures[i] 30 | while st and t >= temperatures[st[-1]]: 31 | st.pop() 32 | if st: 33 | ans[i] = st[-1] - i 34 | st.append(i) 35 | return ans 36 | 37 | 38 | if __name__ == '__main__': 39 | print(Solution().dailyTemperatures([73, 74, 75, 71, 69, 72, 76, 73])) 40 | -------------------------------------------------------------------------------- /8-二叉树/EASY-二叉树的直径.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一棵二叉树的根节点,返回该树的 直径 。 3 | 4 | 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 5 | 6 | 两节点之间路径的 长度 由它们之间边数表示。 7 | """ 8 | 9 | # https://leetcode.cn/problems/diameter-of-binary-tree/solutions/2227017/shi-pin-che-di-zhang-wo-zhi-jing-dpcong-taqma 10 | from typing import Optional 11 | 12 | 13 | # Definition for a binary tree node. 14 | class TreeNode: 15 | def __init__(self, val=0, left=None, right=None): 16 | self.val = val 17 | self.left = left 18 | self.right = right 19 | 20 | 21 | class Solution: 22 | def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: 23 | ans = 0 24 | 25 | def dfs(node): 26 | if node is None: 27 | return -1 28 | l_len = dfs(node.left) + 1 29 | r_len = dfs(node.right) + 1 30 | nonlocal ans 31 | ans = max(ans, l_len + r_len) 32 | return max(l_len, r_len) 33 | 34 | dfs(root) 35 | return ans 36 | 37 | if __name__ == '__main__': 38 | s = Solution() 39 | print(s.diameterOfBinaryTree(TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3)))) 40 | -------------------------------------------------------------------------------- /16-多维动态规划/最长公共子序列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。 3 | 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 4 | 5 | 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。 6 | 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。 7 | 8 | 示例 1: 9 | 输入:text1 = "abcde", text2 = "ace" 10 | 输出:3 11 | 解释:最长公共子序列是 "ace" ,它的长度为 3 。 12 | 13 | 示例 2: 14 | 输入:text1 = "abc", text2 = "abc" 15 | 输出:3 16 | 解释:最长公共子序列是 "abc" ,它的长度为 3 。 17 | 18 | 示例 3: 19 | 输入:text1 = "abc", text2 = "def" 20 | 输出:0 21 | 解释:两个字符串没有公共子序列,返回 0 。 22 | 23 | """ 24 | from functools import cache 25 | 26 | 27 | class Solution: 28 | def longestCommonSubsequence(self, text1: str, text2: str) -> int: 29 | n = len(text1) 30 | m = len(text2) 31 | 32 | @cache 33 | def dfs(i, j): 34 | if i < 0 or j < 0: 35 | return 0 36 | if text1[i] == text2[j]: 37 | return dfs(i - 1, j - 1) + 1 38 | return max(dfs(i - 1, j), dfs(i, j - 1)) 39 | 40 | return dfs(n - 1, m - 1) 41 | 42 | 43 | if __name__ == '__main__': 44 | s = Solution() 45 | print(s.longestCommonSubsequence("abcde", "ace")) 46 | -------------------------------------------------------------------------------- /7-链表/EASY-合并两个有序链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 3 | 4 | """ 5 | from typing import Optional 6 | 7 | 8 | # https://leetcode.cn/problems/merge-two-sorted-lists/solutions/2373691/liang-chong-fang-fa-die-dai-di-gui-pytho-wf75 9 | # Definition for singly-linked list. 10 | class ListNode: 11 | def __init__(self, val=0, next=None): 12 | self.val = val 13 | self.next = next 14 | 15 | 16 | class Solution: 17 | def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: 18 | if list1 is None: 19 | return list2 20 | if list2 is None: 21 | return list1 22 | while list1.val < list2.val: 23 | list1.next = self.mergeTwoLists(list1.next, list2) 24 | return list1 25 | list2.next = self.mergeTwoLists(list1, list2.next) 26 | return list2 27 | 28 | 29 | if __name__ == '__main__': 30 | list1 = ListNode(1, ListNode(4, ListNode(5))) 31 | list2 = ListNode(1, ListNode(3, ListNode(4))) 32 | result = Solution().mergeTwoLists(list1, list2) 33 | while result: 34 | print(result.val) 35 | result = result.next 36 | -------------------------------------------------------------------------------- /8-二叉树/二叉搜索树中的第K小的元素.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 3 | 4 | """ 5 | from typing import Optional 6 | 7 | 8 | # https://leetcode.cn/problems/kth-smallest-element-in-a-bst/solutions/2361685/230-er-cha-sou-suo-shu-zhong-di-k-xiao-d-n3he 9 | 10 | # Definition for a binary tree node. 11 | class TreeNode: 12 | def __init__(self, val=0, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | class Solution: 19 | def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: 20 | def dfs(root): 21 | if root is None: 22 | return 23 | dfs(root.left) 24 | if self.k == 0: 25 | return 26 | self.k -= 1 27 | if self.k == 0: 28 | self.res = root.val 29 | dfs(root.right) 30 | 31 | self.k = k 32 | dfs(root) 33 | return self.res 34 | 35 | 36 | if __name__ == '__main__': 37 | root = TreeNode(3) 38 | root.left = TreeNode(1) 39 | root.right = TreeNode(4) 40 | root.left.right = TreeNode(2) 41 | print(Solution().kthSmallest(root, 1)) 42 | -------------------------------------------------------------------------------- /8-二叉树/路径总和III.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 3 | 4 | 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 5 | 6 | """ 7 | 8 | # https://leetcode.cn/problems/path-sum-iii/solutions/2784856/zuo-fa-he-560-ti-shi-yi-yang-de-pythonja-fmzo 9 | 10 | from typing import Optional 11 | from collections import defaultdict 12 | 13 | # Definition for a binary tree node. 14 | class TreeNode: 15 | def __init__(self, val=0, left=None, right=None): 16 | self.val = val 17 | self.left = left 18 | self.right = right 19 | 20 | 21 | class Solution: 22 | def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int: 23 | if not root: 24 | return 0 25 | 26 | ans = 0 27 | cnt = defaultdict(int) 28 | cnt[0] = 1 29 | 30 | def dfs(node, s): 31 | if node is None: 32 | return 33 | 34 | s += node.val 35 | nonlocal ans 36 | ans += cnt[s - targetSum] 37 | cnt[s] += 1 38 | dfs(node.left, s) 39 | dfs(node.right, s) 40 | cnt[s] -= 1 41 | 42 | dfs(root, 0) 43 | return ans 44 | 45 | -------------------------------------------------------------------------------- /11-二分查找/寻找旋转排序数组中的最小值.py: -------------------------------------------------------------------------------- 1 | """ 2 | 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到: 3 | 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2] 4 | 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7] 5 | 注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。 6 | 给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。 7 | 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。 8 | 9 | 示例 1: 10 | 输入:nums = [3,4,5,1,2] 11 | 输出:1 12 | 解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。 13 | 14 | 示例 2: 15 | 输入:nums = [4,5,6,7,0,1,2] 16 | 输出:0 17 | 解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。 18 | 19 | 示例 3: 20 | 输入:nums = [11,13,15,17] 21 | 输出:11 22 | 解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。 23 | 24 | """ 25 | 26 | from typing import List 27 | 28 | 29 | class Solution: 30 | def findMin(self, nums: List[int]) -> int: 31 | left = -1 32 | right = len(nums) - 1 33 | while left + 1 < right: 34 | mid = (left + right) // 2 35 | if nums[mid] < nums[-1]: 36 | right = mid 37 | else: 38 | left = mid 39 | return nums[right] 40 | 41 | 42 | if __name__ == '__main__': 43 | nums = [3, 4, 5, 1, 2] 44 | print(Solution().findMin(nums)) 45 | -------------------------------------------------------------------------------- /10-回溯/单词搜索.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 3 | 4 | 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 5 | """ 6 | 7 | from typing import List 8 | 9 | 10 | class Solution: 11 | def exist(self, board: List[List[str]], word: str) -> bool: 12 | 13 | def dfs(i, j, k): 14 | if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k]: 15 | return False 16 | 17 | if k == len(word) - 1: 18 | return True 19 | 20 | board[i][j] = "" 21 | if dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1): 22 | return True 23 | board[i][j] = word[k] 24 | 25 | for i in range(len(board)): 26 | for j in range(len(board[0])): 27 | if dfs(i, j, 0): 28 | return True 29 | return False 30 | 31 | 32 | # https://leetcode.cn/problems/word-search/solutions/2361646/79-dan-ci-sou-suo-hui-su-qing-xi-tu-jie-5yui2/ 33 | 34 | if __name__ == '__main__': 35 | print(Solution().exist([["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]], "ABCCED")) 36 | -------------------------------------------------------------------------------- /8-二叉树/二叉树展开为链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你二叉树的根结点 root ,请你将它展开为一个单链表: 3 | 4 | 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 5 | 展开后的单链表应该与二叉树 先序遍历 顺序相同。 6 | """ 7 | 8 | # https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/solutions/131121/dong-hua-yan-shi-si-chong-jie-fa-114-er-cha-shu-zh 9 | from typing import Optional 10 | 11 | 12 | # Definition for a binary tree node. 13 | class TreeNode: 14 | def __init__(self, val=0, left=None, right=None): 15 | self.val = val 16 | self.left = left 17 | self.right = right 18 | 19 | 20 | class Solution: 21 | def flatten(self, root: Optional[TreeNode]) -> None: 22 | """ 23 | Do not return anything, modify root in-place instead. 24 | """ 25 | if root is None: 26 | return 27 | 28 | queue = [] 29 | 30 | def dfs(root): 31 | if not root: 32 | return 33 | queue.append(root) 34 | dfs(root.left) 35 | dfs(root.right) 36 | 37 | dfs(root) 38 | head = queue.pop(0) 39 | head.left = None 40 | while queue: 41 | tmp = queue.pop(0) 42 | tmp.left = None 43 | head.right = tmp 44 | head = tmp 45 | -------------------------------------------------------------------------------- /15-动态规划/单词拆分.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 3 | 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 4 | 5 | 示例 1: 6 | 输入: s = "leetcode", wordDict = ["leet", "code"] 7 | 输出: true 8 | 解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。 9 | 10 | 示例 2: 11 | 输入: s = "applepenapple", wordDict = ["apple", "pen"] 12 | 输出: true 13 | 解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。 14 | 注意,你可以重复使用字典中的单词。 15 | 16 | 示例 3: 17 | 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 18 | 输出: false 19 | """ 20 | from functools import cache 21 | from typing import List 22 | 23 | 24 | class Solution: 25 | def wordBreak(self, s: str, wordDict: List[str]) -> bool: 26 | n = len(s) 27 | 28 | @cache 29 | def dfs(i): 30 | if i == n: 31 | return True 32 | for j in range(i + 1, n + 1): 33 | if s[i:j] in wordDict and dfs(j): 34 | return True 35 | return False 36 | 37 | return dfs(0) 38 | 39 | 40 | # https://leetcode.cn/problems/word-break/solutions/2916680/shi-pin-jiao-ni-yi-bu-bu-si-kao-dong-tai-zcw7/ 41 | 42 | if __name__ == "__main__": 43 | print(Solution().wordBreak("leetcode", ["leet", "code"])) 44 | -------------------------------------------------------------------------------- /11-二分查找/在排序数组中查找元素的第一个和最后一个位置.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 3 | 如果数组中不存在目标值 target,返回 [-1, -1]。 4 | 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 5 | 6 | 示例 1: 7 | 输入:nums = [5,7,7,8,8,10], target = 8 8 | 输出:[3,4] 9 | 10 | 示例 2: 11 | 输入:nums = [5,7,7,8,8,10], target = 6 12 | 输出:[-1,-1] 13 | 14 | 示例 3: 15 | 输入:nums = [], target = 0 16 | 输出:[-1,-1] 17 | 18 | """ 19 | 20 | from typing import List 21 | 22 | 23 | class Solution: 24 | def searchRange(self, nums: List[int], target: int) -> List[int]: 25 | 26 | def get_index(nums, target): 27 | start = 0 28 | end = len(nums) - 1 29 | while start <= end: 30 | mid = (start + end) // 2 31 | if nums[mid] < target: 32 | start = mid + 1 33 | else: 34 | end = mid - 1 35 | return start 36 | 37 | start = get_index(nums, target) 38 | if start == len(nums) or nums[start] != target: 39 | return [-1, -1] 40 | end = get_index(nums, target + 1) - 1 41 | return [start, end] 42 | 43 | 44 | if __name__ == '__main__': 45 | nums = [5, 7, 7, 8, 8, 10] 46 | target = 8 47 | print(Solution().searchRange(nums, target)) 48 | -------------------------------------------------------------------------------- /3-滑动窗口/无重复字符的最长子串.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 3 | 4 | 示例 1: 5 | 输入: s = "abcabcbb" 6 | 输出: 3 7 | 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 8 | 9 | 示例 2: 10 | 输入: s = "bbbbb" 11 | 输出: 1 12 | 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 13 | 14 | 示例 3: 15 | 输入: s = "pwwkew" 16 | 输出: 3 17 | 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 18 | 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 19 | """ 20 | 21 | # https://leetcode.cn/problems/longest-substring-without-repeating-characters/solutions/1959540/xia-biao-zong-suan-cuo-qing-kan-zhe-by-e-iaks 22 | class Solution: 23 | def lengthOfLongestSubstring(self, s: str) -> int: 24 | # 哈希集合,记录每个字符是否出现过 25 | occ = set() 26 | n = len(s) 27 | # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动 28 | rk, ans = -1, 0 29 | for i in range(n): 30 | if i != 0: 31 | # 左指针向右移动一格,移除一个字符 32 | occ.remove(s[i - 1]) 33 | while rk + 1 < n and s[rk + 1] not in occ: 34 | # 不断地移动右指针 35 | occ.add(s[rk + 1]) 36 | rk += 1 37 | # 第 i 到 rk 个字符是一个极长的无重复字符子串 38 | ans = max(ans, rk - i + 1) 39 | return ans 40 | 41 | 42 | if __name__ == "__main__": 43 | Solution().lengthOfLongestSubstring("abcabcbb") 44 | -------------------------------------------------------------------------------- /16-多维动态规划/编译距离.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。 3 | 你可以对一个单词进行如下三种操作: 4 | 5 | 插入一个字符 6 | 删除一个字符 7 | 替换一个字符 8 | 9 | 示例 1: 10 | 输入:word1 = "horse", word2 = "ros" 11 | 输出:3 12 | 解释: 13 | horse -> rorse (将 'h' 替换为 'r') 14 | rorse -> rose (删除 'r') 15 | rose -> ros (删除 'e') 16 | 17 | 示例 2: 18 | 输入:word1 = "intention", word2 = "execution" 19 | 输出:5 20 | 解释: 21 | intention -> inention (删除 't') 22 | inention -> enention (将 'i' 替换为 'e') 23 | enention -> exention (将 'n' 替换为 'x') 24 | exention -> exection (将 'n' 替换为 'c') 25 | exection -> execution (插入 'u') 26 | 27 | """ 28 | from functools import cache 29 | 30 | 31 | class Solution: 32 | def minDistance(self, word1: str, word2: str) -> int: 33 | 34 | n = len(word1) 35 | m = len(word2) 36 | 37 | @cache 38 | def dfs(i, j): 39 | if i < 0: 40 | return j + 1 41 | if j < 0: 42 | return i + 1 43 | if word1[i] == word2[j]: 44 | return dfs(i - 1, j - 1) 45 | 46 | return min(dfs(i - 1, j), dfs(i, j - 1), dfs(i - 1, j - 1)) + 1 47 | 48 | return dfs(n - 1, m - 1) 49 | 50 | 51 | if __name__ == '__main__': 52 | s = Solution() 53 | print(s.minDistance("horse", "ros")) 54 | -------------------------------------------------------------------------------- /7-链表/删除链表的倒数第N个结点.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 3 | """ 4 | from typing import Optional 5 | 6 | 7 | # https://leetcode.cn/problems/remove-nth-node-from-end-of-list/solutions/2004057/ru-he-shan-chu-jie-dian-liu-fen-zhong-ga-xpfs 8 | # Definition for singly-linked list. 9 | class ListNode: 10 | def __init__(self, val=0, next=None): 11 | self.val = val 12 | self.next = next 13 | 14 | 15 | class Solution: 16 | def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: 17 | # 由于可能会删除链表头部,用哨兵节点简化代码 18 | left = right = dummy = ListNode(next=head) 19 | 20 | for _ in range(n): 21 | right = right.next # 右指针先向右走 n 步 22 | 23 | while right.next: 24 | left = left.next 25 | right = right.next # 左右指针一起走 26 | left.next = left.next.next # 左指针的下一个节点就是倒数第 n 个节点 27 | return dummy.next 28 | 29 | # right走到投了,left和right差着n,所以left是倒数的n,直接把下一个copy过来即可 30 | 31 | 32 | if __name__ == '__main__': 33 | head = ListNode(1) 34 | head.next = ListNode(2) 35 | head.next.next = ListNode(3) 36 | result = Solution().removeNthFromEnd(head, 2) 37 | print(result) 38 | # 返回具体链表的值 39 | while result: 40 | print(result.val) 41 | result = result.next -------------------------------------------------------------------------------- /7-链表/环形链表II.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 3 | 4 | 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 5 | 6 | 不允许修改 链表。 7 | """ 8 | 9 | # https://leetcode.cn/problems/linked-list-cycle-ii/solutions/1999271/mei-xiang-ming-bai-yi-ge-shi-pin-jiang-t-nvsq 10 | from typing import Optional 11 | 12 | 13 | # Definition for singly-linked list. 14 | class ListNode: 15 | def __init__(self, x): 16 | self.val = x 17 | self.next = None 18 | 19 | 20 | class Solution: 21 | def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: 22 | slow = fast = head 23 | while fast and fast.next: 24 | slow = slow.next 25 | fast = fast.next.next 26 | if fast is slow: 27 | while slow is not head: 28 | slow = slow.next 29 | head = head.next 30 | return slow 31 | return None 32 | 33 | 34 | if __name__ == '__main__': 35 | head = ListNode(3) 36 | head.next = ListNode(2) 37 | head.next.next = ListNode(0) 38 | head.next.next.next = ListNode(-4) 39 | head.next.next.next.next = head.next 40 | print(Solution().detectCycle(head)) 41 | -------------------------------------------------------------------------------- /15-动态规划/打家劫舍.py: -------------------------------------------------------------------------------- 1 | """ 2 | 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 3 | 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 4 | 5 | 示例 1: 6 | 输入:[1,2,3,1] 7 | 输出:4 8 | 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 9 | 偷窃到的最高金额 = 1 + 3 = 4 。 10 | 11 | 示例 2: 12 | 输入:[2,7,9,3,1] 13 | 输出:12 14 | 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 15 | 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 16 | 17 | """ 18 | from typing import List 19 | 20 | 21 | # https://www.bilibili.com/video/BV1Xj411K7oF/?vd_source=4aed82e35f26bb600bc5b46e65e25c22 22 | 23 | class Solution: 24 | def rob(self, nums: List[int]) -> int: 25 | cur, pre = 0, 0 26 | 27 | for num in nums: 28 | cur, pre = max(pre + num, cur), cur 29 | 30 | return cur 31 | 32 | 33 | if __name__ == '__main__': 34 | nums = [2, 7, 9, 3, 1] 35 | print(Solution().rob(nums)) 36 | 37 | 38 | # 打家劫舍算法题的状态转移方程:dp[i] = max(dp[i-2] + nums[i], dp[i-1]) 39 | # class Solution: 40 | # def rob(self, nums: List[int]) -> int: 41 | # if not nums: 42 | # return 0 43 | # 44 | # dp = [0] * len(nums) 45 | # dp[0] = nums[0] 46 | # 47 | # for i in range(1, len(nums)): 48 | # dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]) 49 | # 50 | # return dp[-1] 51 | -------------------------------------------------------------------------------- /7-链表/两两交换链表中的节点.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 3 | 4 | 输入:head = [1,2,3,4] 5 | 输出:[2,1,4,3] 6 | 7 | 示例 2: 8 | 输入:head = [] 9 | 输出:[] 10 | 11 | 示例 3: 12 | 输入:head = [1] 13 | 输出:[1] 14 | """ 15 | from typing import Optional 16 | 17 | 18 | # Definition for singly-linked list. 19 | class ListNode: 20 | def __init__(self, val=0, next=None): 21 | self.val = val 22 | self.next = next 23 | 24 | 25 | # https://leetcode.cn/problems/swap-nodes-in-pairs/solutions/2374872/tu-jie-die-dai-di-gui-yi-zhang-tu-miao-d-51ap 26 | class Solution: 27 | def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: 28 | node0 = dummy = ListNode(next=head) 29 | node1 = head 30 | while node1 and node1.next: 31 | node2 = node1.next 32 | node3 = node2.next 33 | 34 | node0.next = node2 35 | node2.next = node1 36 | node1.next = node3 37 | 38 | node0 = node1 39 | node1 = node3 40 | return dummy.next 41 | 42 | 43 | if __name__ == '__main__': 44 | head = ListNode(1, ListNode(2, ListNode(3, ListNode(4)))) 45 | # 我要输出具体的链表的值 46 | while head: 47 | print(head.val) 48 | head = head.next 49 | print(Solution().swapPairs(head)) 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /12-栈/字符串解码.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个经过编码的字符串,返回它解码后的字符串。 3 | 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 4 | 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。 5 | 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。 6 | 7 | 示例 1: 8 | 输入:s = "3[a]2[bc]" 9 | 输出:"aaabcbc" 10 | 11 | 示例 2: 12 | 输入:s = "3[a2[c]]" 13 | 输出:"accaccacc" 14 | 15 | 示例 3: 16 | 输入:s = "2[abc]3[cd]ef" 17 | 输出:"abcabccdcdcdef" 18 | 19 | 示例 4: 20 | 输入:s = "abc3[cd]xyz" 21 | 输出:"abccdcdcdxyz" 22 | """ 23 | 24 | # https://leetcode.cn/problems/decode-string/solutions/19447/decode-string-fu-zhu-zhan-fa-di-gui-fa-by-jyd/?envType=study-plan-v2&envId=top-100-liked 25 | 26 | class Solution: 27 | def decodeString(self, s: str) -> str: 28 | stack = [] 29 | res = "" 30 | multi = 0 31 | 32 | for c in s: 33 | if c == "[": 34 | stack.append([multi, res]) 35 | multi = 0 36 | res = "" 37 | elif c == "]": 38 | last_multi, last_res = stack.pop() 39 | res = last_res + last_multi * res 40 | elif "0 " <= c <= "9": 41 | multi = multi * 10 + int(c) 42 | else: 43 | res += c 44 | 45 | return res 46 | 47 | if __name__ == '__main__': 48 | s = "3[a]2[bc]" 49 | print(Solution().decodeString(s)) 50 | -------------------------------------------------------------------------------- /4-子串/滑动窗口最大值.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 3 | 返回 滑动窗口中的最大值 。 4 | 5 | 示例 1: 6 | 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 7 | 输出:[3,3,5,5,6,7] 8 | 解释: 9 | 滑动窗口的位置 最大值 10 | --------------- ----- 11 | [1 3 -1] -3 5 3 6 7 3 12 | 1 [3 -1 -3] 5 3 6 7 3 13 | 1 3 [-1 -3 5] 3 6 7 5 14 | 1 3 -1 [-3 5 3] 6 7 5 15 | 1 3 -1 -3 [5 3 6] 7 6 16 | 1 3 -1 -3 5 [3 6 7] 7 17 | 示例 2: 18 | 19 | 输入:nums = [1], k = 1 20 | 输出:[1] 21 | """ 22 | 23 | import collections 24 | from typing import List 25 | 26 | 27 | class Solution: 28 | def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: 29 | ans = [] 30 | q = collections.deque() # 双端队列 31 | for i, x in enumerate(nums): 32 | # 1. 入 33 | while q and nums[q[-1]] <= x: 34 | q.pop() # 维护 q 的单调性 35 | q.append(i) # 入队 36 | # 2. 出 37 | if i - q[0] >= k: # 队首已经离开窗口了 38 | q.popleft() 39 | # 3. 记录答案 40 | if i >= k - 1: 41 | # 由于队首到队尾单调递减,所以窗口最大值就是队首 42 | ans.append(nums[q[0]]) # q里面只有最大的元素 43 | return ans 44 | 45 | 46 | if __name__ == "__main__": 47 | Solution().maxSlidingWindow([1, 3, -1, -3, 5, 3, 6, 7], 3) 48 | -------------------------------------------------------------------------------- /9-图论/腐烂的橘子.py: -------------------------------------------------------------------------------- 1 | """ 2 | 在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一: 3 | 4 | 值 0 代表空单元格; 5 | 值 1 代表新鲜橘子; 6 | 值 2 代表腐烂的橘子。 7 | 每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 8 | 9 | 返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。 10 | """ 11 | 12 | # https://leetcode.cn/problems/rotting-oranges/solutions/2773461/duo-yuan-bfsfu-ti-dan-pythonjavacgojsrus-yfmh 13 | 14 | from typing import List 15 | 16 | 17 | class Solution: 18 | def orangesRotting(self, grid: List[List[int]]) -> int: 19 | m = len(grid) 20 | n = len(grid[0]) 21 | fresh = 0 22 | q = [] 23 | 24 | for i in range(m): 25 | for j in range(n): 26 | if grid[i][j] == 1: 27 | fresh += 1 28 | if grid[i][j] == 2: 29 | q.append((i, j)) 30 | 31 | ans = -1 32 | while q: 33 | ans += 1 34 | temp = q 35 | q = [] 36 | for x, y in temp: 37 | for i, j in (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1): 38 | if 0 <= i < m and 0 <= j < n and grid[i][j] == 1: 39 | fresh -= 1 40 | grid[i][j] = 2 41 | q.append((i, j)) 42 | 43 | return -1 if fresh else max(ans, 0) 44 | 45 | 46 | if __name__ == "__main__": 47 | print(Solution().orangesRotting([[2, 1, 1], [1, 1, 0], [0, 1, 1]])) 48 | -------------------------------------------------------------------------------- /10-回溯/组合总和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。 3 | candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 4 | 对于给定的输入,保证和为 target 的不同组合数少于 150 个。 5 | 6 | 示例 1: 7 | 输入:candidates = [2,3,6,7], target = 7 8 | 输出:[[2,2,3],[7]] 9 | 解释: 10 | 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 11 | 7 也是一个候选, 7 = 7 。 12 | 仅有这两种组合。 13 | 14 | 示例 2: 15 | 输入: candidates = [2,3,5], target = 8 16 | 输出: [[2,2,2,2],[2,3,3],[3,5]] 17 | 18 | 示例 3: 19 | 输入: candidates = [2], target = 1 20 | 输出: [] 21 | 22 | """ 23 | from typing import List 24 | 25 | 26 | class Solution: 27 | def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: 28 | path = [] 29 | ans = [] 30 | n = len(candidates) 31 | 32 | def dfs(i, left): 33 | if left == 0: 34 | ans.append(path.copy()) 35 | return 36 | 37 | if i == n or left < 0: 38 | return 39 | 40 | dfs(i + 1, left) 41 | 42 | path.append(candidates[i]) 43 | dfs(i, left - candidates[i]) # 因为题目允许重复使用数字,不需要i+1 44 | path.pop() 45 | 46 | dfs(0, target) 47 | return ans 48 | 49 | if __name__ == '__main__': 50 | candidates = [2, 3, 6, 7] 51 | target = 7 52 | ans = Solution().combinationSum(candidates, target) 53 | print(ans) -------------------------------------------------------------------------------- /3-滑动窗口/找到字符串中所有字母异位词.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。 3 | 异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。 4 | 5 | 示例 1: 6 | 输入: s = "cbaebabacd", p = "abc" 7 | 输出: [0,6] 8 | 解释: 9 | 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。 10 | 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。 11 | 12 | 示例 2: 13 | 输入: s = "abab", p = "ab" 14 | 输出: [0,1,2] 15 | 解释: 16 | 起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。 17 | 起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。 18 | 起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。 19 | """ 20 | from typing import List 21 | 22 | # https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/1123971/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin 23 | class Solution: 24 | def findAnagrams(self, s: str, p: str) -> List[int]: 25 | s_len, p_len = len(s), len(p) 26 | 27 | if s_len < p_len: 28 | return [] 29 | 30 | ans = [] 31 | s_count = [0] * 26 32 | p_count = [0] * 26 33 | for i in range(p_len): 34 | s_count[ord(s[i]) - 97] += 1 35 | p_count[ord(p[i]) - 97] += 1 36 | 37 | if s_count == p_count: 38 | ans.append(0) 39 | 40 | for i in range(s_len - p_len): 41 | s_count[ord(s[i]) - 97] -= 1 42 | s_count[ord(s[i + p_len]) - 97] += 1 43 | 44 | if s_count == p_count: 45 | ans.append(i + 1) 46 | 47 | return ans 48 | 49 | 50 | if __name__ == "__main__": 51 | Solution().findAnagrams("cbaebabacd", "abc") 52 | -------------------------------------------------------------------------------- /7-链表/K个一组翻转列表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。 3 | k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。 4 | 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换 5 | 6 | 输入:head = [1,2,3,4,5], k = 2 7 | 输出:[2,1,4,3,5] 8 | """ 9 | from typing import Optional 10 | 11 | 12 | # Definition for singly-linked list. 13 | class ListNode: 14 | def __init__(self, val=0, next=None): 15 | self.val = val 16 | self.next = next 17 | 18 | 19 | # https://leetcode.cn/problems/reverse-nodes-in-k-group/solutions/1992228/you-xie-cuo-liao-yi-ge-shi-pin-jiang-tou-plfs 20 | class Solution: 21 | def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: 22 | n = 0 23 | cur = head 24 | while cur: 25 | n += 1 26 | cur = cur.next 27 | 28 | p0 = dummy = ListNode(next=head) 29 | cur = head 30 | pre = None 31 | while n >= k: 32 | n -= k 33 | # 对k个数据进行翻转 34 | for _ in range(k): 35 | nxt = cur.next 36 | cur.next = pre 37 | pre = cur 38 | cur = nxt 39 | 40 | nxt = p0.next 41 | nxt.next = cur 42 | p0.next = pre 43 | p0 = nxt 44 | return dummy.next 45 | 46 | 47 | if __name__ == '__main__': 48 | head = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5))))) 49 | print(Solution().reverseKGroup(head, 2)) 50 | -------------------------------------------------------------------------------- /7-链表/排序链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 3 | """ 4 | from typing import Optional 5 | 6 | 7 | # https://leetcode.cn/problems/sort-list/solutions/13728/sort-list-gui-bing-pai-xu-lian-biao-by-jyd 8 | 9 | # Definition for singly-linked list. 10 | class ListNode: 11 | def __init__(self, val=0, next=None): 12 | self.val = val 13 | self.next = next 14 | 15 | 16 | class Solution: 17 | def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: 18 | if not head: 19 | return head 20 | if not head.next: 21 | return head 22 | 23 | slow = head 24 | fast = head.next 25 | while fast and fast.next: 26 | slow = slow.next 27 | fast = fast.next.next 28 | 29 | mid = slow.next 30 | slow.next = None 31 | left = self.sortList(head) 32 | right = self.sortList(mid) 33 | 34 | h = res = ListNode(0) 35 | while left and right: 36 | if left.val < right.val: 37 | h.next, left = left, left.next 38 | else: 39 | h.next, right = right, right.next 40 | h = h.next 41 | h.next = left if left else right 42 | return res.next 43 | 44 | 45 | if __name__ == '__main__': 46 | s = Solution() 47 | result = s.sortList(head=ListNode(4, ListNode(2, ListNode(1, ListNode(3))))) 48 | while result: 49 | print(result.val) 50 | result = result.next 51 | -------------------------------------------------------------------------------- /9-图论/课程表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 3 | 4 | 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 5 | 6 | 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。 7 | 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。 8 | 9 | """ 10 | 11 | # https://leetcode.cn/problems/course-schedule/solutions/18806/course-schedule-tuo-bu-pai-xu-bfsdfsliang-chong-fa 12 | 13 | from collections import deque 14 | from typing import List 15 | 16 | 17 | class Solution: 18 | def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: 19 | indegrees = [0 for _ in range(numCourses)] 20 | adjacency = [[] for _ in range(numCourses)] 21 | queue = deque() 22 | # Get the indegree and adjacency of every course. 23 | for cur, pre in prerequisites: 24 | indegrees[cur] += 1 25 | adjacency[pre].append(cur) 26 | # Get all the courses with the indegree of 0. 27 | for i in range(len(indegrees)): 28 | if not indegrees[i]: queue.append(i) 29 | # BFS TopSort. 30 | while queue: 31 | pre = queue.popleft() 32 | numCourses -= 1 33 | for cur in adjacency[pre]: 34 | indegrees[cur] -= 1 35 | if not indegrees[cur]: queue.append(cur) 36 | return not numCourses 37 | 38 | 39 | if __name__ == "__main__": 40 | Solution().canFinish(numCourses=2, prerequisites=[[1, 0]]) 41 | -------------------------------------------------------------------------------- /9-图论/岛屿数量.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 3 | 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 4 | 此外,你可以假设该网格的四条边均被水包围。 5 | 6 | 示例 1: 7 | 8 | 输入:grid = [ 9 | ["1","1","1","1","0"], 10 | ["1","1","0","1","0"], 11 | ["1","1","0","0","0"], 12 | ["0","0","0","0","0"] 13 | ] 14 | 输出:1 15 | 16 | 示例 2: 17 | 输入:grid = [ 18 | ["1","1","0","0","0"], 19 | ["1","1","0","0","0"], 20 | ["0","0","1","0","0"], 21 | ["0","0","0","1","1"] 22 | ] 23 | 输出:3 24 | """ 25 | 26 | # https://leetcode.cn/problems/number-of-islands/solutions/16884/number-of-islands-shen-du-you-xian-bian-li-dfs-or- 27 | 28 | from typing import List 29 | 30 | 31 | class Solution: 32 | def numIslands(self, grid: List[List[str]]) -> int: 33 | count = 0 34 | 35 | def dfs(grid, i, j): 36 | if not 0 <= i < len(grid) or not 0 <= j < len(grid[0]) or grid[i][j] == "0": 37 | return 38 | grid[i][j] = "0" 39 | dfs(grid, i - 1, j) 40 | dfs(grid, i + 1, j) 41 | dfs(grid, i, j - 1) 42 | dfs(grid, i, j + 1) 43 | 44 | for i in range(len(grid)): 45 | for j in range(len(grid[0])): 46 | if grid[i][j] == "1": 47 | dfs(grid, i, j) 48 | count += 1 49 | return count 50 | 51 | 52 | if __name__ == "__main__": 53 | print(Solution().numIslands( 54 | [["1", "1", "1", "1", "0"], ["1", "1", "0", "1", "0"], ["1", "1", "0", "0", "0"], ["0", "0", "0", "0", "0"]])) 55 | -------------------------------------------------------------------------------- /11-二分查找/搜索旋转排序数组.py: -------------------------------------------------------------------------------- 1 | """ 2 | 整数数组 nums 按升序排列,数组中的值 互不相同 。 3 | 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。 4 | 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。 5 | 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。 6 | 7 | 示例 1: 8 | 输入:nums = [4,5,6,7,0,1,2], target = 0 9 | 输出:4 10 | 11 | 示例 2: 12 | 输入:nums = [4,5,6,7,0,1,2], target = 3 13 | 输出:-1 14 | 15 | 示例 3: 16 | 输入:nums = [1], target = 0 17 | 输出:-1 18 | 19 | """ 20 | # https://leetcode.cn/link/?target=https%3A%2F%2Fwww.bilibili.com%2Fvideo%2FBV1QK411d76w%2F 21 | 22 | from typing import List 23 | 24 | 25 | class Solution: 26 | def search(self, nums: List[int], target: int) -> int: 27 | 28 | def get_index(i): 29 | end = nums[-1] 30 | if nums[i] > end: 31 | return target > end and nums[i] >= target 32 | else: 33 | return target > end or nums[i] >= target 34 | 35 | left = -1 36 | right = len(nums) 37 | 38 | while left + 1 < right: 39 | mid = (left + right) // 2 40 | if get_index(mid): 41 | right = mid 42 | else: 43 | left = mid 44 | 45 | if right == len(nums) or nums[right] != target: 46 | return -1 47 | return right 48 | 49 | 50 | if __name__ == '__main__': 51 | nums = [4, 5, 6, 7, 0, 1, 2] 52 | target = 0 53 | print(Solution().search(nums, target)) 54 | -------------------------------------------------------------------------------- /7-链表/两数相加.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 3 | 请你将两个数相加,并以相同形式返回一个表示和的链表。 4 | 你可以假设除了数字 0 之外,这两个数都不会以 0 开头 5 | 6 | 输入:l1 = [2,4,3], l2 = [5,6,4] 7 | 输出:[7,0,8] 8 | 解释:342 + 465 = 807. 9 | 10 | 示例 2: 11 | 输入:l1 = [0], l2 = [0] 12 | 输出:[0] 13 | 14 | 示例 3: 15 | 输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] 16 | 输出:[8,9,9,9,0,0,0,1] 17 | """ 18 | 19 | # https://leetcode.cn/problems/add-two-numbers/solutions/2327008/dong-hua-jian-ji-xie-fa-cong-di-gui-dao-oe0di 20 | from typing import Optional 21 | 22 | 23 | # Definition for singly-linked list. 24 | class ListNode: 25 | def __init__(self, val=0, next=None): 26 | self.val = val 27 | self.next = next 28 | 29 | 30 | class Solution: 31 | def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: 32 | cur = dummy = ListNode() 33 | carry = 0 34 | while l1 or l2 or carry: 35 | carry += (l1.val if l1 else 0) + (l2.val if l2 else 0) 36 | cur.next = ListNode(carry % 10) 37 | carry //= 10 38 | cur = cur.next 39 | if l1: 40 | l1 = l1.next 41 | if l2: 42 | l2 = l2.next 43 | return dummy.next 44 | 45 | 46 | if __name__ == '__main__': 47 | s = Solution() 48 | l1 = ListNode(2, ListNode(4, ListNode(3))) 49 | l2 = ListNode(5, ListNode(6, ListNode(4))) 50 | print(s.addTwoNumbers(l1, l2)) 51 | l3 = s.addTwoNumbers(l1, l2) 52 | # 返回链表具体的值 53 | while l3: 54 | print(l3.val) 55 | l3 = l3.next 56 | -------------------------------------------------------------------------------- /4-子串/最小覆盖子串.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。 3 | 4 | 注意: 5 | 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。 6 | 如果 s 中存在这样的子串,我们保证它是唯一的答案。 7 | 8 | 示例 1: 9 | 输入:s = "ADOBECODEBANC", t = "ABC" 10 | 输出:"BANC" 11 | 解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。 12 | 13 | 示例 2: 14 | 输入:s = "a", t = "a" 15 | 输出:"a" 16 | 解释:整个字符串 s 是最小覆盖子串。 17 | 18 | 示例 3: 19 | 输入: s = "a", t = "aa" 20 | 输出: "" 21 | 解释: t 中两个字符 'a' 均应包含在 s 的子串中, 22 | 因此没有符合条件的子字符串,返回空字符串。 23 | """ 24 | import collections 25 | 26 | 27 | class Solution: 28 | def minWindow(self, s: str, t: str) -> str: 29 | need = collections.defaultdict(int) 30 | for c in t: 31 | need[c] += 1 32 | needCnt = len(t) 33 | i = 0 34 | res = (0, float('inf')) 35 | for j, c in enumerate(s): 36 | if need[c] > 0: # 37 | needCnt -= 1 38 | need[c] -= 1 39 | if needCnt == 0: # 步骤一:滑动窗口包含了所有T元素 40 | while True: # 步骤二:增加i,排除多余元素 41 | c = s[i] 42 | if need[c] == 0: 43 | break 44 | need[c] += 1 45 | i += 1 46 | if j - i < res[1] - res[0]: # 记录结果 47 | res = (i, j) 48 | need[s[i]] += 1 # 步骤三:i增加一个位置,寻找新的满足条件滑动窗口 49 | needCnt += 1 50 | i += 1 51 | return '' if res[1] > len(s) else s[res[0]:res[1] + 1] # 如果res始终没被更新过,代表无满足条件的结果 52 | 53 | 54 | if __name__ == "__main__": 55 | print(Solution().minWindow("ADOBECODEBANC", "ABC")) 56 | -------------------------------------------------------------------------------- /13-堆/数据流的中位数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。 3 | 4 | 例如 arr = [2,3,4] 的中位数是 3 。 5 | 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。 6 | 实现 MedianFinder 类: 7 | 8 | MedianFinder() 初始化 MedianFinder 对象。 9 | 10 | void addNum(int num) 将数据流中的整数 num 添加到数据结构中。 11 | 12 | double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。 13 | 14 | 示例 1: 15 | 16 | 输入 17 | ["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"] 18 | [[], [1], [2], [], [3], []] 19 | 输出 20 | [null, null, null, 1.5, null, 2.0] 21 | 22 | 解释 23 | MedianFinder medianFinder = new MedianFinder(); 24 | medianFinder.addNum(1); // arr = [1] 25 | medianFinder.addNum(2); // arr = [1, 2] 26 | medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2) 27 | medianFinder.addNum(3); // arr[1, 2, 3] 28 | medianFinder.findMedian(); // return 2.0 29 | """ 30 | 31 | from heapq import * 32 | 33 | 34 | class MedianFinder: 35 | def __init__(self): 36 | self.A = [] # 小顶堆,保存较大的一半 37 | self.B = [] # 大顶堆,保存较小的一半 38 | 39 | def addNum(self, num: int) -> None: 40 | if len(self.A) != len(self.B): 41 | heappush(self.B, -heappushpop(self.A, num)) 42 | else: 43 | heappush(self.A, -heappushpop(self.B, -num)) 44 | 45 | def findMedian(self) -> float: 46 | return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0]) / 2.0 47 | 48 | 49 | # https://leetcode.cn/problems/find-median-from-data-stream/solutions/2361972/295-shu-ju-liu-de-zhong-wei-shu-dui-qing-gmdo/ 50 | 51 | # ["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"] 52 | # [[],[1],[2],[],[3],[]] 53 | # M = MedianFinder() 54 | # M.addNum(1) 55 | # M.addNum(2) 56 | # print(M.findMedian()) 57 | # M.addNum(3) 58 | # print(M.findMedian()) 59 | 60 | if __name__ == '__main__': 61 | M = MedianFinder() 62 | M.addNum(1) 63 | M.addNum(2) 64 | print(M.findMedian()) 65 | M.addNum(3) 66 | print(M.findMedian()) 67 | -------------------------------------------------------------------------------- /7-链表/随机链表的复制.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 3 | 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。 4 | 5 | 例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。 6 | 返回复制链表的头节点。 7 | 用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示: 8 | 9 | val:一个表示 Node.val 的整数。 10 | random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。 11 | 你的代码 只 接受原链表的头节点 head 作为传入参数。 12 | """ 13 | 14 | # https://leetcode.cn/problems/copy-list-with-random-pointer/solutions/2361362/138-fu-zhi-dai-sui-ji-zhi-zhen-de-lian-b-6jeo 15 | 16 | from typing import Optional 17 | 18 | 19 | # Definition for a Node. 20 | class Node: 21 | def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): 22 | self.val = int(x) 23 | self.next = next 24 | self.random = random 25 | 26 | 27 | class Solution: 28 | def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': 29 | if not head: 30 | return 31 | 32 | dic = {} 33 | cur = head 34 | while cur: 35 | dic[cur] = Node(x=cur.val) 36 | cur = cur.next 37 | 38 | cur = head 39 | while cur: 40 | dic[cur].next = dic.get(cur.next) 41 | dic[cur].random = dic.get(cur.random) 42 | cur = cur.next 43 | return dic[head] 44 | 45 | 46 | if __name__ == '__main__': 47 | s = Solution() 48 | head = Node(7) 49 | head.next = Node(13) 50 | head.next.next = Node(11) 51 | head.next.next.next = Node(10) 52 | head.next.next.next.next = Node(1) 53 | head.random = head.next.next.next 54 | head.next.random = head 55 | head.next.next.random = head.next.next.next.next 56 | head.next.next.next.random = head.next 57 | head.next.next.next.next.random = head 58 | result = s.copyRandomList(head) 59 | while result: 60 | print(result.val) 61 | result = result.next 62 | -------------------------------------------------------------------------------- /2-双指针/三数之和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。 3 | 注意:答案中不可以包含重复的三元组。 4 | 5 | 示例 1: 6 | 输入:nums = [-1,0,1,2,-1,-4] 7 | 输出:[[-1,-1,2],[-1,0,1]] 8 | 解释: 9 | nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 10 | nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 11 | nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 12 | 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 13 | 注意,输出的顺序和三元组的顺序并不重要。 14 | 15 | 示例 2: 16 | 输入:nums = [0,1,1] 17 | 输出:[] 18 | 解释:唯一可能的三元组和不为 0 。 19 | 20 | 示例 3: 21 | 输入:nums = [0,0,0] 22 | 输出:[[0,0,0]] 23 | 解释:唯一可能的三元组和为 0 。 24 | """ 25 | 26 | from typing import List 27 | 28 | 29 | # https://leetcode.cn/problems/3sum/solutions/1968332/shuang-zhi-zhen-xiang-bu-ming-bai-yi-ge-pno55 30 | class Solution: 31 | def threeSum(self, nums: [int]) -> [[int]]: 32 | nums.sort() 33 | res, k = [], 0 34 | for k in range(len(nums) - 2): 35 | if nums[k] > 0: 36 | break # 1. because of j > i > k. 37 | if k > 0 and nums[k] == nums[k - 1]: 38 | continue # 2. skip the same `nums[k]`. 39 | i, j = k + 1, len(nums) - 1 40 | while i < j: # 3. double pointer 41 | s = nums[k] + nums[i] + nums[j] 42 | if s < 0: 43 | i += 1 44 | while i < j and nums[i] == nums[i - 1]: 45 | i += 1 46 | elif s > 0: 47 | j -= 1 48 | while i < j and nums[j] == nums[j + 1]: 49 | j -= 1 50 | else: 51 | res.append([nums[k], nums[i], nums[j]]) 52 | i += 1 53 | j -= 1 54 | while i < j and nums[i] == nums[i - 1]: 55 | i += 1 56 | while i < j and nums[j] == nums[j + 1]: 57 | j -= 1 58 | return res 59 | 60 | 61 | if __name__ == "__main__": 62 | Solution().threeSum( 63 | [-1, 0, 1, 2, -1, -4] 64 | ) 65 | -------------------------------------------------------------------------------- /7-链表/合并k个升序链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个链表数组,每个链表都已经按升序排列。 3 | 请你将所有链表合并到一个升序链表中,返回合并后的链表。 4 | 5 | 示例 1: 6 | 输入:lists = [[1,4,5],[1,3,4],[2,6]] 7 | 输出:[1,1,2,3,4,4,5,6] 8 | 解释:链表数组如下: 9 | [ 10 | 1->4->5, 11 | 1->3->4, 12 | 2->6 13 | ] 14 | 将它们合并到一个有序链表中得到。 15 | 1->1->2->3->4->4->5->6 16 | 17 | 示例 2: 18 | 输入:lists = [] 19 | 输出:[] 20 | 21 | 示例 3: 22 | 输入:lists = [[]] 23 | 输出:[] 24 | """ 25 | 26 | from typing import Optional, List 27 | 28 | 29 | # Definition for singly-linked list. 30 | class ListNode: 31 | def __init__(self, val=0, next=None): 32 | self.val = val 33 | self.next = next 34 | 35 | 36 | # https://leetcode.cn/problems/merge-k-sorted-lists/solutions/2384305/liang-chong-fang-fa-zui-xiao-dui-fen-zhi-zbzx 37 | class Solution: 38 | # 21. 合并两个有序链表 39 | def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: 40 | cur = dummy = ListNode() # 用哨兵节点简化代码逻辑 41 | while list1 and list2: 42 | if list1.val < list2.val: 43 | cur.next = list1 # 把 list1 加到新链表中 44 | list1 = list1.next 45 | else: # 注:相等的情况加哪个节点都是可以的 46 | cur.next = list2 # 把 list2 加到新链表中 47 | list2 = list2.next 48 | cur = cur.next 49 | cur.next = list1 if list1 else list2 # 拼接剩余链表 50 | return dummy.next 51 | 52 | def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: 53 | m = len(lists) 54 | if m == 0: 55 | return None # 注意输入的 lists 可能是空的 56 | if m == 1: 57 | return lists[0] # 无需合并,直接返回 58 | left = self.mergeKLists(lists[:m // 2]) # 合并左半部分 59 | right = self.mergeKLists(lists[m // 2:]) # 合并右半部分 60 | return self.mergeTwoLists(left, right) # 最后把左半和右半合并 61 | 62 | if __name__ == '__main__': 63 | # 注意输入是lists: List[Optional[ListNode]] 64 | sol = Solution() 65 | list1 = ListNode(1, ListNode(4, ListNode(5))) 66 | list2 = ListNode(1, ListNode(3, ListNode(4))) 67 | list3 = ListNode(2, ListNode(6)) 68 | lists = [list1, list2, list3] 69 | merged = sol.mergeKLists(lists) 70 | while merged: 71 | print(merged.val) 72 | merged = merged.next 73 | 74 | 75 | -------------------------------------------------------------------------------- /7-链表/LRU缓存.py: -------------------------------------------------------------------------------- 1 | """ 2 | 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 3 | 实现 LRUCache 类: 4 | LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 5 | int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 6 | void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。 7 | 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。 8 | """ 9 | 10 | 11 | class Node: 12 | def __init__(self, key=0, value=0): 13 | self.key = key 14 | self.value = value 15 | 16 | 17 | class LRUCache: 18 | 19 | def __init__(self, capacity: int): 20 | self.capacity = capacity 21 | self.dummy = Node() 22 | self.dummy.prev = self.dummy 23 | self.dummy.next = self.dummy 24 | self.key_to_node = dict() 25 | 26 | def get_node(self, key): 27 | if key not in self.key_to_node: 28 | return None 29 | node = self.key_to_node[key] 30 | self.remove(node) 31 | self.push_front(node) 32 | return node 33 | 34 | def remove(self, x: Node): 35 | x.prev.next = x.next 36 | x.next.prev = x.prev 37 | 38 | def push_front(self, x: Node): 39 | x.prev = self.dummy 40 | x.next = self.dummy.next 41 | x.prev.next = x 42 | x.next.prev = x 43 | 44 | def get(self, key: int) -> int: 45 | node = self.get_node(key) 46 | return node.value if node else -1 47 | 48 | def put(self, key: int, value: int) -> None: 49 | node = self.get_node(key) 50 | if node: 51 | node.value = value 52 | return 53 | self.key_to_node[key] = node = Node(key, value) 54 | self.push_front(node) 55 | if len(self.key_to_node) > self.capacity: 56 | back_node = self.dummy.prev 57 | del self.key_to_node[back_node.key] 58 | self.remove(back_node) 59 | 60 | 61 | # Your LRUCache object will be instantiated and called as such: 62 | # obj = LRUCache(capacity) 63 | # param_1 = obj.get(key) 64 | # obj.put(key,value) 65 | 66 | if __name__ == '__main__': 67 | obj = LRUCache(2) 68 | obj.put(1, 1) 69 | obj.put(2, 2) 70 | print(obj.get(1)) 71 | obj.put(3, 3) 72 | print(obj.get(2)) 73 | obj.put(4, 4) 74 | -------------------------------------------------------------------------------- /16-多维动态规划/最长回文子串.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给你一个字符串 s,找到 s 中最长的 3 | 回文子串。 4 | 5 | 示例 1: 6 | 输入:s = "babad" 7 | 输出:"bab" 8 | 解释:"aba" 同样是符合题意的答案。 9 | 10 | 示例 2: 11 | 输入:s = "cbbd" 12 | 输出:"bb" 13 | 14 | """ 15 | 16 | 17 | 18 | # class Solution(object): 19 | # def longestPalindrome(self, s): 20 | # res = '' 21 | # for i in range(len(s)): 22 | # start = max(i - len(res) -1, 0) 23 | # temp = s[start: i+1] 24 | # if temp == temp[::-1]: 25 | # res = temp 26 | # else: 27 | # temp = temp[1:] 28 | # if temp == temp[::-1]: 29 | # res = temp 30 | # return res 31 | 32 | 33 | # 链接:https://leetcode.cn/problems/longest-palindromic-substring/solutions/1601761/by-yu-chen-gg-kz5y/ 34 | 35 | 36 | class Solution: 37 | def longestPalindrome(self, s: str) -> str: 38 | 39 | size = len(s) 40 | # 特殊处理 41 | if size == 1: 42 | return s 43 | # 创建动态规划dynamic programing表 44 | dp = [[False for _ in range(size)] for _ in range(size)] 45 | # 初始长度为1,这样万一不存在回文,就返回第一个值(初始条件设置的时候一定要考虑输出) 46 | max_len = 1 47 | start = 0 48 | for j in range(1, size): 49 | for i in range(j): 50 | # 边界条件: 51 | # 只要头尾相等(s[i]==s[j])就能返回True 52 | if j - i <= 2: 53 | if s[i] == s[j]: 54 | dp[i][j] = True 55 | cur = j - i + 1 56 | # 状态转移方程 57 | # 当前dp[i][j]状态:头尾相等(s[i]==s[j]) 58 | # 过去dp[i][j]状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True) 59 | else: 60 | if s[i] == s[j] and dp[i + 1][j - 1]: 61 | dp[i][j] = True 62 | cur_len = j - i + 1 63 | # 出现回文更新输出 64 | if dp[i][j]: 65 | if cur_len > max_len: 66 | max_len = cur_len 67 | start = i 68 | 69 | return s[start:start + max_len] 70 | 71 | 72 | 73 | # 链接:https: // leetcode.cn / problems / longest - palindromic - substring / solutions / 662473 / 5 - zui - chang - hui - wen - zi - chuan - dong - tai - gu - p7uk / 74 | 75 | if __name__ == "__main__": 76 | print(Solution().longestPalindrome("babad")) -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | 做题思路: 2 | 3 | 1.直接看题解 4 | 2.本地快速实现 5 | 3.写一遍 6 | 7 | 8 | 106版本: 9 | 先过一遍,整体过一遍,不细看 10 | 11 | 12 | 1017版本: 13 | 总结规律 14 | 两数之和:dict 15 | 字母异位词分组:defaultdict(list) 16 | 最长连续序列:set 主要是o(1) 17 | 18 | 移动零:和双指针无关,非零的前移,剩下来的都是0 19 | 三数之和:while->left/right 20 | 21 | 找到字符串中所有字母异位词:滑动窗口内的计数相同,则异位 22 | 无重复字符的最长子串:set做一个小窗口 23 | 24 | 和为k的子数组:累加的数减去k只要能等于之前的累计和,一定存在这么一个数,从这个数到目前的节点和等于k 25 | 26 | 合并区间:sort、for、区间边缘值对比,ans中的-1、1和p对比 27 | 最大子数组和:for,维护一个min,算差值 28 | 29 | 搜索二维矩阵II:矩阵从左到右升序,从上到下升序,j是列,i是行,i往下走,j往左走 30 | 旋转图像:两层循环,i/j,右下角往左上角移动,以此循环 31 | 矩阵置零:找到等于0的,先把行置零,在循环零的行,把列置零 32 | 33 | 反转链表:while,tmp,然后把cur的下一个节点给到None,在cur和pre同步往前走 34 | 合并两个有序链表:递归,一个val小于另一个val,就递归 35 | 回文链表:找个[]存下来所有的数在判断 36 | 环形链表:快慢指针,当快指针遇到慢指针的时候 37 | 相交链表:while,A往后跑,跑完等于B,反之一样 38 | 两两交换链表中的节点:2到0,2到1,1到3,while 39 | 两数相加:while l1 or l2,持续l1.val+l2.val,一直加 40 | 删除链表的倒数第N个节点:快节点跑N,慢节点再走,快慢节点相差N,则快节点到终点,慢节点则是倒数第N 41 | 环形链接II:慢节点和head节点一起走,慢节点等于head节点,换节点 42 | 随机链表的复制:dict存一个node,先把node关系绑定,再把next和random绑上 43 | 44 | 二叉树的中序遍历:判定条件+递归 45 | 二叉树的最大深度:递归,max 46 | 二叉树的直径:dfs,left+right节点 47 | 对称二叉树:dfs,left.val==right.val 48 | 将有序数组组成平衡二叉搜索树:dfs,start/end,中间拉起来,两边递归成树 49 | 翻转二叉树:递归,left和right互换 50 | 二叉搜索树种的第k小的元素:二叉搜索树中序遍历为递归的,dfs中对k-=1 51 | 二叉树展开为链表:ans=[],dfs之后存下来,while ans,left=None,right=ans.pop(0) 52 | 二叉树的右视图:dfs,depth == len(ans),前序遍历 53 | 二叉树的层序遍历:while,每次都把一个节点的值存储起来再循环 54 | 二叉树的最近公共祖先:递归,有谁在就返回谁 55 | 从前序与中序遍历序列构造二叉树:前序:根左右,中序:左根右;找到根之后递归左右 56 | 验证二叉搜索树:left