├── .idea ├── .gitignore ├── Leetcode.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── 1-树 ├── 中序 │ ├── Leetcode230. 二叉搜索树中第K小的元素(28).py │ ├── Leetcode426. 将二叉搜索树转化为排序的双向链表(49).py │ ├── Leetcode94. 二叉树的中序遍历(108).py │ ├── Leetcode98. 验证二叉搜索树(65).py │ ├── Leetcode99. 恢复二叉搜索树(9).py │ └── 剑指 Offer 54. 二叉搜索树的第k大节点(37).py ├── 其他 │ ├── Leetcode105. 从前序与中序遍历序列构造二叉树(75).py │ ├── Leetcode106. 从中序与后序遍历序列构造二叉树(22).py │ ├── Leetcode222. 完全二叉树的节点个数(12).py │ ├── Leetcode235. 二叉搜索树的最近公共祖先(6).py │ ├── Leetcode297. 二叉树的序列化与反序列化(40).py │ ├── Leetcode450. 删除二叉搜索树中的节点(26).py │ ├── Leetcode572. 另一棵树的子树(25).py │ ├── Leetcode剑指 Offer 33. 二叉搜索树的后序遍历序列(14).py │ └── 剑指 Offer 26. 树的子结构(16).py ├── 前序 │ ├── Leetcode104. 二叉树的最大深度(68).py │ ├── Leetcode108. 将有序数组转换为二叉搜索树(12).py │ ├── Leetcode109. 有序链表转换二叉搜索树(9).py │ ├── Leetcode112. 路径总和(58).py │ ├── Leetcode113. 路径总和 II(65).py │ ├── Leetcode129. 求根节点到叶节点数字之和(69).py │ ├── Leetcode144. 二叉树的前序遍历(76).py │ ├── Leetcode257. 二叉树的所有路径(12).py │ ├── Leetcode437. 路径总和 III(10).py │ ├── Leetcode617. 合并二叉树(10).py │ └── Leetcode863. 二叉树中所有距离为 K 的结点(12).py ├── 后序 │ ├── Leetcode110. 平衡二叉树(68).py │ ├── Leetcode124. 二叉树中的最大路径和(110).py │ ├── Leetcode145. 二叉树的后序遍历(33).py │ ├── Leetcode226. 翻转二叉树(53).py │ ├── Leetcode236. 二叉树的最近公共祖先(164).py │ └── Leetcode543. 二叉树的直径(65).py ├── 层次 │ ├── Leetcode101. 对称二叉树(65).py │ ├── Leetcode102. 二叉树的层序遍历(184).py │ ├── Leetcode103. 二叉树的锯齿形层序遍历(103).py │ ├── Leetcode199. 二叉树的右视图(104).py │ ├── Leetcode662. 二叉树最大宽度(44).py │ └── Leetcode958. 二叉树的完全性检验(37).py └── 树专题.ipynb ├── 10-其他 ├── 10-1-Hash │ ├── Leetcode1. 两数之和(195).py │ ├── Leetcode12. 整数转罗马数字(10).py │ ├── Leetcode560. 和为 K 的子数组(35).py │ └── Leetcode974. 和可被 K 整除的子数组(9).py ├── 10-10-其他题 │ ├── Leetcode169. 多数元素(59).py │ ├── Leetcode31. 下一个排列(82).py │ └── Leetcode556. 下一个更大元素 III(16).py ├── 10-2-原地Hash │ ├── Leetcode268. 丢失的数字(268).py │ ├── Leetcode287. 寻找重复数(22).py │ ├── Leetcode41. 缺失的第一个正数(84).py │ ├── Leetcode442. 数组中重复的数据(19).py │ └── 剑指 Offer 03. 数组中重复的数字(13).py ├── 10-3-双指针 │ ├── Leetcode11. 盛最多水的容器(27).py │ ├── Leetcode125. 验证回文串(26).py │ ├── Leetcode15. 三数之和(232).py │ ├── Leetcode16. 最接近的三数之和(21).py │ ├── Leetcode18. 四数之和(14).py │ ├── Leetcode240. 搜索二维矩阵 II(50).py │ ├── Leetcode26. 删除有序数组中的重复项(27).py │ ├── Leetcode283. 移动零(39).py │ ├── Leetcode680. 验证回文字符串 Ⅱ(10).py │ └── Leetcode75. 颜色分类(27).py ├── 10-4-滑动窗口 │ ├── Leetcode1004. 最大连续1的个数 III(15).py │ ├── Leetcode209. 长度最小的子数组(40).py │ ├── Leetcode28. 实现 strStr()(10).py │ ├── Leetcode3. 无重复字符的最长子串(438).py │ └── Leetcode76. 最小覆盖子串(72).py ├── 10-5-字符串 │ ├── Leetcode14. 最长公共前缀(52).py │ ├── Leetcode151. 颠倒字符串中的单词(76).py │ ├── Leetcode165. 比较版本号(64).py │ ├── Leetcode344. 反转字符串(16).py │ ├── Leetcode415. 字符串相加(138).py │ ├── Leetcode43. 字符串相乘(65).py │ ├── Leetcode443. 压缩字符串(17).py │ ├── Leetcode468. 验证IP地址(42).py │ ├── Leetcode557. 反转字符串中的单词 III(17).py │ └── Leetcode6. Z 字形变换(12).py ├── 10-6-模拟 │ ├── 一维模拟 │ │ ├── Leetcode128. 最长连续序列(49).py │ │ ├── Leetcode217. 存在重复元素(3).py │ │ ├── Leetcode238. 除自身以外数组的乘积(7).py │ │ ├── Leetcode657. 机器人能否返回原点.py │ │ ├── Leetcode88. 合并两个有序数组(165).py │ │ ├── Leetcode9. 回文数(22).py │ │ └── Leetcode剑指 Offer 61. 扑克牌中的顺子(17).py │ ├── 二维模拟 │ │ ├── Leetcode48. 旋转图像(58).py │ │ ├── Leetcode54. 螺旋矩阵(136).py │ │ ├── Leetcode59. 螺旋矩阵 II(31).py │ │ └── Leetcode73. 矩阵置零(10).py │ └── 数字模拟 │ │ ├── Leetcode166. 分数到小数(11).py │ │ ├── Leetcode171. 26进制数(12).py │ │ ├── Leetcode231. 2 的幂(7).py │ │ ├── Leetcode7. 整数反转(30).py │ │ ├── Leetcode8. 字符串转换整数 (atoi)(88).py │ │ └── Leetcode89. 格雷编码(5).py ├── 10-7-位运算 │ ├── Leetcode136. 只出现一次的数字(42).py │ ├── Leetcode260. 只出现一次的数字 III(13).py │ ├── 剑指 Offer 15. 二进制中1的个数.py │ └── 笔记.md ├── 10-8-数学题 │ ├── Leetcode172. 阶乘后的零(10).py │ ├── Leetcode292. Nim 游戏(2).py │ ├── Leetcode470. 用 Rand7() 实现 Rand10()(61).py │ └── Leetcode50. Pow(x, n)(31).py └── 10-9-设计题 │ ├── Leetcode208. 实现前缀树(22).py │ └── Leetcode706. 设计哈希映射(10).py ├── 2-回溯、DFS、BFS ├── 普通回溯 │ ├── Leetcode22. 括号生成(82).py │ └── Leetcode面试题 08.06. 汉诺塔问题(9).py ├── 矩阵回溯 │ ├── Leetcode130. 被围绕的区域(12).py │ ├── Leetcode200. 岛屿数量(161).py │ ├── Leetcode329. 矩阵中的最长递增路径(26).py │ ├── Leetcode51. N 皇后(14).py │ ├── Leetcode695. 岛屿的最大面积(47).py │ └── Leetcode79. 单词搜索(35).py └── 组合、排列、子集、分割 │ ├── Leetcode131. 分割回文串(9).py │ ├── Leetcode17. 电话号码的字母组合(17).py │ ├── Leetcode39. 组合总和(59).py │ ├── Leetcode40. 组合总和 II(28).py │ ├── Leetcode46. 全排列(146).py │ ├── Leetcode47.全排列 II(32).py │ ├── Leetcode77. 组合(11).py │ ├── Leetcode78. 子集(62).py │ ├── Leetcode90.子集 II(3).py │ ├── Leetcode93. 复原 IP 地址(77).py │ └── 剑指 Offer 38. 字符串的排列(11).py ├── 3-二分查找 ├── 二分模板.py ├── 旋转数组 │ ├── Leetcode153. 寻找旋转排序数组中的最小值(46).py │ ├── Leetcode154. 寻找旋转排序数组中的最小值 II(12).py │ ├── Leetcode33. 搜索旋转排序数组(166).py │ ├── Leetcode81. 搜索旋转排序数组 II(2).py │ └── 面试题 10.03. 搜索旋转数组(11).py └── 普通二分 │ ├── Leetcode189. 轮转数组(23).py │ ├── Leetcode34. 在排序数组中查找元素的第一个和最后一个位置(53).py │ ├── Leetcode4. 寻找两个正序数组的中位数(96).py │ ├── Leetcode611. 有效三角形的个数(17).py │ ├── Leetcode69. x 的平方根(93).py │ └── Leetcode704. 二分查找(108).py ├── 4-栈和队列 ├── 单调栈和队列 │ ├── Leetcde503. 下一个更大元素 II(16).py │ ├── Leetcode239. 滑动窗口最大值(74).py │ ├── Leetcode42.接雨水(120).py │ ├── Leetcode496. 下一个更大元素 I(1).py │ ├── Leetcode739. 每日温度(33).py │ ├── Leetcode84. 柱状图中最大的矩形(13).py │ └── Leetcode85. 最大矩形(14).py └── 普通栈和队列 │ ├── Leetcode155. 最小栈(67).py │ ├── Leetcode20. 有效的括号(172).py │ ├── Leetcode227. 基本计算器 II(44).py │ ├── Leetcode232. 用栈实现队列(104).py │ ├── Leetcode394. 字符串解码(47).py │ ├── Leetcode402. 移掉 K 位数字(38).py │ ├── Leetcode622. 设计循环队列(11).py │ ├── Leetcode678. 有效的括号字符串(19).py │ ├── Leetcode71. 简化路径(19).py │ └── 剑指 Offer 09. 用两个栈实现队列(37).py ├── 5-贪心 ├── 两个维度 │ ├── Leetcode135. 分发糖果(28).py │ └── Leetcode406. 根据身高重建队列(3).py ├── 区间贪心 │ ├── Leetcode435. 无重叠区间(6).py │ ├── Leetcode45. 跳跃游戏 II(22).py │ ├── Leetcode452. 用最少数量的箭引爆气球(6).py │ ├── Leetcode55. 跳跃游戏(30).py │ └── Leetcode56. 合并区间(99).py ├── 复杂贪心 │ ├── Leetcode134. 加油站(17).py │ └── Leetcode253. 会议室 II(14).py └── 简单常识题 │ ├── Leetcode1005. K 次取反后最大化的数组和.py │ ├── Leetcode162. 寻找峰值(52).py │ ├── Leetcode376. 摆动序列(3).py │ ├── Leetcode455. 分发饼干(0).py │ └── Leetcode860. 柠檬水找零(1).py ├── 6-动态规划 ├── 动态规划笔记.ipynb ├── 单串 │ ├── Leetcode139. 单词拆分(37).py │ ├── Leetcode152. 乘积最大子数组(45).py │ ├── Leetcode300. 最长递增子序列长度(123).py │ ├── Leetcode376. 摆动序列(3).py │ ├── Leetcode5. 找到最长回文子串(161).py │ ├── Leetcode516. 最长回文子序列长度(15).py │ ├── Leetcode53. 最大子数组和(211).py │ ├── Leetcode647. 回文子串个数(10).py │ ├── Leetcode673. 最长递增子序列的个数(19).py │ ├── Leetcode674. 最长连续递增子序列长度(11).py │ └── 找到最长的递增子序列.py ├── 双串 │ ├── Leetcode1143. 最长公共子序列(80).py │ ├── Leetcode115. 不同的子序列(14).py │ ├── Leetcode718. 最长重复子数组(57).py │ ├── Leetcode72. 编辑距离(93).py │ └── Leetcode97. 交错字符串(14).py ├── 普通动归 │ ├── Leetcode264. 丑数 II(14).py │ ├── Leetcode32. 最长有效括号(65).py │ ├── Leetcode343. 整数拆分(14).py │ ├── Leetcode70. 爬楼梯(100).py │ ├── Leetcode96. 不同的二叉搜索树(20).py │ └── Leetcode剑指 Offer 46. 把数字翻译成字符串(11).py └── 经典题型 │ ├── 买卖股票 │ ├── Leetcode121. 买卖股票的最佳时机(173).py │ ├── Leetcode122. 买卖股票的最佳时机 II(44).py │ ├── Leetcode123. 买卖股票的最佳时机 III(29).py │ └── Leetcode188. 买卖股票的最佳时机 IV(11).py │ ├── 打家劫舍 │ ├── Leetcode198. 打家劫舍(43).py │ ├── Leetcode213. 打家劫舍 II(20).py │ └── Leetcode337. 打家劫舍 III(11).py │ ├── 背包问题 │ ├── 0-1背包 │ │ ├── 0-1背包.py │ │ ├── Leetcode1049. 最后一块石头的重量 II(1).py │ │ ├── Leetcode416. 分割等和子集(11).py │ │ └── Leetcode494. 目标和(13).py │ ├── 完全背包 │ │ ├── Leetcode279. 完全平方数(13).py │ │ ├── Leetcode322. 零钱兑换(65).py │ │ └── Leetcode518. 零钱兑换 II(32).py │ └── 背包问题.ipynb │ └── 路径问题 │ ├── Leetcode120. 三角形最小路径和(21).py │ ├── Leetcode221. 最大正方形(51).py │ ├── Leetcode62. 不同路径(50).py │ ├── Leetcode63. 不同路径 II(16).py │ └── Leetcode64. 最小路径和(62).py ├── 7-链表 ├── Leetcode114. 二叉树展开为链表(24).py ├── Leetcode138. 复制带随机指针的链表(40).py ├── Leetcode141. 环形链表(172).py ├── Leetcode142. 环形链表 II(139).py ├── Leetcode143. 重排链表(112).py ├── Leetcode146. LRU缓存机制(396).py ├── Leetcode160. 相交链表(160).py ├── Leetcode19. 删除链表的倒数第 N 个结点(100).py ├── Leetcode2. 两数相加(91).py ├── Leetcode206. 反转链表(487).py ├── Leetcode21. 合并两个有序链表(200).py ├── Leetcode23. 合并K个升序链表(140).py ├── Leetcode234. 回文链表(61).py ├── Leetcode237. 删除链表中的节点(3).py ├── Leetcode24. 两两交换链表中的节点(45).py ├── Leetcode25. K 个一组翻转链表(249).py ├── Leetcode328. 奇偶链表(26).py ├── Leetcode445. 两数相加 II(24).py ├── Leetcode61. 旋转链表(30).py ├── Leetcode82. 删除排序链表中的重复元素 II(93).py ├── Leetcode83. 删除排序链表中的重复元素(50).py ├── Leetcode86. 分隔链表(18).py ├── Leetcode92. 反转链表 II(134).py ├── 剑指 Offer 22. 链表中倒数第k个节点(88).py └── 链表模板.py ├── 8-排序 ├── Leetcode148. 排序链表(92).py ├── Leetcode179. 最大数(43).py ├── Leetcode215. 数组中的第K个最大元素(327).py ├── Leetcode347. 前 K 个高频元素(18).py ├── Leetcode912. 排序数组(216).py ├── Leetcode剑指 Offer 45. 把数组排成最小的数(17).py └── 排序模板.py ├── 9-图 ├── Dijkstra.py ├── Leetcode207. 课程表(36).py ├── Leetcode210. 课程表 II(16).py ├── Leetcode547. 省份数量(11).py └── Leetcode841. 钥匙和房间.py └── README.md /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/Leetcode.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /1-树/中序/Leetcode230. 二叉搜索树中第K小的元素(28).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode: 3 | def __init__(self, val=0, left=None, right=None): 4 | self.val = val 5 | self.left = left 6 | self.right = right 7 | class Solution: 8 | def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: 9 | stack, cur = [], root 10 | while stack or cur: 11 | while cur: 12 | stack.append(cur) 13 | cur = cur.left 14 | cur = stack.pop() 15 | if k == 1: 16 | return cur.val 17 | k -= 1 -------------------------------------------------------------------------------- /1-树/中序/Leetcode426. 将二叉搜索树转化为排序的双向链表(49).py: -------------------------------------------------------------------------------- 1 | """ 2 | # Definition for a Node. 3 | class Node: 4 | def __init__(self, val, left=None, right=None): 5 | self.val = val 6 | self.left = left 7 | self.right = right 8 | """ 9 | class Solution: 10 | def treeToDoublyList(self, root: 'Node') -> 'Node': 11 | # left=next 12 | if not root: return root 13 | stack, cur = [], root 14 | pre = None 15 | while stack or cur: 16 | while cur: 17 | stack.append(cur) 18 | cur = cur.left 19 | cur = stack.pop() 20 | if pre == None: head = cur 21 | else: 22 | pre.right = cur 23 | cur.left = pre 24 | pre = cur 25 | cur = cur.right 26 | # pre = last node 27 | pre.right = head 28 | head.left = pre 29 | return head -------------------------------------------------------------------------------- /1-树/中序/Leetcode94. 二叉树的中序遍历(108).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 11:22 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | class TreeNode: 8 | def __init__(self, val=0, left=None, right=None): 9 | self.val = val 10 | self.left = left 11 | self.right = right 12 | class Solution: 13 | def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 14 | # 一、递归 15 | res = [] 16 | def dfs(root): 17 | if not root: return 18 | dfs(root.left) 19 | res.append(root.val) 20 | dfs(root.right) 21 | dfs(root) 22 | return res 23 | 24 | # 二、迭代 25 | res = [] 26 | cur, stack = root, [] 27 | while cur or stack: 28 | while cur: 29 | stack.append(cur) 30 | cur = cur.left 31 | cur = stack.pop() 32 | res.append(cur.val) 33 | cur = cur.right 34 | return res -------------------------------------------------------------------------------- /1-树/中序/Leetcode98. 验证二叉搜索树(65).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode: 3 | def __init__(self, val=0, left=None, right=None): 4 | self.val = val 5 | self.left = left 6 | self.right = right 7 | class Solution: 8 | def isValidBST(self, root: Optional[TreeNode]) -> bool: 9 | if not root: return False 10 | stack, cur = [], root 11 | pre_val = float('-inf') 12 | while stack or cur: 13 | while cur: 14 | stack.append(cur) 15 | cur = cur.left 16 | cur = stack.pop() 17 | if cur.val <= pre_val: return False 18 | pre_val = cur.val 19 | cur = cur.right 20 | return True -------------------------------------------------------------------------------- /1-树/中序/Leetcode99. 恢复二叉搜索树(9).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def recoverTree(self, root: Optional[TreeNode]) -> None: 9 | """ 10 | Do not return anything, modify root in-place instead. 11 | """ 12 | stack = [] 13 | cur = root 14 | pre = TreeNode(float('-inf')) 15 | firstNode, secondNode = None, None 16 | while stack or cur: 17 | while cur: 18 | stack.append(cur) 19 | cur = cur.left 20 | cur = stack.pop() 21 | if not firstNode and pre.val > cur.val: 22 | # 第一个错的 取前一个节点 32 23 | firstNode = pre 24 | if firstNode and pre.val > cur.val: 25 | # 第二个错的 取后一个节点 21 26 | secondNode = cur 27 | pre = cur 28 | cur = cur.right 29 | if firstNode and secondNode: 30 | firstNode.val, secondNode.val = secondNode.val, firstNode.val -------------------------------------------------------------------------------- /1-树/中序/剑指 Offer 54. 二叉搜索树的第k大节点(37).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | 8 | class Solution: 9 | def kthLargest(self, root: TreeNode, k: int) -> int: 10 | stack, cur = [], root 11 | while stack or cur: 12 | while cur: 13 | stack.append(cur) 14 | cur = cur.right 15 | cur = stack.pop() 16 | k -= 1 17 | if k == 0: return cur.val 18 | cur = cur.left -------------------------------------------------------------------------------- /1-树/其他/Leetcode105. 从前序与中序遍历序列构造二叉树(75).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode: 3 | def __init__(self, val=0, left=None, right=None): 4 | self.val = val 5 | self.left = left 6 | self.right = right 7 | class Solution: 8 | def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: 9 | if not preorder or not inorder: return 10 | root = TreeNode(preorder[0]) 11 | idx = inorder.index(preorder[0]) 12 | root.left = self.buildTree(preorder[1:idx+1], inorder[:idx]) 13 | root.right =self.buildTree(preorder[idx+1:], inorder[idx+1:]) 14 | return root -------------------------------------------------------------------------------- /1-树/其他/Leetcode106. 从中序与后序遍历序列构造二叉树(22).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: 9 | # 中: 左 根 右 10 | # 后: 左 右 根 11 | if not inorder or not postorder: return 12 | root = TreeNode(postorder[-1]) 13 | idx = inorder.index(postorder[-1]) 14 | root.left = self.buildTree(inorder[:idx], postorder[:idx]) 15 | root.right = self.buildTree(inorder[idx+1:], postorder[idx:-1]) 16 | return root -------------------------------------------------------------------------------- /1-树/其他/Leetcode222. 完全二叉树的节点个数(12).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def countNodes(self, root: Optional[TreeNode]) -> int: 9 | def dfs(root): 10 | # 返回这棵树的节点个数 11 | if not root: return 0 12 | if not root.left and not root.right: return 1 13 | left_node, right_node = root, root 14 | left_h, right_h = 0, 0 15 | while left_node: 16 | left_node = left_node.left 17 | left_h += 1 18 | while right_node: 19 | right_node = right_node.right 20 | right_h += 1 21 | if left_h == right_h: return 2 ** left_h - 1 22 | else: return dfs(root.left) + dfs(root.right) + 1 23 | return dfs(root) -------------------------------------------------------------------------------- /1-树/其他/Leetcode235. 二叉搜索树的最近公共祖先(6).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | 8 | class Solution: 9 | def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': 10 | if not root or not p or not q: return None 11 | while root: 12 | if root.val > p.val and root.val > q.val: root = root.left 13 | elif root.val < p.val and root.val < q.val: root = root.right 14 | else: return root -------------------------------------------------------------------------------- /1-树/其他/Leetcode297. 二叉树的序列化与反序列化(40).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode(object): 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | 8 | class Codec: 9 | 10 | def serialize(self, root): 11 | """Encodes a tree to a single string. 12 | 13 | :type root: TreeNode 14 | :rtype: str 15 | """ 16 | self.res = '' 17 | if not root: return self.res 18 | 19 | def dfs(root): 20 | if not root: 21 | self.res += ' #' 22 | else: 23 | self.res += ' ' + str(root.val) 24 | dfs(root.left) 25 | dfs(root.right) 26 | 27 | dfs(root) 28 | return self.res 29 | 30 | def deserialize(self, data): 31 | """Decodes your encoded data to tree. 32 | 33 | :type data: str 34 | :rtype: TreeNode 35 | """ 36 | if not data: return None 37 | data = list(data.split(' '))[1:] 38 | 39 | # print(data) 40 | def dfs(): 41 | if data[0] == '#': 42 | data.pop(0) 43 | return None 44 | else: 45 | cur = TreeNode(data[0]) 46 | data.pop(0) 47 | cur.left = dfs() 48 | cur.right = dfs() 49 | return cur 50 | 51 | return dfs() -------------------------------------------------------------------------------- /1-树/其他/Leetcode450. 删除二叉搜索树中的节点(26).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: 9 | # https://www.bilibili.com/video/BV1LS4y1R7T8?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 10 | # 返回删除节点key后的root为根节点的二叉搜索树 11 | if not root: return None 12 | # 寻找要删除的节点 -> 删除左子树中节点 13 | if root.val > key: root.left = self.deleteNode(root.left, key) 14 | # 寻找要删除的节点 -> 删除右子树中节点 15 | elif root.val < key: root.right = self.deleteNode(root.right, key) 16 | else: 17 | # 已经找到要删除的节点 18 | # 如果当前要删除的节点是叶子节点 19 | if not root.left and not root.right: return None 20 | # 如果左子树为空 右子树不为空 21 | elif not root.left and root.right: return root.right 22 | # 如果左子树不为空 右子树为空 23 | elif root.left and not root.right: return root.left 24 | else: 25 | # 左右子树都不为空 26 | temp = root.right 27 | while temp and temp.left: 28 | temp = temp.left 29 | root.val, temp.val = temp.val, root.val 30 | root.right = self.deleteNode(root.right, key) 31 | return root -------------------------------------------------------------------------------- /1-树/其他/Leetcode572. 另一棵树的子树(25).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def isSubtree(self, root: TreeNode, subRoot: TreeNode) -> bool: 9 | def isSameTree(tree1, tree2): 10 | # 判断两棵树是不是相同的树 11 | if not tree1 and not tree2: return True 12 | if not tree1 or not tree2: return False 13 | if tree1.val != tree2.val: return False 14 | else: 15 | return isSameTree(tree1.left, tree2.left) and isSameTree(tree1.right, tree2.right) 16 | 17 | if not root and not subRoot: return True 18 | if not root or not subRoot: return False 19 | if isSameTree(root, subRoot) == True: return True 20 | else: 21 | return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot) -------------------------------------------------------------------------------- /1-树/其他/Leetcode剑指 Offer 33. 二叉搜索树的后序遍历序列(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def verifyPostorder(self, postorder: List[int]) -> bool: 3 | if not postorder: return True 4 | def dfs(i, j): 5 | if i >= j: return True 6 | idx = i 7 | while postorder[idx] < postorder[j]: idx += 1 8 | mid = idx 9 | while postorder[idx] > postorder[j]: idx += 1 10 | return idx == j and dfs(i, mid-1) and dfs(mid, j-1) 11 | return dfs(0, len(postorder)-1) -------------------------------------------------------------------------------- /1-树/其他/剑指 Offer 26. 树的子结构(16).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | 8 | class Solution: 9 | def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool: 10 | def is_contain(A, B): 11 | # A是否包含B 12 | if not B: return True 13 | if not A: return False 14 | if A.val != B.val: return False 15 | else: return is_contain(A.left, B.left) and is_contain(A.right, B.right) 16 | 17 | if not A or not B: return False 18 | if is_contain(A, B): return True 19 | else: return self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B) -------------------------------------------------------------------------------- /1-树/前序/Leetcode104. 二叉树的最大深度(68).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 10:43 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | class TreeNode: 8 | def __init__(self, val=0, left=None, right=None): 9 | self.val = val 10 | self.left = left 11 | self.right = right 12 | class Solution: 13 | def maxDepth(self, root: Optional[TreeNode]) -> int: 14 | if not root: return 0 15 | max_depth = 0 16 | def dfs(node, cur_depth): 17 | nonlocal max_depth 18 | if not node: return 19 | if not node.left and not node.right: 20 | max_depth = max(max_depth, cur_depth + 1) 21 | dfs(node.left, cur_depth + 1) 22 | dfs(node.right, cur_depth + 1) 23 | dfs(root, 0) 24 | return max_depth -------------------------------------------------------------------------------- /1-树/前序/Leetcode108. 将有序数组转换为二叉搜索树(12).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]: 9 | def dfs(nums): 10 | if not nums: return 11 | mid = len(nums) // 2 12 | root = TreeNode(nums[mid]) 13 | root.left = dfs(nums[:mid]) 14 | root.right = dfs(nums[mid+1:]) 15 | return root 16 | return dfs(nums) -------------------------------------------------------------------------------- /1-树/前序/Leetcode109. 有序链表转换二叉搜索树(9).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | # Definition for a binary tree node. 7 | # class TreeNode: 8 | # def __init__(self, val=0, left=None, right=None): 9 | # self.val = val 10 | # self.left = left 11 | # self.right = right 12 | class Solution: 13 | def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]: 14 | if not head: return None 15 | if not head.next: return TreeNode(head.val) 16 | def findMid(head): 17 | pre = None 18 | slow, fast = head, head 19 | while fast and fast.next: 20 | pre = slow 21 | slow = slow.next 22 | fast = fast.next.next 23 | return pre, slow 24 | pre, mid = findMid(head) 25 | pre.next = None 26 | head2 = mid.next 27 | root = TreeNode(mid.val) 28 | # print(head) 29 | # print(head2) 30 | root.left = self.sortedListToBST(head) 31 | root.right = self.sortedListToBST(head2) 32 | return root -------------------------------------------------------------------------------- /1-树/前序/Leetcode112. 路径总和(58).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: 9 | if not root: return False 10 | self.flag = False 11 | def dfs(root, cur_sum): 12 | if not root: return 13 | if not root.left and not root.right: 14 | if cur_sum - root.val == 0: 15 | self.flag = True 16 | dfs(root.left, cur_sum-root.val) 17 | dfs(root.right, cur_sum-root.val) 18 | dfs(root, targetSum) 19 | return self.flag -------------------------------------------------------------------------------- /1-树/前序/Leetcode113. 路径总和 II(65).py: -------------------------------------------------------------------------------- 1 | # Leetcode112. 路径总和 2 | # Definition for a binary tree node. 3 | class TreeNode: 4 | def __init__(self, val=0, left=None, right=None): 5 | self.val = val 6 | self.left = left 7 | self.right = right 8 | class Solution: 9 | def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: 10 | if not root: return False 11 | self.flag = False 12 | def dfs(root, cur): 13 | if not root: return 14 | if not root.left and not root.right: 15 | if cur - root.val == 0: self.flag = True 16 | dfs(root.left, cur-root.val) 17 | dfs(root.right, cur-root.val) 18 | dfs(root, targetSum) 19 | return self.flag 20 | 21 | # Leetcode113. 路径总和 II 22 | # Definition for a binary tree node. 23 | class TreeNode: 24 | def __init__(self, val=0, left=None, right=None): 25 | self.val = val 26 | self.left = left 27 | self.right = right 28 | class Solution: 29 | def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]: 30 | if not root: return [] 31 | res = [] 32 | path = [] 33 | def dfs(root, cur, res, path): 34 | if not root: return 35 | if not root.left and not root.right: 36 | if cur - root.val == 0: 37 | res.append(path+[root.val]) 38 | return 39 | dfs(root.left, cur-root.val, res, path+[root.val]) 40 | dfs(root.right, cur-root.val, res, path+[root.val]) 41 | dfs(root, targetSum, res, path) 42 | return res -------------------------------------------------------------------------------- /1-树/前序/Leetcode129. 求根节点到叶节点数字之和(69).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 10:39 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | class TreeNode: 8 | def __init__(self, val=0, left=None, right=None): 9 | self.val = val 10 | self.left = left 11 | self.right = right 12 | class Solution: 13 | def sumNumbers(self, root: TreeNode) -> int: 14 | res = 0 15 | def dfs(node, path_sum): 16 | nonlocal res 17 | if not node: return 18 | if not node.left and not node.right: 19 | res += path_sum * 10 + node.val 20 | return 21 | dfs(node.left, path_sum*10+node.val) 22 | dfs(node.right, path_sum*10+node.val) 23 | dfs(root, 0) 24 | return res -------------------------------------------------------------------------------- /1-树/前序/Leetcode144. 二叉树的前序遍历(76).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 10:21 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | # class TreeNode: 8 | # def __init__(self, val=0, left=None, right=None): 9 | # self.val = val 10 | # self.left = left 11 | # self.right = right 12 | class Solution: 13 | def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 14 | # 一、递归 15 | if not root: return [] 16 | res = [] 17 | def dfs(root): 18 | if not root: return 19 | res.append(root.val) 20 | dfs(root.left) 21 | dfs(root.right) 22 | dfs(root) 23 | return res 24 | 25 | # 二、迭代 26 | if not root: return [] 27 | res = [] 28 | stack, cur = [], root 29 | while stack or cur: 30 | while cur: 31 | res.append(cur.val) 32 | stack.append(cur) 33 | cur = cur.left 34 | cur = stack.pop() 35 | cur = cur.right 36 | return res -------------------------------------------------------------------------------- /1-树/前序/Leetcode257. 二叉树的所有路径(12).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]: 9 | # pathStr += root.val + '->' 10 | def dfs(root, path, res): 11 | if not root: return 12 | if not root.left and not root.right: 13 | path += str(root.val) 14 | res.append(path) 15 | return 16 | dfs(root.left, path + str(root.val) + '->', res) 17 | dfs(root.right, path + str(root.val) + '->', res) 18 | 19 | if not root: return [] 20 | if not root.left and not root.right: return [str(root.val)] 21 | path, res = '', [] 22 | dfs(root, path, res) 23 | return res -------------------------------------------------------------------------------- /1-树/前序/Leetcode437. 路径总和 III(10).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def __init__(self): 9 | self.res = 0 10 | def dfs(self, node, curSum): 11 | if not node: return 12 | if curSum == node.val: 13 | self.res += 1 14 | self.dfs(node.left, curSum - node.val) 15 | self.dfs(node.right, curSum - node.val) 16 | def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int: 17 | if not root: return 0 18 | self.dfs(root, targetSum) 19 | self.pathSum(root.left, targetSum) 20 | self.pathSum(root.right, targetSum) 21 | return self.res -------------------------------------------------------------------------------- /1-树/前序/Leetcode617. 合并二叉树(10).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: 9 | def dfs(root1, root2): 10 | if not root1 and not root2: return None 11 | if not root1: return root2 12 | if not root2: return root1 13 | root1.val += root2.val 14 | root1.left = dfs(root1.left, root2.left) 15 | root1.right = dfs(root1.right, root2.right) 16 | return root1 17 | return dfs(root1, root2) -------------------------------------------------------------------------------- /1-树/前序/Leetcode863. 二叉树中所有距离为 K 的结点(12).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | 8 | class Solution: 9 | def distanceK(self, root: TreeNode, target: TreeNode, k: int) -> List[int]: 10 | # https://leetcode.cn/problems/all-nodes-distance-k-in-binary-tree/solution/er-cha-shu-zhong-suo-you-ju-chi-wei-kde-nlct0/ 11 | if not root: return [] 12 | self.res = [] 13 | mapp = dict() # key: child val: parent 14 | 15 | def dfs_get_parent(node): 16 | # 找到所有节点的父亲节点 方便向上遍历 17 | if not node: return 18 | if node.left: 19 | mapp[node.left] = node 20 | if node.right: 21 | mapp[node.right] = node 22 | dfs_get_parent(node.left) 23 | dfs_get_parent(node.right) 24 | 25 | def dfs_find_res(cur, pre, idx): 26 | if not cur: return 27 | if idx == k: 28 | self.res.append(cur.val) 29 | return 30 | # 如果是只向下找 是不可能出现重复元素的 但是这里会向上找 所以可能会重复 需要设置一定条件 31 | # 所以每走一个节点都要确定一下当前节点肯定不等于上一时刻刚走了的节点 不能走回头路 32 | # 往左走 需要肯定左子树不等于上一个节点 如 535 33 | if cur.left != pre: 34 | dfs_find_res(cur.left, cur, idx + 1) 35 | # 往右走 需要肯定右子树不等于上一个节点 如 252 36 | if cur.right != pre: 37 | dfs_find_res(cur.right, cur, idx + 1) 38 | # 往上走 需要肯定有父亲节点 且 父亲节点不等于上一个节点 如 353 39 | if cur in mapp and mapp[cur] != pre: 40 | dfs_find_res(mapp[cur], cur, idx + 1) 41 | 42 | dfs_get_parent(root) 43 | dfs_find_res(target, None, 0) 44 | return self.res 45 | -------------------------------------------------------------------------------- /1-树/后序/Leetcode110. 平衡二叉树(68).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 11:05 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | class TreeNode: 8 | def __init__(self, val=0, left=None, right=None): 9 | self.val = val 10 | self.left = left 11 | self.right = right 12 | class Solution: 13 | def isBalanced(self, root: TreeNode) -> bool: 14 | if not root: return True 15 | def dfs(root): 16 | # 返回root这颗子树的高度 如果返回高度为正常值 就是返回这棵树的高度 17 | # 但是如果返回-1说明不是平衡二叉树 高度为-0 18 | if not root: return 0 19 | left = dfs(root.left) 20 | if left == -1: return -1 21 | right = dfs(root.right) 22 | if right == -1: return -1 23 | # 左右子树都是平衡二叉树 返回的都是正常树的高度 24 | # 再判断当前节点为根节点的树的高度 25 | # 如果是平衡二叉树返回-0 如果不是就正常返回当前树的高度 26 | return max(left,right)+1 if abs(left-right)<=1 else -1 27 | return dfs(root) != -1 -------------------------------------------------------------------------------- /1-树/后序/Leetcode124. 二叉树中的最大路径和(110).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/28 9:21 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | # class TreeNode: 8 | # def __init__(self, val=0, left=None, right=None): 9 | # self.val = val 10 | # self.left = left 11 | # self.right = right 12 | class Solution: 13 | def maxPathSum(self, root: Optional[TreeNode]) -> int: 14 | if not root: return 0 15 | self.res = float('-inf') 16 | def dfs(root): 17 | if not root: return 0 18 | left = max(dfs(root.left), 0) 19 | right = max(dfs(root.right), 0) 20 | self.res = max(self.res, left + right + root.val) 21 | return max(left, right) + root.val 22 | dfs(root) 23 | return self.res -------------------------------------------------------------------------------- /1-树/后序/Leetcode145. 二叉树的后序遍历(33).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 9 | # 0、递归 10 | self.res = [] 11 | def dfs(root): 12 | if not root: return 13 | dfs(root.left) 14 | dfs(root.right) 15 | self.res.append(root.val) 16 | dfs(root) 17 | return self.res 18 | 19 | # 2、迭代 20 | # 后序:左右根 前序:根左右 21 | res = [] 22 | stack, cur = [], root 23 | while cur or stack: 24 | while cur: 25 | res.append(cur.val) 26 | stack.append(cur) 27 | cur = cur.right 28 | cur = stack.pop() 29 | cur = cur.left 30 | return res[::-1] -------------------------------------------------------------------------------- /1-树/后序/Leetcode226. 翻转二叉树(53).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | # 前序后序都可以 8 | class Solution: 9 | def invertTree(self, root: TreeNode) -> TreeNode: 10 | if not root: return None 11 | def dfs(node): 12 | if not node: return 13 | left = dfs(node.left) 14 | right = dfs(node.right) 15 | node.left, node.right = right, left 16 | return dfs(root) 17 | 18 | 19 | # 剑指 Offer 27. 二叉树的镜像 20 | class Solution: 21 | def mirrorTree(self, root: TreeNode) -> TreeNode: 22 | if not root: return None 23 | def dfs(root): 24 | if not root: return 25 | left = dfs(root.left) 26 | right = dfs(root.right) 27 | root.left = right 28 | root.right = left 29 | return root 30 | return dfs(root) -------------------------------------------------------------------------------- /1-树/后序/Leetcode236. 二叉树的最近公共祖先(164).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 11:00 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 二叉树中最常考的题 最经典的题 7 | # class TreeNode: 8 | # def __init__(self, x): 9 | # self.val = x 10 | # self.left = None 11 | # self.right = None 12 | 13 | class Solution: 14 | def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': 15 | if not root: return None 16 | res = None 17 | def dfs(root, p, q): 18 | nonlocal res 19 | # 递归返回的是以root为根节点的子树是否包含p或q 20 | if not root: return False 21 | left = dfs(root.left, p, q) 22 | right = dfs(root.right, p, q) 23 | # 找到p和q最近公共祖先的条件: 24 | # 0、左子树和右子树各有一个p或q 25 | # 2、root=p/q and left/right中有p或q 26 | if (left and right) or ((root == p or root == q) and (left or right)): 27 | res = root 28 | # 返回以root为根节点的子树是否包含p或q 29 | return left or right or root == p or root == q 30 | dfs(root, p, q) 31 | return res -------------------------------------------------------------------------------- /1-树/后序/Leetcode543. 二叉树的直径(65).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode: 3 | def __init__(self, val=0, left=None, right=None): 4 | self.val = val 5 | self.left = left 6 | self.right = right 7 | class Solution: 8 | def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: 9 | if not root: return 0 10 | self.max_count = 1 11 | def dfs(root): 12 | # 以root为根节点这颗树的最大深度 13 | if not root: return 0 14 | left = dfs(root.left) 15 | right = dfs(root.right) 16 | # 记录以root为根节点这颗树的最大节点个数 17 | # root这颗树的最大节点个数=左子树最大节点个数+右子树最大节点个数+0 18 | self.max_count = max(self.max_count, left+right+1) 19 | return max(left, right) + 1 20 | dfs(root) 21 | # 最大直径=最大节点个数-0 22 | return self.max_count - 1 -------------------------------------------------------------------------------- /1-树/层次/Leetcode101. 对称二叉树(65).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def isSymmetric(self, root: Optional[TreeNode]) -> bool: 9 | if not root: return True 10 | queue = [root] 11 | while queue: 12 | n, arr = len(queue), [] 13 | for _ in range(n): 14 | cur = queue.pop(0) 15 | if cur: arr.append(cur.val) 16 | else: arr.append('#') 17 | if cur: 18 | queue.append(cur.left) 19 | queue.append(cur.right) 20 | if arr != arr[::-1]: return False 21 | return True -------------------------------------------------------------------------------- /1-树/层次/Leetcode102. 二叉树的层序遍历(184).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 10:57 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | class TreeNode: 8 | def __init__(self, val=0, left=None, right=None): 9 | self.val = val 10 | self.left = left 11 | self.right = right 12 | class Solution: 13 | def levelOrder(self, root: TreeNode) -> List[List[int]]: 14 | if not root: return [] 15 | res, queue = [], [root] 16 | while queue: 17 | size, arr = len(queue), [] 18 | for i in range(size): 19 | cur_node = queue.pop(0) 20 | arr.append(cur_node.val) 21 | if cur_node.left: queue.append(cur_node.left) 22 | if cur_node.right: queue.append(cur_node.right) 23 | res.append(copy.deepcopy(arr)) 24 | return res -------------------------------------------------------------------------------- /1-树/层次/Leetcode103. 二叉树的锯齿形层序遍历(103).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 8:55 4 | @github: https://github.com/HuKai97 5 | """ 6 | class TreeNode: 7 | def __init__(self, val=0, left=None, right=None): 8 | self.val = val 9 | self.left = left 10 | self.right = right 11 | class Solution: 12 | def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]: 13 | if not root: return [] 14 | queue = [root] 15 | reverse = False # 当前层是否逆序 16 | res = [] 17 | while queue: 18 | size, arr = len(queue), [] 19 | for i in range(size): 20 | cur_node = queue.pop(0) 21 | arr.append(cur_node.val) 22 | if cur_node.left: queue.append(cur_node.left) 23 | if cur_node.right: queue.append(cur_node.right) 24 | if reverse == True: 25 | res.append(arr[::-1]) 26 | reverse = False 27 | else: 28 | res.append(arr) 29 | reverse = True 30 | return res 31 | -------------------------------------------------------------------------------- /1-树/层次/Leetcode199. 二叉树的右视图(104).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 11:26 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for a binary tree node. 7 | class TreeNode: 8 | def __init__(self, val=0, left=None, right=None): 9 | self.val = val 10 | self.left = left 11 | self.right = right 12 | class Solution: 13 | def rightSideView(self, root: Optional[TreeNode]) -> List[int]: 14 | # 层次遍历 15 | if not root: return [] 16 | res, queue = [], [root] 17 | while queue: 18 | size = len(queue) 19 | for i in range(size): 20 | cur = queue.pop(0) 21 | if i == size-1: 22 | res.append(cur.val) 23 | if cur.left: queue.append(cur.left) 24 | if cur.right: queue.append(cur.right) 25 | return res -------------------------------------------------------------------------------- /1-树/层次/Leetcode662. 二叉树最大宽度(44).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: 9 | if not root: return 0 10 | queue = [(root, 0)] 11 | max_width = 1 12 | while queue: 13 | cur_width = queue[-1][1] - queue[0][1] + 1 # 刚在末尾判断会报错 末尾时queue=None 14 | if cur_width > max_width: max_width = cur_width 15 | size = len(queue) 16 | for i in range(size): 17 | cur, pos = queue.pop(0) 18 | if cur.left: queue.append((cur.left, 2*pos+1)) 19 | if cur.right: queue.append((cur.right, 2*pos+2)) 20 | return max_width -------------------------------------------------------------------------------- /1-树/层次/Leetcode958. 二叉树的完全性检验(37).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def isCompleteTree(self, root: TreeNode) -> bool: 9 | if not root: return True 10 | queue = [(root, 0)] 11 | count = 0 12 | while queue: 13 | cur, idx = queue.pop(0) 14 | count += 1 15 | if cur.left: queue.append((cur.left, 2*idx+1)) 16 | if cur.right: queue.append((cur.right, 2*idx+2)) 17 | return count == idx+1 -------------------------------------------------------------------------------- /10-其他/10-1-Hash/Leetcode1. 两数之和(195).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 9:44 4 | @github: https://github.com/HuKai97 5 | """ 6 | # hash map 7 | class Solution: 8 | def twoSum(self, nums: List[int], target: int) -> List[int]: 9 | # 第一想法是和三数之和一样用双指针 但是这里返回的是idx 所以不能sort 也就不能用双指针了 10 | if len(nums) < 1: return [] 11 | mapp = {nums[0]: 0} 12 | for i in range(1, len(nums)): 13 | if target - nums[i] in mapp: 14 | return [i, mapp[target - nums[i]]] 15 | mapp[nums[i]] = i 16 | 17 | 18 | class Solution: 19 | def twoSum(self, nums: List[int], target: int) -> List[int]: 20 | if not nums: return [-1, -1] 21 | mapp = [] 22 | for i in range(len(nums)): 23 | if target - nums[i] in mapp: 24 | return [i, mapp.index(target-nums[i])] 25 | mapp.append(nums[i]) 26 | return [-1, -1] -------------------------------------------------------------------------------- /10-其他/10-1-Hash/Leetcode12. 整数转罗马数字(10).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def intToRoman(self, num: int) -> str: 3 | # 0 1 2 3 4 5 6 7 8 9 4 | mapp = [ 5 | ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'], # 个位 6 | ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'], # 十位 7 | ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'], # 百位 8 | ['', 'M', 'MM', 'MMM'] # 千位 9 | ] 10 | return mapp[3][num // 1000] + mapp[2][num % 1000 // 100] + mapp[1][num % 1000 % 100 // 10] + mapp[0][num % 1000 % 100 % 10] -------------------------------------------------------------------------------- /10-其他/10-1-Hash/Leetcode560. 和为 K 的子数组(35).py: -------------------------------------------------------------------------------- 1 | # 前缀和 2 | class Solution: 3 | def subarraySum(self, nums: List[int], k: int) -> int: 4 | res = 0 5 | mapp = defaultdict(int) 6 | mapp[0] = 1 7 | cur_sum = 0 8 | for i in range(len(nums)): 9 | cur_sum += nums[i] 10 | if cur_sum - k in mapp: res += mapp[cur_sum-k] 11 | mapp[cur_sum] += 1 12 | return res -------------------------------------------------------------------------------- /10-其他/10-1-Hash/Leetcode974. 和可被 K 整除的子数组(9).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def subarraysDivByK(self, nums: List[int], k: int) -> int: 3 | mapp = defaultdict(int) # key: 余数 val: 余数出现的次数 4 | mapp[0] = 1 5 | total = 0 6 | res = 0 7 | for i in range(len(nums)): 8 | total += nums[i] 9 | mod = total % k 10 | if mod in mapp: # start: mod=0 res=0+1=1 11 | res += mapp[mod] 12 | mapp[mod] += 1 13 | return res -------------------------------------------------------------------------------- /10-其他/10-10-其他题/Leetcode169. 多数元素(59).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def majorityElement(self, nums: List[int]) -> int: 3 | # 投票法 两类 是多数元素和不是多少元素 4 | res = nums[0] 5 | count = 1 6 | for i in range(1, len(nums)): 7 | if count == 0: 8 | res = nums[i] 9 | if nums[i] != res: 10 | count -= 1 11 | else: 12 | count += 1 13 | return res -------------------------------------------------------------------------------- /10-其他/10-10-其他题/Leetcode31. 下一个排列(82).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def nextPermutation(self, nums: List[int]) -> None: 3 | """ 4 | Do not return anything, modify nums in-place instead. 5 | """ 6 | # 从后往前 找到第一个升序的位置 7 | i = len(nums) - 2 8 | while i >= 0 and nums[i+1] <= nums[i]: 9 | i -= 1 10 | # 找到nums[i] < nums[i+1] 11 | 12 | # 从后往前 找到第一个比nums[i]大的数 13 | # 23541 -> 24531 23451 -> 23541 14 | if i >= 0: # 321 全降序情况下 i-=1 => i=-1 全降序 没有更大的数了 15 | j = len(nums) - 1 16 | while j >= 0 and nums[j] <= nums[i]: 17 | j -= 1 18 | # 找到nums[j] < nums[i] 19 | nums[i], nums[j] = nums[j], nums[i] 20 | # print(nums[i+1:]) 21 | # 再把nums[i+1:]后面的数升序排列 22 | # [i+1,end) 必然是降序 翻转 就是升序 23 | start, end = i + 1, len(nums) - 1 24 | while start < end: 25 | nums[start], nums[end] = nums[end], nums[start] 26 | start += 1 27 | end -= 1 -------------------------------------------------------------------------------- /10-其他/10-10-其他题/Leetcode556. 下一个更大元素 III(16).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def nextGreaterElement(self, n: int) -> int: 3 | res = [] 4 | while n: 5 | res.append(n % 10) 6 | n //= 10 7 | res = res[::-1] 8 | 9 | # 找到第一个升序的位置 i 10 | i = len(res) - 2 11 | while i >= 0 and res[i+1] <= res[i]: i -= 1 12 | 13 | # 找到第一个比res[i]更大的数 j 14 | if i >= 0: 15 | j = len(res) - 1 16 | while j > i and res[j] <= res[i]: j -= 1 17 | res[i], res[j] = res[j], res[i] 18 | else: return -1 19 | 20 | # 排序 21 | start, end = i + 1, len(res) - 1 22 | while start < end: 23 | res[start], res[end] = res[end], res[start] 24 | start += 1 25 | end -= 1 26 | ans = 0 27 | while res: 28 | ans = ans * 10 + res.pop(0) 29 | return ans if ans <= 2**31 - 1 else -1 -------------------------------------------------------------------------------- /10-其他/10-2-原地Hash/Leetcode268. 丢失的数字(268).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def missingNumber(self, nums: List[int]) -> int: 3 | n = len(nums) 4 | for i in range(n): 5 | # 6 | while nums[i] < n and nums[i] != i: 7 | nums[nums[i]], nums[i] = nums[i], nums[nums[i]] 8 | # print(nums) 9 | for i in range(len(nums)): 10 | if nums[i] != i: return i 11 | return len(nums) -------------------------------------------------------------------------------- /10-其他/10-2-原地Hash/Leetcode287. 寻找重复数(22).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findDuplicate(self, nums: List[int]) -> int: 3 | for i in range(len(nums)): 4 | while 1 <= nums[i] <= len(nums) and nums[i]-1 != i and nums[nums[i]-1] != nums[i]: 5 | nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] 6 | for i in range(len(nums)): 7 | if nums[i]-1 != i: return nums[i] -------------------------------------------------------------------------------- /10-其他/10-2-原地Hash/Leetcode41. 缺失的第一个正数(84).py: -------------------------------------------------------------------------------- 1 | # https://leetcode.cn/problems/first-missing-positive/solution/que-shi-de-di-yi-ge-zheng-shu-by-leetcode-solution/ 2 | class Solution: 3 | def firstMissingPositive(self, nums: List[int]) -> int: 4 | if len(nums) == 0: return 1 5 | for i in range(len(nums)): 6 | # nums[i] != nums[nums[i]-0]条件避免重复 出现死循环 7 | # 1 <= nums[i] <= len(nums) 必须在1-n这个范围内 不在这个范围的值不用管 它肯定不是要求的值 8 | # nums[i]-1 != i 不相等就交换 9 | # nums[nums[i]-1] != nums[i] 如果要交换的位置已经有这个值 也就是重复出现了 也不用管 10 | while 1 <= nums[i] <= len(nums) and nums[i]-1 != i and nums[i] != nums[nums[i]-1]: 11 | nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] 12 | for i in range(len(nums)): 13 | if nums[i] != i+1: 14 | return i+1 15 | return len(nums) + 1 -------------------------------------------------------------------------------- /10-其他/10-2-原地Hash/Leetcode442. 数组中重复的数据(19).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findDuplicates(self, nums: List[int]) -> List[int]: 3 | if len(nums) < 2: return [] 4 | res = [] 5 | for i in range(len(nums)): 6 | while 1 <= nums[i] <= len(nums) and nums[i]-1 != i and nums[nums[i]-1] != nums[i]: 7 | nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] 8 | for i in range(len(nums)): 9 | if nums[i] - 1 != i: 10 | res.append(nums[i]) 11 | return res 12 | -------------------------------------------------------------------------------- /10-其他/10-2-原地Hash/剑指 Offer 03. 数组中重复的数字(13).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findRepeatNumber(self, nums: List[int]) -> int: 3 | n = len(nums) 4 | for i in range(n): 5 | while nums[i] != i: 6 | if nums[nums[i]] == nums[i]: return nums[i] 7 | else: 8 | nums[nums[i]], nums[i] = nums[i], nums[nums[i]] -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode11. 盛最多水的容器(27).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxArea(self, height: List[int]) -> int: 3 | if len(height) < 2: return 0 4 | max_v = 0 5 | left, right = 0, len(height) - 1 6 | while left < right: 7 | # 注意这里的宽是right-left+0-0 8 | cur_v = min(height[left], height[right]) * (right-left) 9 | max_v = max(max_v, cur_v) 10 | if height[left] > height[right]: 11 | right -= 1 12 | else: 13 | left += 1 14 | return max_v -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode125. 验证回文串(26).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isPalindrome(self, s: str) -> bool: 3 | def check(c): 4 | if '0' <= c <= '9' or 'a' <= c <= 'z': return True 5 | else: return False 6 | 7 | if len(s) == 0: return True 8 | left, right = 0, len(s) - 1 9 | s = s.lower() 10 | while left < right: 11 | while left < right and check(s[left]) == False: 12 | left += 1 13 | while left < right and check(s[right]) == False: 14 | right -= 1 15 | if s[left] != s[right]: return False 16 | left += 1 17 | right -= 1 18 | return True -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode15. 三数之和(232).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 9:35 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 10-3-双指针 7 | class Solution: 8 | def threeSum(self, nums: List[int]) -> List[List[int]]: 9 | if len(nums) < 3: return [] 10 | nums.sort() 11 | res = [] 12 | for i in range(len(nums)-2): 13 | if nums[i] > 0: break # 剪枝1 nums[i]>0 后面也必>0 不存在相加为0的组合 14 | if i > 0 and nums[i] == nums[i-1]: continue # 剪枝 排除重复元素 15 | target = 0 - nums[i] 16 | left, right = i+1, len(nums) - 1 17 | while left < right: 18 | cur_sum = nums[left] + nums[right] 19 | if cur_sum == target and [nums[i], nums[left], nums[right]] not in res: 20 | res.append([nums[i], nums[left], nums[right]]) 21 | left += 1 22 | right -= 1 23 | elif cur_sum > target: right -= 1 24 | else: left += 1 25 | return res -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode16. 最接近的三数之和(21).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def threeSumClosest(self, nums: List[int], target: int) -> int: 3 | if len(nums) == 3: return nums[0]+nums[1]+nums[2] 4 | nums.sort() # 不要忘了sort 因为这里用到了双指针的解法 必须有序 5 | res = nums[0] + nums[1] + nums[2] 6 | for i in range(len(nums)-2): 7 | if i > 0 and nums[i] == nums[i-1]: continue 8 | left, right = i+1, len(nums)-1 9 | while left < right: 10 | cur_sum = nums[i] + nums[left] + nums[right] 11 | if abs(cur_sum-target) < abs(res-target): 12 | res = cur_sum 13 | if cur_sum == target: return cur_sum 14 | elif cur_sum > target: right -= 1 15 | else: left += 1 16 | return res -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode18. 四数之和(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def fourSum(self, nums: List[int], target: int) -> List[List[int]]: 3 | if len(nums) < 4: return [] 4 | res = [] 5 | nums.sort() 6 | for i in range(len(nums)-3): 7 | if i > 0 and nums[i] == nums[i-1]: continue # i去重 8 | for j in range(i+1, len(nums)-2): 9 | if j > i+1 and nums[j] == nums[j-1]: continue # j去重 10 | # two sum 11 | t = target - nums[i] - nums[j] 12 | left, right = j + 1, len(nums) - 1 13 | while left < right: 14 | cur = nums[left] + nums[right] 15 | if cur == t: 16 | res.append([nums[i], nums[j], nums[left], nums[right]]) 17 | left += 1 18 | while left < right and nums[left] == nums[left-1]: left += 1 # left去重 19 | right -= 1 20 | while left < right and nums[right] == nums[right+1]: right -= 1 # right去重 21 | elif cur > t: right -= 1 22 | else: left += 1 23 | return res -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode240. 搜索二维矩阵 II(50).py: -------------------------------------------------------------------------------- 1 | # 这套代码74道题也可以A掉 2 | class Solution: 3 | def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: 4 | if not matrix: return False 5 | m, n = len(matrix), len(matrix[0]) 6 | row, col = 0, n-1 7 | while row < m and col >= 0: 8 | if matrix[row][col] == target: return True 9 | elif matrix[row][col] > target: col -= 1 10 | else: row += 1 11 | return False -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode26. 删除有序数组中的重复项(27).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def removeDuplicates(self, nums: List[int]) -> int: 3 | if len(nums) == 0: return 0 4 | if len(nums) == 1: return 1 5 | slow, fast = 1, 1 # idx=0的位置不需要管 它肯定是对的 6 | while fast < len(nums): 7 | if nums[fast] != nums[fast-1]: 8 | nums[slow] = nums[fast] 9 | slow += 1 10 | fast += 1 11 | return slow -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode283. 移动零(39).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def moveZeroes(self, nums: List[int]) -> None: 3 | """ 4 | Do not return anything, modify nums in-place instead. 5 | """ 6 | slow, fast = 0, 0 7 | while fast < len(nums): 8 | if nums[fast] != 0: 9 | nums[slow] = nums[fast] 10 | slow += 1 11 | fast += 1 12 | else: 13 | fast += 1 14 | for i in range(slow, len(nums)): 15 | nums[i] = 0 -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode680. 验证回文字符串 Ⅱ(10).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def validPalindrome(self, s: str) -> bool: 3 | def isPalindrome(s): 4 | left, right = 0, len(s) - 1 5 | while left < right: 6 | if s[left] != s[right]: return False 7 | left += 1 8 | right -= 1 9 | return True 10 | 11 | if len(s) < 2: return True 12 | if s == s[::-1]: return True 13 | left, right = 0, len(s) - 1 14 | while left <= right: 15 | if s[left] == s[right]: 16 | left += 1 17 | right -= 1 18 | else: 19 | return isPalindrome(s[left + 1: right + 1]) or isPalindrome(s[left: right]) -------------------------------------------------------------------------------- /10-其他/10-3-双指针/Leetcode75. 颜色分类(27).py: -------------------------------------------------------------------------------- 1 | # https://www.bilibili.com/video/BV1tz4y1o7n5?spm_id_from=333.337.search-card.all.click 2 | class Solution: 3 | def sortColors(self, nums: List[int]) -> None: 4 | """ 5 | Do not return anything, modify nums in-place instead. 6 | """ 7 | zero = 0 8 | two = len(nums) - 1 9 | i = 0 10 | while i <= two: 11 | if nums[i] == 0: 12 | # 交换后的nums[i]肯定=1 不需要比较 i++ 13 | nums[i], nums[zero] = nums[zero], nums[i] 14 | zero += 1 15 | i += 1 16 | elif nums[i] == 1: 17 | i += 1 18 | elif nums[i] == 2: 19 | # 交换后的nums[i]不知道是什么数 需要重新比较 i不变 20 | nums[i], nums[two] = nums[two], nums[i] 21 | two -= 1 22 | -------------------------------------------------------------------------------- /10-其他/10-4-滑动窗口/Leetcode1004. 最大连续1的个数 III(15).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestOnes(self, nums: List[int], k: int) -> int: 3 | # 找最大连续的窗口 该窗口内最多有k个0 4 | res = 0 5 | start = 0 6 | count_one = 0 7 | for end in range(len(nums)): 8 | if nums[end] == 1: 9 | count_one += 1 10 | while count_one + k < end - start + 1: 11 | if nums[start] == 1: 12 | count_one -= 1 13 | start += 1 14 | res = max(res, end - start + 1) 15 | return res -------------------------------------------------------------------------------- /10-其他/10-4-滑动窗口/Leetcode209. 长度最小的子数组(40).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minSubArrayLen(self, target: int, nums: List[int]) -> int: 3 | if not nums: return 0 4 | res = len(nums) + 1 5 | start = 0 6 | cur_sum = 0 7 | for end in range(len(nums)): 8 | cur_sum += nums[end] # 每改变一次都比较一次 9 | while cur_sum >= target: 10 | res = min(res, end-start+1) 11 | cur_sum -= nums[start] 12 | start += 1 13 | return res if res != len(nums) + 1 else 0 -------------------------------------------------------------------------------- /10-其他/10-4-滑动窗口/Leetcode28. 实现 strStr()(10).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def strStr(self, haystack: str, needle: str) -> int: 3 | start = 0 4 | for end in range(len(needle)-1, len(haystack)): 5 | if haystack[start: end+1] == needle: 6 | return start 7 | start += 1 8 | return -1 -------------------------------------------------------------------------------- /10-其他/10-4-滑动窗口/Leetcode3. 无重复字符的最长子串(438).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 8:43 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 不定长滑动窗口 7 | class Solution: 8 | def lengthOfLongestSubstring(self, s: str) -> int: 9 | if len(s) < 2: return len(s) 10 | start = 0 11 | max_len = 1 12 | mapp = set() 13 | for end in range(len(s)): 14 | while s[end] in mapp: 15 | mapp.remove(s[start]) 16 | start += 1 17 | mapp.add(s[end]) 18 | max_len = max(max_len, end-start+1) # 比较一次即可 19 | return max_len 20 | 21 | # 栈 22 | class Solution: 23 | def lengthOfLongestSubstring(self, s: str) -> int: 24 | if len(s) < 2: return len(s) 25 | start = 0 26 | stack = [] 27 | res = 0 28 | for end in range(len(s)): 29 | while s[end] in stack: 30 | stack.pop(0) 31 | start += 1 32 | stack.append(s[end]) 33 | if end - start + 1 > res: 34 | res = end - start + 1 35 | return res 36 | # 空间复杂度O(1) 37 | class Solution: 38 | def lengthOfLongestSubstring(self, s: str) -> int: 39 | res = 0 40 | start = 0 41 | for end in range(len(s)): 42 | while s[end] in s[start:end]: 43 | start += 1 44 | res = max(res, end - start + 1) 45 | return res -------------------------------------------------------------------------------- /10-其他/10-4-滑动窗口/Leetcode76. 最小覆盖子串(72).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minWindow(self, s: str, t: str) -> str: 3 | def isContain(window_dict, t_dict): 4 | # 当前滑动窗口是否包含t 5 | for key in t_dict: 6 | if window_dict[key] < t_dict[key]: return False 7 | return True 8 | window_dict = defaultdict(int) # 存放当前窗口所有元素 9 | t_dict = defaultdict(int) # 存放t中的所有元素 key:val 10 | for key in t: t_dict[key] += 1 11 | start = 0 12 | min_len = len(s) + 1 13 | res = "" 14 | for end in range(len(s)): 15 | if s[end] in t_dict: window_dict[s[end]] += 1 16 | while isContain(window_dict, t_dict): 17 | if min_len > (end - start + 1): 18 | min_len = end - start + 1 19 | res = s[start: end+1] 20 | window_dict[s[start]] -= 1 21 | start += 1 22 | return res -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode14. 最长公共前缀(52).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestCommonPrefix(self, strs: List[str]) -> str: 3 | if len(strs) == 1: return strs[0] 4 | for i in range(len(strs[0])): # 比较第i个字符 5 | for j in range(1, len(strs)): # 比较第j个字符串 6 | # 如果第j个字符串长度就等于i个字符 或 第j个字符串的第i个字符不等于第0个字符串的第i个字符 7 | if i == len(strs[j]) or strs[0][i] != strs[j][i]: return strs[0][:i] 8 | return strs[0] -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode151. 颠倒字符串中的单词(76).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseWords(self, s: str) -> str: 3 | def removeSpaces(s): 4 | # 去除开头结尾和中间多余空格 以及单词之间的多余的空格 5 | left, right = 0, len(s) - 1 6 | # 去除开头空格和结尾空格 7 | while left < right and s[left] == ' ': left += 1 8 | while left < right and s[right] == ' ': right -= 1 9 | new_s = [] 10 | while left <= right: 11 | if s[left] != ' ': new_s.append(s[left]) 12 | elif s[left] == ' ' and new_s[-1] != ' ': new_s.append(s[left]) 13 | left += 1 14 | return new_s 15 | def reverseString(s): 16 | # 翻转字符串 17 | left, right = 0, len(s) - 1 18 | while left < right: 19 | s[left], s[right] = s[right], s[left] 20 | left += 1 21 | right -= 1 22 | return s 23 | def reverseEachWord(s): 24 | left, right = 0, 0 25 | n = len(s) 26 | while left < n: 27 | while right < n and s[right] != ' ': 28 | right += 1 29 | # s[left: right]就是一个单词 30 | s[left: right] = reverseString(s[left: right]) 31 | left = right + 1 # s[right]=' ' 32 | right += 1 33 | return s 34 | s = removeSpaces(s) 35 | s = reverseString(s) 36 | s = reverseEachWord(s) 37 | return ''.join(s) 38 | 39 | 40 | -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode165. 比较版本号(64).py: -------------------------------------------------------------------------------- 1 | # https://www.bilibili.com/video/BV1LA41137us?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 2 | class Solution: 3 | def compareVersion(self, version1: str, version2: str) -> int: 4 | def getNum(version, idx): 5 | res = 0 6 | while idx < len(version) and '0' <= version[idx] <= '9': 7 | res = res * 10 + int(version[idx]) 8 | idx += 1 9 | return res, idx 10 | idx1, idx2 = 0, 0 11 | while idx1 < len(version1) or idx2 < len(version2): 12 | num1, idx1 = getNum(version1, idx1) 13 | num2, idx2 = getNum(version2, idx2) 14 | if num1 > num2: return 1 15 | if num1 < num2: return -1 16 | idx1 += 1 # 遇到. 17 | idx2 += 1 18 | return 0 -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode344. 反转字符串(16).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseString(self, s: List[str]) -> None: 3 | """ 4 | Do not return anything, modify s in-place instead. 5 | """ 6 | left, right = 0, len(s) - 1 7 | while left < right: 8 | s[left], s[right] = s[right], s[left] 9 | left += 1 10 | right -= 1 11 | -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode415. 字符串相加(138).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 9:49 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 模拟加法 7 | class Solution: 8 | def addStrings(self, num1: str, num2: str) -> str: 9 | # 模拟加法 10 | if not num1: return num2 11 | if not num2: return num1 12 | res = [] 13 | add = 0 14 | i, j = len(num1) - 1, len(num2) - 1 15 | # add != 0是为了防止1 9 生成10 add=0 但是i=-0 j=-0 所以还要继续执行一次 16 | while i >= 0 or j >= 0 or add != 0: 17 | x = int(num1[i]) if i >= 0 else 0 18 | y = int(num2[j]) if j >= 0 else 0 19 | cur_sum = x + y + add 20 | res.append(str(cur_sum % 10)) 21 | add = cur_sum // 10 22 | i -= 1 23 | j -= 1 24 | # 从后往前append的 所以要-0 25 | # ['0', '3', '4'] -> '134' 26 | return ''.join(res[::-1]) -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode43. 字符串相乘(65).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def multiply(self, num1: str, num2: str) -> str: 3 | if num1 == '0' or num2 == '0': return '0' 4 | num1, num2 = num1[::-1], num2[::-1] 5 | res = [0 for _ in range(len(num1)+len(num2)+1)] 6 | for i in range(len(num1)): 7 | for j in range(len(num2)): 8 | res[i+j] += int(num1[i]) * int(num2[j]) 9 | # print(res) # [18, 27, 28, 13, 4, 0, 0] 10 | for i in range(len(res)-1): 11 | res[i+1] += res[i] // 10 12 | res[i] %= 10 13 | # print(res) # [8, 8, 0, 6, 5, 0, 0] 14 | while res and res[-1] == 0: 15 | res.pop() 16 | return ''.join(map(str, res[::-1])) -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode443. 压缩字符串(17).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def compress(self, chars: List[str]) -> int: 3 | if len(chars) < 2: return len(chars) 4 | cur = 0 # 压缩后的字符串索引位置 逐区间压缩 5 | i = 0 # 当前区间的起始位置索引 6 | while i < len(chars): 7 | j = i # 当前区间结束位置索引 8 | while j < len(chars) - 1 and chars[j] == chars[j+1]: j += 1 9 | chars[cur] = chars[i] 10 | cur += 1 11 | if j != i: 12 | times = str(j-i+1) 13 | for k in range(len(times)): 14 | chars[cur] = times[k] 15 | cur += 1 16 | i = j + 1 # 更新下一个区间的起始位置 17 | return cur -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode468. 验证IP地址(42).py: -------------------------------------------------------------------------------- 1 | # https://www.bilibili.com/video/BV1nK4y147ZF?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 2 | class Solution: 3 | def validIPAddress(self, queryIP: str) -> str: 4 | def checkIPv4(ip): 5 | for s in ip: 6 | # check length 7 | if len(s) < 1 or len(s) > 3: return 'Neither' 8 | # check digit 9 | for i in range(len(s)): 10 | if s[i] < '0' or s[i] > '9': return 'Neither' 11 | # check leading zero 12 | if len(s) > 1 and s[0] == '0': return 'Neither' 13 | # check range 14 | if len(s) == 3: 15 | cur_sum = 0 16 | for i in range(len(s)): 17 | cur_sum = cur_sum * 10 + int(s[i]) 18 | if cur_sum > 255: return 'Neither' 19 | return 'IPv4' 20 | 21 | def checkIPv6(ip): 22 | for s in ip: 23 | # check lenght 24 | if len(s) < 1 or len(s) > 4: return 'Neither' 25 | # check digit 26 | for i in range(len(s)): 27 | if '0'<=s[i]<='9' or 'a'<=s[i]<='f': continue 28 | return 'Neither' 29 | return 'IPv6' 30 | 31 | ip = queryIP.lower() 32 | ipv4 = ip.split('.') 33 | if len(ipv4) == 4: 34 | return checkIPv4(ipv4) 35 | ipv6 = ip.split(':') 36 | if len(ipv6) == 8: 37 | return checkIPv6(ipv6) 38 | return 'Neither' 39 | -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode557. 反转字符串中的单词 III(17).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverseWords(self, s: str) -> str: 3 | def reverseString(s): 4 | left, right = 0, len(s) - 1 5 | while left < right: 6 | s[left], s[right] = s[right], s[left] 7 | left += 1 8 | right -= 1 9 | return s 10 | def reverseEachWord(s): 11 | left, right = 0, 0 12 | while left < len(s): 13 | while right < len(s) and s[right] != ' ': right += 1 14 | s[left: right] = reverseString(s[left: right]) 15 | left = right + 1 16 | right = left 17 | return s 18 | res = reverseEachWord(list(s)) 19 | return ''.join(res) -------------------------------------------------------------------------------- /10-其他/10-5-字符串/Leetcode6. Z 字形变换(12).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def convert(self, s: str, numRows: int) -> str: 3 | # https://www.bilibili.com/video/BV1U34y1q7UN?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | if numRows == 1: return s 5 | rows = ['' for _ in range(numRows)] 6 | cur_row = 0 7 | down = False # 当前方向向上 8 | for i in range(len(s)): 9 | rows[cur_row] += s[i] 10 | if cur_row == 0 or cur_row == numRows - 1: down = not down 11 | cur_row += 1 if down == True else -1 12 | return ''.join(rows) -------------------------------------------------------------------------------- /10-其他/10-6-模拟/一维模拟/Leetcode128. 最长连续序列(49).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestConsecutive(self, nums: List[int]) -> int: 3 | if len(nums) < 2: return len(nums) 4 | nums_set = set(nums) 5 | max_len = 1 6 | for i in range(len(nums)): 7 | if (nums[i] - 1) not in nums_set: 8 | cur_start = nums[i] 9 | cur_len = 1 10 | while (cur_start + 1) in nums_set: 11 | cur_start += 1 12 | cur_len += 1 13 | if cur_len > max_len: max_len = cur_len 14 | return max_len -------------------------------------------------------------------------------- /10-其他/10-6-模拟/一维模拟/Leetcode217. 存在重复元素(3).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def containsDuplicate(self, nums: List[int]) -> bool: 3 | nums.sort() 4 | for i in range(1, len(nums)): 5 | if nums[i] == nums[i-1]: return True 6 | return False 7 | 8 | -------------------------------------------------------------------------------- /10-其他/10-6-模拟/一维模拟/Leetcode238. 除自身以外数组的乘积(7).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def productExceptSelf(self, nums: List[int]) -> List[int]: 3 | left, right = [1], [1] 4 | cur = 1 5 | for i in range(1, len(nums)): 6 | cur *= nums[i-1] 7 | left.append(cur) 8 | # print(left) 9 | cur = 1 10 | for i in range(len(nums)-2, -1, -1): 11 | cur *= nums[i+1] 12 | right.append(cur) 13 | right = right[::-1] 14 | # print(right) 15 | for i in range(len(left)): 16 | left[i] *= right[i] 17 | return left -------------------------------------------------------------------------------- /10-其他/10-6-模拟/一维模拟/Leetcode657. 机器人能否返回原点.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def judgeCircle(self, moves: str) -> bool: 3 | X = 0 4 | Y = 0 5 | for i in range(len(moves)): 6 | if moves[i] == 'R': 7 | X += 1 8 | elif moves[i] == 'L': 9 | X -= 1 10 | elif moves[i] == 'U': 11 | Y += 1 12 | elif moves[i] == 'D': 13 | Y -= 1 14 | return X == 0 and Y == 0 -------------------------------------------------------------------------------- /10-其他/10-6-模拟/一维模拟/Leetcode88. 合并两个有序数组(165).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 9:14 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: 8 | """ 9 | Do not return anything, modify nums1 in-place instead. 10 | """ 11 | # 写的很巧妙 12 | while m > 0 and n > 0: 13 | if nums1[m-1] > nums2[n-1]: 14 | nums1[m+n-1] = nums1[m-1] 15 | m -= 1 16 | else: 17 | nums1[m+n-1] = nums2[n-1] 18 | n -= 1 19 | if n > 0: nums1[:n] = nums2[:n] -------------------------------------------------------------------------------- /10-其他/10-6-模拟/一维模拟/Leetcode9. 回文数(22).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isPalindrome(self, x: int) -> bool: 3 | if x < 0: return False 4 | if x == 0: return True 5 | res = [] 6 | while x != 0: 7 | res.append(x%10) 8 | x = x // 10 9 | return res == res[::-1] -------------------------------------------------------------------------------- /10-其他/10-6-模拟/一维模拟/Leetcode剑指 Offer 61. 扑克牌中的顺子(17).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isStraight(self, nums: List[int]) -> bool: 3 | if len(nums) != 5: return False 4 | min_v, max_v = 14, 0 5 | mapp = [] 6 | for i in range(len(nums)): 7 | # 除了0意外不能重复 如果重复 一定不是顺子 8 | if nums[i] in mapp: return False 9 | # 大小王不用管 10 | if nums[i] == 0: continue 11 | min_v = min(min_v, nums[i]) 12 | max_v = max(max_v, nums[i]) 13 | mapp.append(nums[i]) 14 | # 如果最大-最小值<5 就可以形成顺子 15 | # >5肯定不行 =5 12356肯定不是顺子 <5 12345 12500是顺子 16 | return max_v - min_v < 5 -------------------------------------------------------------------------------- /10-其他/10-6-模拟/二维模拟/Leetcode48. 旋转图像(58).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rotate(self, matrix: List[List[int]]) -> None: 3 | """ 4 | Do not return anything, modify matrix in-place instead. 5 | """ 6 | n = len(matrix) 7 | # 转置 + 行逆序 8 | for i in range(n): 9 | for j in range(i): 10 | matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] 11 | for i in range(n): 12 | matrix[i] = matrix[i][::-1] 13 | return matrix -------------------------------------------------------------------------------- /10-其他/10-6-模拟/二维模拟/Leetcode54. 螺旋矩阵(136).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 10:04 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def spiralOrder(self, matrix: List[List[int]]) -> List[int]: 8 | if not matrix: return [] 9 | m, n = len(matrix), len(matrix[0]) 10 | left, right, top, bottom = 0, n - 1, 0, m - 1 11 | count = 0 12 | res = [] 13 | while count < m * n: 14 | # 向右走 15 | for j in range(left, right+1): 16 | # 注意这里要加上这个判断条件 否则[[0,2,3,4],[5,6,7,8],[9,10,11,12]] 17 | # 会错:[0,2,3,4,8,12,11,10,9,5,6,7,6] 18 | if count < m * n: 19 | res.append(matrix[top][j]) 20 | count += 1 21 | top += 1 22 | # 往下走 23 | for i in range(top, bottom+1): 24 | if count < m * n: 25 | res.append(matrix[i][right]) 26 | count += 1 27 | right -= 1 28 | # 往左走 29 | for j in range(right, left-1, -1): 30 | if count < m * n: 31 | res.append(matrix[bottom][j]) 32 | count += 1 33 | bottom -= 1 34 | # 往上走 35 | for i in range(bottom, top-1, -1): 36 | if count < m * n: 37 | res.append(matrix[i][left]) 38 | count += 1 39 | left += 1 40 | return res 41 | 42 | -------------------------------------------------------------------------------- /10-其他/10-6-模拟/二维模拟/Leetcode59. 螺旋矩阵 II(31).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def generateMatrix(self, n: int) -> List[List[int]]: 3 | if n == 1: return [[1]] 4 | count = n * n 5 | left, right, top, buttom = 0, n-1, 0, n-1 6 | matrix = [[0 for _ in range(n)]for _ in range(n)] 7 | cur = 1 8 | while cur <= count: 9 | for i in range(left, right+1): 10 | matrix[top][i] = cur 11 | cur += 1 12 | top += 1 13 | for i in range(top, buttom+1): 14 | matrix[i][right] = cur 15 | cur += 1 16 | right -= 1 17 | for i in range(right, left-1, -1): 18 | matrix[buttom][i] = cur 19 | cur += 1 20 | buttom -= 1 21 | for i in range(buttom, top-1, -1): 22 | matrix[i][left] = cur 23 | cur += 1 24 | left += 1 25 | return matrix -------------------------------------------------------------------------------- /10-其他/10-6-模拟/二维模拟/Leetcode73. 矩阵置零(10).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def setZeroes(self, matrix: List[List[int]]) -> None: 3 | """ 4 | Do not return anything, modify matrix in-place instead. 5 | """ 6 | m, n = len(matrix), len(matrix[0]) 7 | rows, cols = [False] * m, [False] * n 8 | for i in range(m): 9 | for j in range(n): 10 | if matrix[i][j] == 0: 11 | rows[i] = True 12 | cols[j] = True 13 | for i in range(m): 14 | for j in range(n): 15 | if rows[i] == True or cols[j] == True: 16 | matrix[i][j] = 0 -------------------------------------------------------------------------------- /10-其他/10-6-模拟/数字模拟/Leetcode166. 分数到小数(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def fractionToDecimal(self, numerator: int, denominator: int) -> str: 3 | if numerator == 0: return '0' 4 | res = [] 5 | 6 | # 有负数 7 | if numerator * denominator < 0: res.append('-') 8 | numerator, denominator = abs(numerator), abs(denominator) 9 | 10 | res.append(str(numerator // denominator)) 11 | remain = numerator % denominator 12 | # 整除 13 | if remain == 0: return ''.join(res) 14 | 15 | # 有小数 16 | mapp = {} # remain: idx 17 | res.append('.') 18 | while remain != 0 and remain not in mapp: 19 | mapp[remain] = len(res) 20 | remain *= 10 21 | res.append(str(remain // denominator)) 22 | remain %= denominator 23 | 24 | # 有循环 25 | if remain: 26 | insertIndex = mapp[remain] 27 | res.insert(insertIndex, '(') 28 | res.append(')') 29 | 30 | return ''.join(res) -------------------------------------------------------------------------------- /10-其他/10-6-模拟/数字模拟/Leetcode171. 26进制数(12).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def titleToNumber(self, columnTitle: str) -> int: 3 | res = 0 4 | for c in columnTitle: 5 | num = ord(c) - ord('A') + 1 6 | res = res * 26 + num 7 | return res -------------------------------------------------------------------------------- /10-其他/10-6-模拟/数字模拟/Leetcode231. 2 的幂(7).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isPowerOfTwo(self, n: int) -> bool: 3 | i = 0 4 | cur = 2 ** i 5 | while cur < n: 6 | i += 1 7 | cur = 2 ** i 8 | return cur == n -------------------------------------------------------------------------------- /10-其他/10-6-模拟/数字模拟/Leetcode7. 整数反转(30).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reverse(self, x: int) -> int: 3 | flag = 1 4 | if x < 0: 5 | flag = -1 6 | x *= flag 7 | res = 0 8 | while x != 0: 9 | res = res * 10 + x % 10 10 | x //= 10 11 | res *= flag 12 | return res if -2**31 <= res <= 2**31-1 else 0 -------------------------------------------------------------------------------- /10-其他/10-6-模拟/数字模拟/Leetcode8. 字符串转换整数 (atoi)(88).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 9:49 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def myAtoi(self, s: str) -> int: 8 | if len(s) == 0: return 0 9 | MIN, MAX = -2**31, 2**31-1 10 | # 丢弃前后空格 11 | s = s.strip() 12 | if len(s) == 0: return 0 13 | # 是否是负数 14 | flag = False 15 | if s[0] == '-': 16 | flag = True 17 | s = s[1:] 18 | elif s[0] == '+': 19 | s = s[1:] 20 | res = 0 21 | for c in s: 22 | if '0' <= c <= '9': 23 | res = res * 10 + int(c) 24 | else: break # 下一个非数字字符跳过 25 | if flag == True: res *= -1 26 | if res < MIN: return MIN 27 | if res > MAX: return MAX 28 | return res -------------------------------------------------------------------------------- /10-其他/10-6-模拟/数字模拟/Leetcode89. 格雷编码(5).py: -------------------------------------------------------------------------------- 1 | # https://www.bilibili.com/video/BV1ib4y1h7bv?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 2 | class Solution: 3 | def grayCode(self, n: int) -> List[int]: 4 | if n == 0: return [[0]] 5 | res = [[] for _ in range(n+1)] 6 | res[0] = ['0'] 7 | for i in range(1, n+1): 8 | reverse = res[i-1][::-1] 9 | res[i] = res[i-1] + reverse 10 | for j in range(len(res[i-1])): 11 | res[i][j] += '0' 12 | for j in range(len(res[i-1]), 2*len(res[i-1])): 13 | res[i][j] += '1' 14 | for i in range(len(res[n])): 15 | res[n][i] = int(res[n][i], 2) 16 | return res[n] -------------------------------------------------------------------------------- /10-其他/10-7-位运算/Leetcode136. 只出现一次的数字(42).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def singleNumber(self, nums: List[int]) -> int: 3 | # 异或运算 a^0=a a^a=0 a^b^c=a^c^b 4 | res = nums[0] 5 | for i in range(1, len(nums)): 6 | res ^= nums[i] 7 | return res -------------------------------------------------------------------------------- /10-其他/10-7-位运算/Leetcode260. 只出现一次的数字 III(13).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def singleNumber(self, nums: List[int]) -> List[int]: 3 | if len(nums) == 2: return nums 4 | num = 0 5 | for i in range(len(nums)): 6 | num ^= nums[i] 7 | # print(num) 110 8 | 9 | # 找到num右边第一个1 10 | mask = 1 # 001 11 | while num & mask == 0: 12 | mask <<= 1 13 | 14 | # 把3和5分在两个组 15 | res = [0, 0] 16 | for n in nums: 17 | if n & mask == 0: 18 | res[0] ^= n 19 | else: 20 | res[1] ^= n 21 | return res -------------------------------------------------------------------------------- /10-其他/10-7-位运算/剑指 Offer 15. 二进制中1的个数.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def hammingWeight(self, n: int) -> int: 3 | if n == 0: return 0 4 | count = 0 5 | while n != 0: 6 | n &= (n-1) 7 | count += 1 8 | return count -------------------------------------------------------------------------------- /10-其他/10-7-位运算/笔记.md: -------------------------------------------------------------------------------- 1 | # 一、异或^运算 2 | 1、a^a=0 3 | 2、a^0=a 4 | 3、a^b^c=a^c^b 5 | 6 | 7 | # 二、与运算 8 | 9 | 找到一个二进制数中1的个数 10 | count = 0 11 | while n != 0: 12 | n &= (n-1) # 判断最后一位是不是1 13 | count += 1 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /10-其他/10-8-数学题/Leetcode172. 阶乘后的零(10).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def trailingZeroes(self, n: int) -> int: 3 | # https://www.bilibili.com/video/BV1u54y1b7Hs?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | # 统计所有元素的因子中5的个数 5 | res = 0 6 | while n >= 5: 7 | res += (n // 5) 8 | n //= 5 9 | return res -------------------------------------------------------------------------------- /10-其他/10-8-数学题/Leetcode292. Nim 游戏(2).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canWinNim(self, n: int) -> bool: 3 | # 如果石头数目是4的倍数,无论你选1、2、3块石头,对上都可以让剩余石头数是4的倍数 4 | # 比如有8个石头,你选1/2/3块石头,对手就选3/2/1块石头 最终肯定会剩下4块石头 5 | # 你再选1/2/3块石头,最终对手就选3/2/1块石头,最后一块石头肯定会再对上手中,对手必赢 6 | return n % 4 != 0 7 | -------------------------------------------------------------------------------- /10-其他/10-8-数学题/Leetcode470. 用 Rand7() 实现 Rand10()(61).py: -------------------------------------------------------------------------------- 1 | # The rand7() API is already defined for you. 2 | # def rand7(): 3 | # @return a random integer in the range 0 to 7 4 | 5 | class Solution: 6 | def rand10(self): 7 | """ 8 | :rtype: int 9 | """ 10 | index = float('inf') 11 | while index >= 40: 12 | # 超过40的话就重新选 13 | index = 7 * (rand7() - 1) + (rand7() - 1) 14 | return index % 10 + 1 -------------------------------------------------------------------------------- /10-其他/10-8-数学题/Leetcode50. Pow(x, n)(31).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def myPow(self, x: float, n: int) -> float: 3 | if n == 0: return 1 4 | if n == 1: return x 5 | def pow(x, cur): 6 | if cur == 0: return 1 7 | y = pow(x, cur // 2) 8 | return y * y if cur % 2 == 0 else y * y * x 9 | return pow(x, n) if n >= 0 else 1.0 / pow(x, -n) -------------------------------------------------------------------------------- /10-其他/10-9-设计题/Leetcode208. 实现前缀树(22).py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self): 3 | self.children = defaultdict(TreeNode) 4 | self.is_word = False 5 | 6 | 7 | class Trie: 8 | def __init__(self): 9 | self.root = TreeNode() 10 | 11 | def insert(self, word: str) -> None: 12 | cur = self.root 13 | for c in word: 14 | cur = cur.children[c] 15 | cur.is_word = True 16 | 17 | def search(self, word: str) -> bool: 18 | cur = self.root 19 | for c in word: 20 | # dict.get(key, default=None) 21 | cur = cur.children.get(c, None) 22 | if cur is None: return False 23 | return cur.is_word 24 | 25 | def startsWith(self, prefix: str) -> bool: 26 | cur = self.root 27 | for c in prefix: 28 | cur = cur.children.get(c, None) 29 | if cur == None: return False 30 | return True 31 | 32 | # Your Trie object will be instantiated and called as such: 33 | # obj = Trie() 34 | # obj.insert(word) 35 | # param_2 = obj.search(word) 36 | # param_3 = obj.startsWith(prefix) -------------------------------------------------------------------------------- /10-其他/10-9-设计题/Leetcode706. 设计哈希映射(10).py: -------------------------------------------------------------------------------- 1 | class MyHashMap: 2 | 3 | def __init__(self): 4 | self.mapp = defaultdict(int) 5 | 6 | def put(self, key: int, value: int) -> None: 7 | self.mapp[key] = value 8 | 9 | def get(self, key: int) -> int: 10 | if key in self.mapp: return self.mapp[key] 11 | else: return -1 12 | 13 | def remove(self, key: int) -> None: 14 | if key in self.mapp: del self.mapp[key] -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/普通回溯/Leetcode22. 括号生成(82).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def generateParenthesis(self, n: int) -> List[str]: 3 | if n < 1: return [] 4 | def dfs(res, path, left, right): 5 | if left == n and right == n: 6 | res.append(copy.deepcopy(path)) 7 | return 8 | if left < n: 9 | dfs(res, path+'(', left+1, right) 10 | if left > right: 11 | dfs(res, path+')', left, right+1) 12 | res, path = [], '' 13 | left, right = 0, 0 14 | dfs(res, path, left, right) 15 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/普通回溯/Leetcode面试题 08.06. 汉诺塔问题(9).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def hanota(self, A: List[int], B: List[int], C: List[int]) -> None: 3 | """ 4 | Do not return anything, modify C in-place instead. 5 | """ 6 | def dfs(n, A, B, C): 7 | if n == 1: 8 | C.append(A.pop()) 9 | return 10 | else: 11 | dfs(n-1, A, C, B) # 把A上面n-1个节点从A移动到B 12 | C.append(A.pop()) # 再把最后一个节点从A移动到C 13 | dfs(n-1, B, A, C) # 再把B中的n-1个节点从B移动到C 14 | n = len(A) 15 | dfs(n, A, B, C) # 把A中n个节点通过B移动到C -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/矩阵回溯/Leetcode130. 被围绕的区域(12).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solve(self, board: List[List[str]]) -> None: 3 | """ 4 | Do not return anything, modify board in-place instead. 5 | """ 6 | m, n = len(board), len(board[0]) 7 | def dfs(row, col): 8 | if row < 0 or row >= m or col < 0 or col >= n or board[row][col] != 'O': return 9 | board[row][col] = 'A' 10 | dfs(row + 1, col) 11 | dfs(row - 1, col) 12 | dfs(row, col + 1) 13 | dfs(row, col - 1) 14 | 15 | if not board or m == 1 or n == 1: return 16 | for i in range(m): 17 | if board[i][0] == 'O': dfs(i, 0) # 第一列和最后一列 18 | if board[i][n-1] == 'O': dfs(i, n - 1) 19 | for j in range(n): 20 | if board[0][j] == 'O': dfs(0, j) # 第一行和最后一行 21 | if board[m-1][j] == 'O': dfs(m - 1, j) 22 | 23 | for i in range(m): 24 | for j in range(n): 25 | if board[i][j] == 'O': 26 | board[i][j] = 'X' 27 | elif board[i][j] == 'A': 28 | board[i][j] = 'O' -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/矩阵回溯/Leetcode200. 岛屿数量(161).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 9:22 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def numIslands(self, grid: List[List[str]]) -> int: 8 | m, n = len(grid), len(grid[0]) 9 | if m == 0 or n == 0: return 0 10 | res = 0 11 | def dfs(i, j): 12 | if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] == '0': return 13 | grid[i][j] = '0' 14 | dfs(i-1, j) 15 | dfs(i+1, j) 16 | dfs(i, j-1) 17 | dfs(i, j+1) 18 | for i in range(m): 19 | for j in range(n): 20 | if grid[i][j] == '0': 21 | res += 1 22 | dfs(i, j) 23 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/矩阵回溯/Leetcode329. 矩阵中的最长递增路径(26).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestIncreasingPath(self, matrix: List[List[int]]) -> int: 3 | if not matrix: return 0 4 | m, n = len(matrix), len(matrix[0]) 5 | directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] 6 | 7 | @cache 8 | def dfs(i, j): 9 | cur = 1 10 | for xi, xj in directions: 11 | x, y = xi + i, xj + j 12 | if 0<=x matrix[i][j]: 13 | cur = max(cur, dfs(x, y) + 1) 14 | return cur 15 | 16 | res = 0 17 | for i in range(m): 18 | for j in range(n): 19 | if dfs(i, j) > res: 20 | res = dfs(i, j) 21 | return res 22 | -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/矩阵回溯/Leetcode51. N 皇后(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def solveNQueens(self, n: int) -> List[List[str]]: 3 | def dfs(board, res, row): 4 | if row == n: 5 | res.append([''.join(r) for r in board]) 6 | return 7 | for col in range(n): 8 | if check(board, row, col): # 遍历每一列 9 | board[row][col] = 'Q' 10 | dfs(board, res, row + 1) 11 | board[row][col] = '.' 12 | 13 | def check(board, row, col): 14 | for i in range(n): # 判断同行和同列有没有皇后 15 | if board[row][i] == 'Q' or board[i][col] == 'Q': return False 16 | for i in range(n): 17 | for j in range(n): 18 | # 判断同一斜线上有没有皇后 19 | if (i + j) == (row + col) and board[i][j] == 'Q': return False 20 | if (i - j) == (row - col) and board[i][j] == 'Q': return False 21 | return True 22 | 23 | board = [['.' for _ in range(n)] for _ in range(n)] 24 | res = [] 25 | row = 0 26 | dfs(board, res, row) 27 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/矩阵回溯/Leetcode695. 岛屿的最大面积(47).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxAreaOfIsland(self, grid: List[List[int]]) -> int: 3 | m, n = len(grid), len(grid[0]) 4 | if m == 0 or n == 0: return 0 5 | max_area = 0 6 | def dfs(i, j): 7 | if i<0 or i>=m or j<0 or j>=n or grid[i][j] == 0: return 0 8 | grid[i][j] = 0 9 | cur_area = 1 10 | cur_area += dfs(i-1, j) 11 | cur_area += dfs(i+1, j) 12 | cur_area += dfs(i, j+1) 13 | cur_area += dfs(i, j-1) 14 | return cur_area 15 | for i in range(m): 16 | for j in range(n): 17 | if grid[i][j] == 1: 18 | max_area = max(dfs(i, j), max_area) 19 | return max_area -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/矩阵回溯/Leetcode79. 单词搜索(35).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def exist(self, board: List[List[str]], word: str) -> bool: 3 | if not board: return False 4 | m, n = len(board), len(board[0]) 5 | directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] 6 | 7 | def dfs(i, j, visited, idx): 8 | # 从board[i][j]开始搜索,看有没有word[idx:] 9 | if i < 0 or j < 0 or i >= m or j >= n or board[i][j] != word[idx]: 10 | return False 11 | if idx == len(word) - 1: return True # idx 0~len(word)-1 12 | visited.add((i, j)) 13 | for di, dj in directions: 14 | new_i, new_j = i + di, j + dj 15 | if (new_i, new_j) not in visited: 16 | if dfs(new_i, new_j, visited, idx+1) == True: 17 | return True 18 | visited.remove((i, j)) 19 | return False 20 | 21 | idx = 0 22 | visited = set() 23 | for i in range(m): 24 | for j in range(n): 25 | if dfs(i, j, visited, idx) == True: 26 | return True 27 | return False -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode131. 分割回文串(9).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def partition(self, s: str) -> List[List[str]]: 3 | def check(s): 4 | left, right = 0, len(s) - 1 5 | while left < right: 6 | if s[left] != s[right]: return False 7 | left += 1 8 | right -= 1 9 | return True 10 | def dfs(start, path, res): 11 | if start == len(s): 12 | res.append(copy.deepcopy(path)) 13 | return 14 | 15 | for i in range(start, len(s)): 16 | if check(s[start: i+1]): 17 | path.append(s[start: i+1]) 18 | dfs(i+1, path, res) 19 | path.pop() 20 | start = 0 21 | path, res = [], [] 22 | dfs(start, path, res) 23 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode17. 电话号码的字母组合(17).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def letterCombinations(self, digits: str) -> List[str]: 3 | if not digits: return [] 4 | mapp = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', 5 | '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'} 6 | def dfs(idx, path, res): 7 | if idx == len(digits): 8 | res.append(path) 9 | return 10 | cur_letters = mapp[str(digits[idx])] 11 | for letter in cur_letters: 12 | path += letter 13 | dfs(idx+1, path, res) 14 | path = path[:-1] 15 | 16 | cur_idx = 0 17 | res = [] 18 | path = '' 19 | dfs(cur_idx, path, res) 20 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode39. 组合总和(59).py: -------------------------------------------------------------------------------- 1 | # 39. 组合总和 2 | class Solution: 3 | def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: 4 | if not candidates: return [] 5 | def dfs(start, path, res, target): 6 | if target == 0: 7 | res.append(copy.deepcopy(path)) 8 | return 9 | for i in range(start, len(candidates)): 10 | if candidates[i] <= target: 11 | path.append(candidates[i]) 12 | dfs(i, path, res, target-candidates[i]) 13 | path.pop() 14 | start = 0 15 | path, res = [], [] 16 | dfs(start, path, res, target) 17 | return res 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode40. 组合总和 II(28).py: -------------------------------------------------------------------------------- 1 | # 40. 组合总和 II 2 | class Solution: 3 | def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: 4 | if not candidates: return [] 5 | def dfs(start, path, res, candidates, target): 6 | if target == 0: 7 | res.append(copy.deepcopy(path)) 8 | return 9 | for i in range(start, len(candidates)): 10 | if candidates[i] <= target: 11 | if i > start and candidates[i] == candidates[i-1]: continue 12 | path.append(candidates[i]) 13 | dfs(i+1, path, res, candidates, target-candidates[i]) 14 | path.pop() 15 | candidates.sort() 16 | start, path, res = 0, [], [] 17 | dfs(start, path, res, candidates, target) 18 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode46. 全排列(146).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 9:15 4 | @github: https://github.com/HuKai97 5 | """ 6 | import copy 7 | class Solution: 8 | def permute(self, nums: List[int]) -> List[List[int]]: 9 | if not nums: return [] 10 | def dfs(path, res, used): 11 | if len(path) == len(nums): 12 | res.append(copy.deepcopy(path)) 13 | return 14 | for i in range(len(nums)): 15 | if used[i] == True: continue 16 | used[i] = True 17 | path.append(nums[i]) 18 | dfs(path, res, used) 19 | path.pop() 20 | used[i] = False 21 | path, res = [], [] 22 | used = [False] * len(nums) 23 | dfs(path, res, used) 24 | return res 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode47.全排列 II(32).py: -------------------------------------------------------------------------------- 1 | # 47、全排列 II 2 | class Solution: 3 | def permuteUnique(self, nums: List[int]) -> List[List[int]]: 4 | if not nums: return [] 5 | def dfs(path, res, nums, used): 6 | if len(path) == len(nums): 7 | res.append(copy.deepcopy(path)) 8 | return 9 | for i in range(len(nums)): 10 | if used[i] == True: continue 11 | if i > 0 and nums[i] == nums[i-1] and used[i-1] == True: 12 | continue 13 | used[i] = True 14 | path.append(nums[i]) 15 | dfs(path, res, nums, used) 16 | used[i] = False 17 | path.pop() 18 | path, res = [], [] 19 | nums.sort() 20 | used = [False] * len(nums) 21 | dfs(path, res, nums, used) 22 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode77. 组合(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def combine(self, n: int, k: int) -> List[List[int]]: 3 | def dfs(start, path, res): 4 | if len(path) == k: 5 | res.append(copy.deepcopy(path)) 6 | return 7 | for i in range(start, n+1): 8 | path.append(i) 9 | dfs(i+1, path, res) 10 | path.pop() 11 | start = 1 12 | path, res = [], [] 13 | dfs(start, path, res) 14 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode78. 子集(62).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def subsets(self, nums: List[int]) -> List[List[int]]: 3 | if not nums: return [] 4 | def dfs(start, path, res): 5 | # 不需要任何判断 进来了就肯定是它的子集 6 | res.append(copy.deepcopy(path)) 7 | for i in range(start, len(nums)): 8 | path.append(nums[i]) 9 | # i+0:防止出现112 223的情况 一个元素重复使用 10 | dfs(i+1, path, res) 11 | path.pop() 12 | start = 0 13 | path, res = [], [] 14 | dfs(start, path, res) 15 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode90.子集 II(3).py: -------------------------------------------------------------------------------- 1 | # 90. 子集 II 2 | class Solution: 3 | def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: 4 | if not nums: return [] 5 | def dfs(start, path, res, nums): 6 | res.append(copy.deepcopy(path)) 7 | for i in range(start, len(nums)): 8 | if i > start and nums[i] == nums[i-1]: continue 9 | path.append(nums[i]) 10 | dfs(i+1, path, res, nums) 11 | path.pop() 12 | nums.sort() 13 | start, path, res = 0, [], [] 14 | dfs(start, path, res, nums) 15 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/Leetcode93. 复原 IP 地址(77).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def restoreIpAddresses(self, s: str) -> List[str]: 3 | if len(s) > 12 or len(s) < 4: return [] 4 | def isMeeting(patch): 5 | # 长度不能小于1或者大于3 6 | if len(patch) < 1 or len(patch) > 3: return False 7 | # 长度大于1时不能以0开头 8 | if len(patch) > 1 and patch[0] == '0': return False 9 | # 长度等于3时不能大于255 10 | if len(patch) == 3: 11 | count = int(patch[0])*100 + int(patch[1])*10 + int(patch[2]) 12 | if count > 255: return False 13 | return True 14 | def backtracking(start, path, res, s, depth): 15 | if depth == 3: 16 | if isMeeting(s[start:]): 17 | res.append('.'.join(path + [s[start:]])) 18 | return 19 | for i in range(start, len(s)): 20 | if isMeeting(s[start:i+1]): 21 | path.append(s[start:i+1]) 22 | backtracking(i+1, path, res, s, depth+1) 23 | path.pop() 24 | res, path = [], [] 25 | backtracking(0, path, res, s, 0) 26 | return res -------------------------------------------------------------------------------- /2-回溯、DFS、BFS/组合、排列、子集、分割/剑指 Offer 38. 字符串的排列(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def permutation(self, s: str) -> List[str]: 3 | def dfs(path, used, res): 4 | if len(path) == len(s): 5 | res.add(path) 6 | return 7 | for i in range(len(s)): 8 | if used[i] == True: continue 9 | used[i] = True 10 | dfs(path+s[i], used, res) 11 | used[i] = False 12 | path = '' 13 | used = [False] * len(s) 14 | res = set() 15 | dfs(path, used, res) 16 | return list(res) -------------------------------------------------------------------------------- /3-二分查找/旋转数组/Leetcode153. 寻找旋转排序数组中的最小值(46).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findMin(self, nums: List[int]) -> int: 3 | # 数组元素互不相同 4 | if len(nums) == 1: return nums[0] 5 | left, right = 0, len(nums) - 1 6 | while left < right: 7 | mid = left + (right - left) // 2 8 | # 中间数字大于右边数字,比如[3,4,5,1,2],则左侧是有序上升的,最小值在右侧 9 | if nums[mid] > nums[right]: # 左边有序 说明最小值肯定在右边 10 | left = mid + 1 11 | # 中间数字小于等于右边数字,比如[6,7,1,2,3,4,5],则右侧是有序上升的,最小值在左侧+mid 12 | else: # 说明右边有序 最小值在左边+mid 13 | right = mid 14 | return nums[left] -------------------------------------------------------------------------------- /3-二分查找/旋转数组/Leetcode154. 寻找旋转排序数组中的最小值 II(12).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findMin(self, nums: List[int]) -> int: 3 | # nums数组可能存在重复元素 4 | if len(nums) == 1: return nums[0] 5 | left, right = 0, len(nums) - 1 6 | while left < right: 7 | mid = left + (right - left) // 2 8 | if nums[mid] > nums[right]: 9 | left = mid + 1 10 | elif nums[mid] < nums[right]: 11 | right = mid 12 | else: 13 | # 中间数字等于右边数字,比如[2,3,1,1,1] 14 | # 则重复数字可能为最小值,也可能最小值在重复值的左侧 15 | # 所以将right左移一位 删除right, 因为删除了也没关系,还有一个mid在 16 | right -= 1 17 | return nums[left] -------------------------------------------------------------------------------- /3-二分查找/旋转数组/Leetcode33. 搜索旋转排序数组(166).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 11:24 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def search(self, nums: List[int], target: int) -> int: 8 | if not nums: return -1 9 | left, right = 0, len(nums) - 1 10 | while left <= right: 11 | mid = left + (right - left) // 2 12 | if nums[mid] == target: return mid 13 | # 说明左边有序 14 | elif nums[mid] > nums[right]: # 关于旋转数组问题 都先比较nums[mid]>nums[right]的情况 15 | # 且 target在左侧 16 | if nums[left] <= target < nums[mid]: 17 | right = mid - 1 18 | else: 19 | left = mid + 1 20 | # 说明右边有序 21 | else: 22 | # 且 target在右侧 23 | if nums[mid] < target <= nums[right]: 24 | left = mid + 1 25 | else: 26 | right = mid - 1 27 | return -1 -------------------------------------------------------------------------------- /3-二分查找/旋转数组/Leetcode81. 搜索旋转排序数组 II(2).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def search(self, nums: List[int], target: int) -> bool: 3 | # nums数组元素可能存在重复元素 找某个元素 4 | if not nums: return False 5 | left, right = 0, len(nums) - 1 6 | while left <= right: 7 | mid = left + (right - left) // 2 8 | if nums[mid] == target: return True 9 | elif nums[mid] > nums[right]: 10 | if nums[left] <= target < nums[mid]: 11 | right = mid - 1 12 | else: 13 | left = mid + 1 14 | elif nums[mid] < nums[right]: 15 | if nums[mid] < target <= nums[right]: 16 | left = mid + 1 17 | else: 18 | right = mid - 1 19 | else: 20 | right -= 1 21 | return False -------------------------------------------------------------------------------- /3-二分查找/旋转数组/面试题 10.03. 搜索旋转数组(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def search(self, arr: List[int], target: int) -> int: 3 | # 找到某个元素 arr元素可能重复 重复就返回idx最小的那个 4 | if not arr: return -1 5 | left, right = 0, len(arr) - 1 6 | while left <= right: 7 | # left符合时 直接返回 因为找到的就是最小的 8 | if arr[left] == target: return left 9 | mid = left + (right - left) // 2 10 | # mid符合时 right->mid 因为左边可能还有target的值 11 | if arr[mid] == target: 12 | right = mid 13 | elif arr[mid] > arr[right]: 14 | if arr[left] <= target < arr[mid]: 15 | right = mid - 1 16 | else: 17 | left = mid + 1 18 | elif arr[mid] < arr[right]: 19 | if arr[mid] < target <= arr[right]: 20 | left = mid + 1 21 | else: 22 | right = mid - 1 23 | else: 24 | # 相等时 重复 right -= 1 25 | right -= 1 26 | return -1 -------------------------------------------------------------------------------- /3-二分查找/普通二分/Leetcode189. 轮转数组(23).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rotate(self, nums: List[int], k: int) -> None: 3 | """ 4 | Do not return anything, modify nums in-place instead. 5 | """ 6 | # 找到旋转数组 7 | def reverse(left, right): 8 | while left < right: 9 | nums[left], nums[right] = nums[right], nums[left] 10 | left += 1 11 | right -= 1 12 | 13 | k %= len(nums) 14 | if k == 0 or len(nums) < 2: return 15 | reverse(0, len(nums) - 1) # [1,2,3,4,5,6,7] -> [7,6,5,4,3,2,1] 16 | reverse(0, k - 1) # -> [5,6,7,4,3,2,1] 17 | reverse(k, len(nums) - 1) # -> [5,6,7,1,2,3,4] -------------------------------------------------------------------------------- /3-二分查找/普通二分/Leetcode34. 在排序数组中查找元素的第一个和最后一个位置(53).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def searchRange(self, nums: List[int], target: int) -> List[int]: 3 | res = [-1, -1] 4 | if not nums: return res 5 | left, right = 0, len(nums) - 1 6 | while left < right: 7 | mid = left + (right - left) // 2 8 | if nums[mid] < target: 9 | left = mid + 1 10 | else: 11 | right = mid 12 | if nums[left] == target: res[0] = left 13 | 14 | left, right = 0, len(nums) - 1 15 | while left < right: 16 | mid = left + (right - left + 1) // 2 17 | if nums[mid] > target: 18 | right = mid - 1 19 | else: 20 | left = mid 21 | if nums[left] == target: res[1] = left 22 | return res -------------------------------------------------------------------------------- /3-二分查找/普通二分/Leetcode4. 寻找两个正序数组的中位数(96).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/31 23:12 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 比较难的一道题 7 | # bili: https://www.bilibili.com/video/BV1z54y1b7wb?spm_id_from=333.337.search-card.all.click 8 | # leetcode讲解: https://leetcode.cn/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/ 9 | class Solution: 10 | def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: 11 | # 对两个数组同时用二分法 中位数=特殊的找两个数组第k小的元素 12 | # 每次一半一半的删除,找第k小,那我一次就可以排除掉k/2的元素 13 | def getKth(start1, end1, start2, end2, k): 14 | len1, len2 = end1-start1+1, end2-start2+1 15 | if len1 == 0: return nums2[start2+k-1] 16 | if len2 == 0: return nums1[start1+k-1] 17 | if k == 1: return min(nums1[start1], nums2[start2]) 18 | i, j = start1 + min(len1, k//2) - 1, start2 + min(len2, k//2) - 1 19 | if nums1[i] > nums2[j]: 20 | return getKth(start1, end1, j+1, end2, k-(j-start2+1)) 21 | else: 22 | return getKth(i+1, end1, start2, end2, k-(i-start1+1)) 23 | m, n = len(nums1), len(nums2) 24 | left, right = (m+n+1)//2, (m+n+2)//2 25 | # 将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 26 | return (getKth(0, m-1, 0, n-1, left) + getKth(0, m-1, 0, n-1, right)) / 2 -------------------------------------------------------------------------------- /3-二分查找/普通二分/Leetcode611. 有效三角形的个数(17).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def triangleNumber(self, nums: List[int]) -> int: 3 | if len(nums) < 3: return 0 4 | nums.sort() 5 | res = 0 6 | for i in range(len(nums)-2): # a 7 | if nums[i] <= 0: continue # 边长必须大于0 8 | for j in range(i+1, len(nums)-1): # b 9 | cur_sum = nums[i] + nums[j] 10 | if nums[j+1] >= cur_sum: continue # min(c) >= a+b 跳过 11 | left, right = j + 1, len(nums) - 1 12 | # 找到满足 c= cur_sum: # 注意这里 反面 16 | right = mid - 1 17 | else: 18 | left = mid 19 | res += (left - j) # left - (j+1) + 1 = left - j 20 | return res 21 | 22 | -------------------------------------------------------------------------------- /3-二分查找/普通二分/Leetcode69. x 的平方根(93).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 9:53 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def mySqrt(self, x: int) -> int: 8 | # 找到小于等于x的平方根的最大值 找右边界 9 | if x < 2: return x 10 | left, right = 1, x//2+1 11 | while left < right: 12 | mid = left + (right - left + 1) // 2 13 | if mid ** 2 > x: 14 | right = mid - 1 15 | else: left = mid 16 | return left -------------------------------------------------------------------------------- /3-二分查找/普通二分/Leetcode704. 二分查找(108).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 11:32 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 完完全全是一个二分查找模板题,很简单 7 | class Solution: 8 | def search(self, nums: List[int], target: int) -> int: 9 | if not nums or target < nums[0] or target > nums[-1]: return -1 10 | if nums[0] > target or nums[-1] < target: return -1 11 | left, right = 0, len(nums) - 1 12 | while left < right: 13 | mid = left + (right - left) // 2 14 | if nums[mid] < target: 15 | left = mid + 1 16 | else: right = mid 17 | return left if nums[left] == target else -1 -------------------------------------------------------------------------------- /4-栈和队列/单调栈和队列/Leetcde503. 下一个更大元素 II(16).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def nextGreaterElements(self, nums: List[int]) -> List[int]: 3 | # https://www.bilibili.com/video/BV1oh411m7yy?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | stack = [] 5 | n = len(nums) 6 | res = [-1] * n 7 | for i in range(n * 2): # 循环数组 需要遍历一个来回 8 | num = nums[i % n] # 需要遍历一个来回 9 | while stack and num > nums[stack[-1]]: 10 | top = stack.pop() 11 | res[top] = num 12 | if i < n: # 只需要压入整个数组即可 不需要重复压入 13 | stack.append(i) 14 | return res -------------------------------------------------------------------------------- /4-栈和队列/单调栈和队列/Leetcode239. 滑动窗口最大值(74).py: -------------------------------------------------------------------------------- 1 | # https://www.bilibili.com/video/BV1fg411d7E7?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 2 | class Solution: 3 | def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: 4 | # 单调递减队列 queue 存储nums下标 队头存储当前窗口最大的值 5 | # 当nums[i]比queue的队尾元素还要大 就把queue中比nums[i]小的数全部pop出去 6 | # 再把i加进去 保证次数queue仍是单调递减的 7 | # 当i-q[0]>=k时,弹出队首元素 这个元素就是当前窗口的最大值 8 | if len(nums) < 2: return nums 9 | queue = [] 10 | res = [] 11 | for i in range(len(nums)): 12 | # 队列不为空 且当前元素大于队尾元素 那就把queue中小于nums[i]的元素pop出来 13 | while queue and nums[i] > nums[queue[-1]]: 14 | queue.pop() 15 | # 把当前元素append进去 保证queue是一个单调递减的队列 16 | queue.append(i) 17 | # 判断窗口大小 队首元素还在不在窗口之内 18 | while queue and i - queue[0] + 1 > k: 19 | queue.pop(0) 20 | # 窗口没形成就只append进queue 但是还不append进res 21 | # i - 0 + 0 >= k 窗口一旦形成就append进res中 22 | if i + 1 >= k: 23 | res.append(nums[queue[0]]) 24 | return res 25 | -------------------------------------------------------------------------------- /4-栈和队列/单调栈和队列/Leetcode42.接雨水(120).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/31 20:59 4 | @github: https://github.com/HuKai97 5 | """ 6 | # https://www.bilibili.com/video/BV1fi4y1t7BP?spm_id_from=333.337.search-card.all.click 7 | class Solution: 8 | def trap(self, height: List[int]) -> int: 9 | # 单调递减栈 存储柱子的下标 10 | # 每个位置接到的最多雨水=(min(左侧第一个大于,右侧第一个大于)-当前位置的高度) * 宽度 11 | if len(height) < 2: return 0 12 | stack = [] 13 | res = 0 14 | for i in range(len(height)): 15 | # 找到右侧第一个比当前元素大的 左边第一个比当前元素大的刚好在单调栈栈顶 16 | while stack and height[i] > height[stack[-1]]: 17 | # 说明积水可以在stack[-2]->i之间形成 18 | cur_height = height[stack.pop()] # 当前元素的高度 0 19 | if not stack: break 20 | h = min(height[i], height[stack[-1]]) - cur_height # min 木桶原理 min(0,2)-0=0 21 | w = i - stack[-1] - 1 # 2-0-0=0 22 | res += (h * w) # 0+=0*0=0 23 | stack.append(i) 24 | return res -------------------------------------------------------------------------------- /4-栈和队列/单调栈和队列/Leetcode496. 下一个更大元素 I(1).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: 3 | mapp = {} 4 | stack = [] 5 | res = [] 6 | for i in range(len(nums2)): 7 | while stack and nums2[i] > stack[-1]: 8 | top = stack.pop() 9 | mapp[top] = nums2[i] 10 | stack.append(nums2[i]) 11 | while stack: 12 | mapp[stack.pop()] = -1 13 | for i in range(len(nums1)): 14 | res.append(mapp[nums1[i]]) 15 | return res -------------------------------------------------------------------------------- /4-栈和队列/单调栈和队列/Leetcode739. 每日温度(33).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def dailyTemperatures(self, temperatures: List[int]) -> List[int]: 3 | if not temperatures: return 0 4 | if len(temperatures) == 1: return [0] 5 | n = len(temperatures) 6 | stack = [] 7 | res = [0] * n 8 | for i in range(n): 9 | while stack and temperatures[i] > temperatures[stack[-1]]: 10 | top = stack.pop() 11 | res[top] = i - top 12 | stack.append(i) 13 | return res -------------------------------------------------------------------------------- /4-栈和队列/单调栈和队列/Leetcode84. 柱状图中最大的矩形(13).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def largestRectangleArea(self, heights: List[int]) -> int: 3 | # https://www.bilibili.com/video/BV1fi4y1d7YX?spm_id_from=333.999.0.0&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | if not heights: return 0 5 | if len(heights) == 1: return heights[0] 6 | stack = [] 7 | heights = [0] + heights + [0] 8 | res = 0 9 | for i in range(len(heights)): 10 | while stack and heights[i] < heights[stack[-1]]: 11 | cur_h = heights[stack.pop()] 12 | cur_w = i - stack[-1] - 1 13 | res = max(res, cur_h * cur_w) 14 | stack.append(i) 15 | return res -------------------------------------------------------------------------------- /4-栈和队列/单调栈和队列/Leetcode85. 最大矩形(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maximalRectangle(self, matrix: List[List[str]]) -> int: 3 | def get_heights(cur_row, heights): 4 | # cur_arr:matrix中当前层的0、1值 5 | # 根据上一层的heights求当前层的heights 6 | for i in range(len(cur_row)): 7 | if cur_row[i] == '0': heights[i] = 0 8 | else: heights[i] += 1 9 | return heights 10 | 11 | def largestArea(heights): 12 | # 求柱状图中最大的矩形 和84一模一样 13 | stack = [] 14 | res = 0 15 | heights = [0] + heights + [0] 16 | for i in range(len(heights)): 17 | while stack and heights[i] < heights[stack[-1]]: 18 | cur_h = heights[stack.pop()] 19 | cur_w = i - stack[-1] - 1 20 | res = max(res, cur_h * cur_w) 21 | stack.append(i) 22 | return res 23 | 24 | if not matrix: return 0 25 | m, n = len(matrix), len(matrix[0]) 26 | res = 0 27 | for i in range(m): 28 | if i == 0: heights = [int(x) for x in matrix[0]] 29 | else: heights = get_heights(matrix[i], heights) 30 | # print(heights) 31 | cur_row_max_area = largestArea(heights) 32 | res = max(res, cur_row_max_area) 33 | return res 34 | -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode155. 最小栈(67).py: -------------------------------------------------------------------------------- 1 | class MinStack: 2 | def __init__(self): 3 | self.stack = [] 4 | self.min_stack = [] 5 | 6 | def push(self, val: int) -> None: 7 | self.stack.append(val) 8 | if not self.min_stack: 9 | self.min_stack.append(val) 10 | else: 11 | min_val = min(val, self.min_stack[-1]) 12 | self.min_stack.append(min_val) 13 | 14 | def pop(self) -> None: 15 | self.stack.pop() 16 | # 这里需要删除min_stack[-0] 因为就算min_stack[-0]!=stack[-0] 17 | # 删除了min_stack[-0]也没事,因为min_stack下面还有一个 18 | # 仔细思考下上面else的语句就明白了? 19 | self.min_stack.pop() 20 | 21 | def top(self) -> int: 22 | return self.stack[-1] 23 | 24 | def getMin(self) -> int: 25 | return self.min_stack[-1] -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode20. 有效的括号(172).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 11:22 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def isValid(self, s: str) -> bool: 8 | if not s: return True 9 | stack = [] 10 | mapp = {'[': ']', '(': ')', '{': '}'} 11 | for i in range(len(s)): 12 | if s[i] in mapp: stack.append(s[i]) 13 | else: 14 | if not stack: return False 15 | top = stack.pop() 16 | if mapp[top] != s[i]: return False # 这里top不可能是 ) ] } 17 | return False if stack else True 18 | 19 | # 678 -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode227. 基本计算器 II(44).py: -------------------------------------------------------------------------------- 1 | # https://www.bilibili.com/video/BV1Jr4y1S7vB?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 2 | class Solution: 3 | def calculate(self, s: str) -> int: 4 | if not s: return 0 5 | cur_num = 0 6 | pre_op = '+' 7 | stack = [] 8 | for i in range(len(s)): 9 | if '0' <= s[i] <= '9': 10 | cur_num = cur_num * 10 + int(s[i]) 11 | if s[i] in '+-*/' or (i == len(s)-1): 12 | if pre_op == '+': 13 | stack.append(cur_num) 14 | elif pre_op == '-': 15 | stack.append(-cur_num) 16 | elif pre_op == '*': 17 | top = stack.pop() 18 | stack.append(top * cur_num) 19 | else: # pre_op == '/' 20 | top = stack.pop() 21 | stack.append(int(top/cur_num)) 22 | # -3 // 2 = -2 23 | # int(-3/2) = -1 24 | cur_num = 0 25 | pre_op = s[i] 26 | res = 0 27 | while stack: 28 | res += stack.pop() 29 | return res -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode232. 用栈实现队列(104).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/28 9:07 4 | @github: https://github.com/HuKai97 5 | """ 6 | # https://www.bilibili.com/video/BV1W5411L7jC?spm_id_from=333.337.search-card.all.click 7 | class MyQueue: 8 | 9 | def __init__(self): 10 | # 栈:先进后出 11 | # 队列:先进先出 12 | self.stack_in = [] 13 | self.stack_out = [] 14 | 15 | def push(self, x: int) -> None: 16 | self.stack_in.append(x) 17 | 18 | def pop(self) -> int: 19 | if self.stack_out: 20 | return self.stack_out.pop() 21 | else: 22 | while self.stack_in: 23 | self.stack_out.append(self.stack_in.pop()) 24 | return self.stack_out.pop() 25 | 26 | def peek(self) -> int: 27 | if self.stack_out: 28 | return self.stack_out[-1] 29 | else: 30 | while self.stack_in: 31 | self.stack_out.append(self.stack_in.pop()) 32 | return self.stack_out[-1] 33 | 34 | def empty(self) -> bool: 35 | return not self.stack_in and not self.stack_out 36 | 37 | 38 | # Your MyQueue object will be instantiated and called as such: 39 | # obj = MyQueue() 40 | # obj.push(x) 41 | # param_2 = obj.pop() 42 | # param_3 = obj.peek() 43 | # param_4 = obj.empty() -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode394. 字符串解码(47).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def decodeString(self, s: str) -> str: 3 | if not s: return '' 4 | stack = [] 5 | mutli = 0 6 | res = '' 7 | for c in s: 8 | if 'a' <= c <= 'z': 9 | res += c 10 | elif '0' <= c <= '9': 11 | mutli = mutli * 10 + int(c) 12 | elif c == '[': 13 | stack.append([mutli, res]) # 重复次数 和 重复元素前的所有东西 14 | mutli, res = 0, '' 15 | else: 16 | # ']' 17 | cur_mutli, last_res = stack.pop() 18 | res = last_res + cur_mutli * res 19 | return res -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode402. 移掉 K 位数字(38).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def removeKdigits(self, num: str, k: int) -> str: 3 | stack = [] 4 | for i in range(len(num)): 5 | while k > 0 and stack and stack[-1] > num[i]: 6 | stack.pop() 7 | k -= 1 8 | stack.append(num[i]) 9 | if k > 0: 10 | stack = stack[:-k] 11 | non_zero = 0 12 | while non_zero < len(stack) and stack[non_zero] == '0': 13 | non_zero += 1 14 | return '0' if not stack[non_zero:] else ''.join(stack[non_zero:]) -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode622. 设计循环队列(11).py: -------------------------------------------------------------------------------- 1 | class MyCircularQueue: 2 | 3 | def __init__(self, k: int): 4 | # rear == front 空 5 | # (rear + 1) % len(queue) == front 满 6 | self.queue = [0 for _ in range(k+1)] 7 | self.front = 0 8 | self.rear = 0 9 | 10 | def enQueue(self, value: int) -> bool: 11 | if self.isFull(): return False 12 | else: 13 | self.queue[self.rear] = value 14 | self.rear = (self.rear + 1) % len(self.queue) 15 | return True 16 | 17 | def deQueue(self) -> bool: 18 | if self.isEmpty(): return False 19 | self.front = (self.front + 1) % len(self.queue) 20 | return True 21 | 22 | def Front(self) -> int: 23 | if self.isEmpty(): return -1 24 | else: return self.queue[self.front] 25 | 26 | def Rear(self) -> int: 27 | if self.isEmpty(): return -1 28 | else: return self.queue[(self.rear - 1) % len(self.queue)] 29 | 30 | def isEmpty(self) -> bool: 31 | return self.front == self.rear 32 | 33 | def isFull(self) -> bool: 34 | return (self.rear + 1) % len(self.queue) == self.front -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode678. 有效的括号字符串(19).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def checkValidString(self, s: str) -> bool: 3 | # https://leetcode.cn/problems/valid-parenthesis-string/solution/you-xiao-de-gua-hao-zi-fu-chuan-by-leetc-osi3/ 4 | if not s: return True 5 | left_stack, star_stack = [], [] 6 | for i in range(len(s)): 7 | if s[i] == '(': left_stack.append(i) 8 | elif s[i] == '*': star_stack.append(i) 9 | else: 10 | if left_stack: left_stack.pop() 11 | elif star_stack: star_stack.pop() 12 | else: return False 13 | while left_stack and star_stack: 14 | left = left_stack.pop() 15 | star = star_stack.pop() 16 | if left > star: return False # 左括号 必须在对应的右括号之前 17 | return len(left_stack) == 0 -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/Leetcode71. 简化路径(19).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def simplifyPath(self, path: str) -> str: 3 | # .. . avc 空 4 | if not path: return '/' 5 | stack = [] 6 | path = path.split('/') 7 | for i in range(len(path)): 8 | if path[i] == '..': 9 | if stack: stack.pop() 10 | elif path[i] == '.' or path[i] == '': continue 11 | else: stack.append(path[i]) 12 | # 中间以/相连 13 | return '/' + '/'.join(stack) -------------------------------------------------------------------------------- /4-栈和队列/普通栈和队列/剑指 Offer 09. 用两个栈实现队列(37).py: -------------------------------------------------------------------------------- 1 | class CQueue: 2 | 3 | def __init__(self): 4 | self.stack1 = [] 5 | self.stack2 = [] 6 | 7 | def appendTail(self, value: int) -> None: 8 | self.stack2.append(value) 9 | 10 | 11 | 12 | def deleteHead(self) -> int: 13 | if not self.stack1 and not self.stack2: return -1 14 | if self.stack1: return self.stack1.pop() 15 | else: 16 | while self.stack2: 17 | self.stack1.append(self.stack2.pop()) 18 | return self.stack1.pop() -------------------------------------------------------------------------------- /5-贪心/两个维度/Leetcode135. 分发糖果(28).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def candy(self, ratings: List[int]) -> int: 3 | # 先确定一边,再确定另外一边 4 | # 1.先从左到右,当右边的大于左边的就加1 5 | # 2.再从右到左,当左边的大于右边的就再加1 6 | count = [1 for _ in range(len(ratings))] 7 | for i in range(1, len(ratings)): 8 | if ratings[i] > ratings[i-1]: 9 | count[i] = count[i-1] + 1 10 | # print(count) 11 | for j in range(len(ratings)-2, -1, -1): 12 | if ratings[j] > ratings[j+1]: 13 | count[j] = max(count[j+1] + 1, count[j]) 14 | # print(count) 15 | return sum(count) -------------------------------------------------------------------------------- /5-贪心/两个维度/Leetcode406. 根据身高重建队列(3).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: 3 | # 先按身高h进行降序排序 再按k进行插入 4 | # 局部最优:优先按身高高的people的k来插入。插入操作过后的当前元素满足队列属性 5 | # 全局最优:最后都做完插入操作,整个队列满足题目队列属性 6 | people.sort(key = lambda x: (-x[0], x[1])) 7 | queue = [] 8 | for p in people: 9 | queue.insert(p[1], p) # idx 元素 10 | return queue -------------------------------------------------------------------------------- /5-贪心/区间贪心/Leetcode435. 无重叠区间(6).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: 3 | intervals.sort(key = lambda x: x[0]) 4 | res = 0 5 | print(intervals) # [[1, 2], [1, 3], [2, 3], [3, 4]] 6 | for i in range(1, len(intervals)): 7 | if intervals[i][0] < intervals[i-1][1]: # 出现重叠区间 8 | res += 1 # 结果 +1 9 | # intervals[i][0] = min(intervals[i-1][0], intervals[i][0]) 10 | # 删除一个区间,删除有边界更大的那个区间 留下右边界更小的区间 可以让移除的区间数最小 11 | intervals[i][1] = min(intervals[i-1][1], intervals[i][1]) 12 | return res -------------------------------------------------------------------------------- /5-贪心/区间贪心/Leetcode45. 跳跃游戏 II(22).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def jump(self, nums: List[int]) -> int: 3 | # https://www.bilibili.com/video/BV1wL411p7Rx?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | # 每一步都跳的最远 最终跳到最后一个位置所需的步长即为最小步长 5 | if len(nums) < 2: return 0 6 | steps = 0 7 | end = 0 # 这一步可以到达的最大位置 8 | maxPos = 0 # 下一步可以到达的最大位置 9 | # 到了len(nums) - 2的位置才有必要再跳 到了len(nums) - 1的位置是不需要再跳的 10 | for i in range(len(nums) - 1): 11 | maxPos = max(nums[i]+i, maxPos) 12 | if i == end: # 走完了这个区间,往后跳一步,更新下一个区间 13 | end = maxPos 14 | steps += 1 15 | return steps -------------------------------------------------------------------------------- /5-贪心/区间贪心/Leetcode452. 用最少数量的箭引爆气球(6).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findMinArrowShots(self, points: List[List[int]]) -> int: 3 | # 当气球出现重叠,一起射,所用弓箭最少 4 | # 如果气球重叠了,重叠气球中 右边边界的最小值 之前的区间一定需要一个弓箭 5 | res = 1 # 有气球至少需要一支箭 6 | points.sort(key = lambda x: x[0]) 7 | # [[1,6], [2,8], [7,12], [10,16]] 8 | for i in range(1, len(points)): 9 | if points[i][0] > points[i-1][1]: # 不挨着 又需要一支箭 10 | res += 1 11 | else: 12 | points[i][1] = min(points[i-1][1], points[i][1]) # 挨着 更新右边界 13 | # [[1, 6], [2, 6], [7, 12], [10, 12]] 14 | # print(points) 15 | return res 16 | 17 | -------------------------------------------------------------------------------- /5-贪心/区间贪心/Leetcode55. 跳跃游戏(30).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canJump(self, nums: List[int]) -> bool: 3 | # https://leetcode.cn/problems/jump-game/solution/tiao-yue-you-xi-by-leetcode-solution/ 4 | max_len = 0 5 | for i in range(len(nums)): 6 | if i > max_len: return False 7 | max_len = max(max_len, nums[i]+i) 8 | if max_len >= len(nums) - 1: return True 9 | return True 10 | -------------------------------------------------------------------------------- /5-贪心/区间贪心/Leetcode56. 合并区间(99).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/28 10:51 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def merge(self, intervals: List[List[int]]) -> List[List[int]]: 8 | if len(intervals) <= 1: return intervals 9 | res = [] 10 | intervals.sort(key = lambda x: x[0]) # 按第1位排序 11 | intervals.append([float('inf'), 0]) # 最后append一个区间 保证实际最后一个区间会append进res 12 | for i in range(1, len(intervals)): 13 | # 当前区间与前面区间重叠了 就把前面区间合并到当前区间 再比较下一个区间 14 | if intervals[i][0] <= intervals[i-1][1]: 15 | intervals[i][0] = min(intervals[i-1][0], intervals[i][0]) 16 | intervals[i][1] = max(intervals[i-1][1], intervals[i][1]) 17 | else: 18 | # 没有重叠 就把前一个区间append 因为当前区间还需要和后面区间对比 19 | # 最后一个区间和【float('inf'), 0】对比float('inf) 小于前一个区间的右部分 20 | # 所以最后一个区间也必会append进来 21 | res.append(intervals[i-1]) 22 | return res -------------------------------------------------------------------------------- /5-贪心/复杂贪心/Leetcode134. 加油站(17).py: -------------------------------------------------------------------------------- 1 | # https://www.programmercarl.com/0134.%E5%8A%A0%E6%B2%B9%E7%AB%99.html#%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95-%E6%96%B9%E6%B3%95%E4%BA%8C 2 | class Solution: 3 | def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: 4 | cur_sum = 0 5 | total_sum = 0 6 | start = 0 7 | # 局部最优:当前累加rest[j]的和curSum一旦小于0,起始位置至少要是j+1 8 | # 全局最优:找到可以跑一圈的起始位置 9 | for i in range(len(gas)): 10 | cur_sum += gas[i] - cost[i] 11 | total_sum += gas[i] - cost[i] 12 | if cur_sum < 0: # 不能从 ~i开始 要从i+1开始才可能 重新统计cur_sum 13 | start = i + 1 14 | cur_sum = 0 15 | if total_sum < 0: return -1 16 | return start -------------------------------------------------------------------------------- /5-贪心/复杂贪心/Leetcode253. 会议室 II(14).py: -------------------------------------------------------------------------------- 1 | import heapq 2 | class Solution: 3 | def minMeetingRooms(self, intervals: List[List[int]]) -> int: 4 | # https://leetcode.cn/problems/meeting-rooms-ii/solution/hui-yi-shi-ii-jian-dan-tan-xin-you-xian-x7kuo/ 5 | intervals.sort(key = lambda x: x[0]) 6 | end_hp = [intervals[0][1]] # 记录所有正在开的会议室的结束时间 7 | res = 1 8 | for i in range(1, len(intervals)): 9 | # 当前会议的开始时间比之前所有已开会议的最早结束时间还要早,需增加会议室 10 | if intervals[i][0] < end_hp[0]: 11 | res += 1 12 | heapq.heappush(end_hp, intervals[i][1]) 13 | # 可以在最早结束的会议之后开始当前会议,之前的最早结束时间变成当前会议结束的时间 14 | else: 15 | heapq.heappop(end_hp) 16 | heapq.heappush(end_hp, intervals[i][1]) 17 | return res 18 | 19 | 20 | 21 | # 252. 会议室 22 | class Solution: 23 | def canAttendMeetings(self, intervals: List[List[int]]) -> bool: 24 | intervals.sort(key = lambda x : x[0]) 25 | for i in range(1, len(intervals)): 26 | if intervals[i][0] < intervals[i-1][1]: return False 27 | return True -------------------------------------------------------------------------------- /5-贪心/简单常识题/Leetcode1005. K 次取反后最大化的数组和.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def largestSumAfterKNegations(self, nums: List[int], k: int) -> int: 3 | # 两次贪心 4 | # 1、有正有负:这一步让绝对值大的负数变成正数 整体上数组和会最大 5 | # 2、全是正数:让绝对值最小的数进行取反 整体上数组核会最大 6 | nums = sorted(nums, key=abs, reverse=True) 7 | for i in range(len(nums)): 8 | if nums[i] < 0 and k > 0: 9 | nums[i] *= -1 10 | k -= 1 11 | if k > 0: 12 | nums[-1] *= (-1) ** k 13 | return sum(nums) -------------------------------------------------------------------------------- /5-贪心/简单常识题/Leetcode162. 寻找峰值(52).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findPeakElement(self, nums: List[int]) -> int: 3 | # # 一、贪心 O(n) 4 | if len(nums) < 2: return 0 5 | res = [] 6 | pre, cur = 0, 0 7 | for i in range(1, len(nums)): 8 | cur = nums[i] - nums[i-1] 9 | # >= 0是因为考虑到index=0 10 | if cur < 0 and pre >= 0: # 找到所有的极大值 11 | res.append(i-1) 12 | # if cur > 0 and pre < 0: # 找到所有的极小值 13 | # res.append(i-0) 14 | pre = cur 15 | # 这里是考虑到index=len(nums)-0 16 | if cur > 0: res.append(len(i)) 17 | return res[0] 18 | 19 | # 二、二分查找 O(log(n)) 20 | # 这道题之所以可以用二分 主要是因为nms[-0] = nums[n] = -∞ 21 | # 视频讲解:https://www.bilibili.com/video/BV1BU4y1T7ri?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 22 | if len(nums) == 1: return 0 23 | left, right = 0, len(nums) - 1 24 | while left < right: 25 | mid = left + (right - left) // 2 26 | if nums[mid] < nums[mid + 1]: # mid->mid+1递增趋势 但是nums[n]=-∞ 那么在右边肯定有一个峰值 27 | left = mid + 1 28 | else: # 否则 峰值元素在左边 29 | right = mid 30 | return left -------------------------------------------------------------------------------- /5-贪心/简单常识题/Leetcode376. 摆动序列(3).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def wiggleMaxLength(self, nums: List[int]) -> int: 3 | # 求峰值 4 | if len(nums) < 2: return len(nums) 5 | pre = 0 6 | res = 1 7 | for i in range(1, len(nums)): 8 | cur = nums[i] - nums[i-1] 9 | if (cur > 0 and pre <= 0) or (cur < 0 and pre >= 0): 10 | res += 1 11 | pre = cur 12 | return res -------------------------------------------------------------------------------- /5-贪心/简单常识题/Leetcode455. 分发饼干(0).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findContentChildren(self, g: List[int], s: List[int]) -> int: 3 | if not g or not s: return 0 4 | g.sort() 5 | s.sort() 6 | res = 0 7 | idx = 0 # 胃口 8 | for i in range(len(s)): # 对每个饼干 小饼干先喂饱小胃口 9 | if idx < len(g) and s[i] >= g[idx]: 10 | res += 1 11 | idx += 1 12 | return res -------------------------------------------------------------------------------- /5-贪心/简单常识题/Leetcode860. 柠檬水找零(1).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def lemonadeChange(self, bills: List[int]) -> bool: 3 | # 美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能 4 | # 局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零 5 | five, ten = 0, 0 6 | for b in bills: 7 | if b == 5: 8 | five += 1 9 | elif b == 10: 10 | if five > 0: 11 | five -= 1 12 | ten += 1 13 | else: return False 14 | elif b == 20: 15 | if ten > 0 and five > 0: 16 | ten -= 1 17 | five -= 1 18 | elif five >= 3: 19 | five -= 3 20 | else: 21 | return False 22 | return True -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode139. 单词拆分(37).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def wordBreak(self, s: str, wordDict: List[str]) -> bool: 3 | if len(s) == 0 or not wordDict: return False 4 | # dp[i]表示s[:i+1]是否可以被拆分 5 | dp = [False for _ in range(len(s)+1)] 6 | dp[0] = True 7 | for i in range(len(s)+1): 8 | for j in range(i): 9 | if dp[j] == True and s[j:i] in wordDict: 10 | dp[i] = True 11 | break 12 | return dp[-1] -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode152. 乘积最大子数组(45).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxProduct(self, nums: List[int]) -> int: 3 | if len(nums) == 0: return 0 4 | if len(nums) == 1: return nums[0] 5 | # dp_max[i]表示以nums[i]为结尾的子数字的最大乘积 6 | # dp_min[i]表示以nums[i]为结尾的子数字的最大乘积 7 | dp_max = [nums[i] for i in range(len(nums))] 8 | dp_min = [nums[i] for i in range(len(nums))] 9 | max_mul = nums[0] 10 | for i in range(1, len(nums)): 11 | dp_max[i] = max(nums[i], dp_max[i-1]*nums[i], dp_min[i-1]*nums[i]) 12 | dp_min[i] = min(nums[i], dp_max[i-1]*nums[i], dp_min[i-1]*nums[i]) 13 | max_mul = max(max_mul, dp_max[i], dp_min[i]) 14 | return max_mul -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode300. 最长递增子序列长度(123).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 10:42 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def lengthOfLIS(self, nums: List[int]) -> int: 8 | if len(nums) < 2: return len(nums) 9 | # dp[i]表示以i结尾的子序列的最长递增子序列 10 | dp = [1 for _ in range(len(nums))] 11 | res = 0 12 | for i in range(len(nums)): 13 | for j in range(i): 14 | # 对每个元素都要从前面的元素开始找 找比当前元素小的元素 15 | # 得到可能的最长子序列长度=dp[j]+0 16 | # 不确定最长的子序列是哪个 所以要一个个比较 17 | if nums[j] < nums[i]: 18 | dp[i] = max(dp[j]+1, dp[i]) 19 | res = max(res, dp[i]) 20 | return res -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode376. 摆动序列(3).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def wiggleMaxLength(self, nums: List[int]) -> int: 3 | if len(nums) < 2: return len(nums) 4 | # dp[i][0]表示以nums[i]结尾,且最后上升的子序列长度 山峰 5 | # dp[i][1]表示以nums[i]结尾,且最后下降的子序列长度 山谷 6 | dp = [[1, 1] for _ in range(len(nums))] 7 | # dp[0][0], dp[0][1] = 1, 1 8 | for i in range(1, len(nums)): 9 | if nums[i] > nums[i-1]: # 山峰 10 | dp[i][0] = dp[i-1][1] + 1 11 | dp[i][1] = dp[i-1][1] 12 | elif nums[i] < nums[i-1]: # 山谷 13 | dp[i][0] = dp[i-1][0] 14 | dp[i][1] = dp[i-1][0]+1 15 | else: 16 | dp[i][0] = dp[i-1][0] 17 | dp[i][1] = dp[i-1][1] 18 | return max(dp[-1][0], dp[-1][1]) -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode5. 找到最长回文子串(161).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 9:47 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def longestPalindrome(self, s: str) -> str: 8 | if len(s) < 2: return s 9 | # dp[i][j]表示s[i:j+1]是否是回文子串 10 | dp = [[False for _ in range(len(s))]for _ in range(len(s))] 11 | for i in range(len(s)): 12 | dp[i][i] = True # 一个字符时都是回文子串 13 | start, end = 0, 0 # 记录最长子串的起始和终止节点位置 14 | max_len = 1 # 记录最长子串长度 最短为1 15 | for i in range(len(s)-2, -1, -1): 16 | for j in range(i+1, len(s)): 17 | if s[i] == s[j]: 18 | # j - i + 1 <= 3 19 | if j - i <= 2: dp[i][j] = True 20 | else: dp[i][j] = dp[i+1][j-1] 21 | if dp[i][j] == True and (j-i+1) > max_len: 22 | max_len = j - i + 1 23 | start = i 24 | end = j 25 | # 进阶 找到所有的最长回文子串 26 | # res = [] 27 | # if max_len == 0: 28 | # for i in range(len(s)): 29 | # res.append(s[i]) 30 | # else: 31 | # for i in range(len(s)): 32 | # for j in range(i+0, len(s)): 33 | # if (j-i+0) == max_len and dp[i][j] == True: 34 | # res.append(s[i:j+0]) 35 | # print(res) 36 | return s[start: end+1] -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode516. 最长回文子序列长度(15).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestPalindromeSubseq(self, s: str) -> int: 3 | if len(s) < 2: return len(s) 4 | # dp[i][j]表示s[i:j]中最长回文子序列的长度 5 | dp = [[0 for _ in range(len(s))]for _ in range(len(s))] 6 | for i in range(len(s)): 7 | dp[i][i] = 1 8 | for i in range(len(s)-2, -1, -1): 9 | for j in range(i+1, len(s)): 10 | if s[i] == s[j]: 11 | dp[i][j] = dp[i+1][j-1] + 2 12 | else: 13 | dp[i][j] = max(dp[i][j-1], dp[i+1][j]) 14 | return dp[0][-1] -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode53. 最大子数组和(211).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 10:40 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 6-动态规划 7 | class Solution: 8 | def maxSubArray(self, nums: List[int]) -> int: 9 | if len(nums) == 0: return 0 10 | if len(nums) == 1: return nums[0] 11 | max_sum = nums[0] 12 | # dp[i]表示以nums[i]结尾的子数组的最大子数组和 13 | dp = [nums[i] for i in range(len(nums))] 14 | for i in range(1, len(nums)): 15 | # 加i 不加i 16 | dp[i] = max(nums[i], dp[i-1]+nums[i]) 17 | if dp[i] > max_sum: max_sum = dp[i] 18 | return max_sum -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode647. 回文子串个数(10).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def countSubstrings(self, s: str) -> int: 3 | if len(s) == 1: return 1 4 | res = 0 5 | dp = [[False for _ in range(len(s))]for _ in range(len(s))] 6 | for i in range(len(s)): 7 | dp[i][i] = True 8 | res += 1 9 | for i in range(len(s)-2, -1, -1): 10 | for j in range(i+1, len(s)): 11 | if s[i] == s[j]: 12 | if j - i + 1 <= 3: dp[i][j] = True 13 | else: dp[i][j] = dp[i+1][j-1] 14 | if dp[i][j] == True: res += 1 15 | return res -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode673. 最长递增子序列的个数(19).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findNumberOfLIS(self, nums: List[int]) -> int: 3 | if len(nums) < 2: return len(nums) 4 | # dp[i]表示以nums[i]结尾的数组的最长递增子序列的长度 5 | dp = [1 for _ in range(len(nums))] 6 | # count[j]表示以nums[i]结尾的数组的最长递增子序列的个数 7 | count = [1 for _ in range(len(nums))] 8 | max_len = 1 9 | res = 0 10 | for i in range(len(nums)): 11 | for j in range(i): 12 | if nums[i] > nums[j]: 13 | if dp[i] < dp[j] + 1: 14 | dp[i] = dp[j] + 1 15 | count[i] = count[j] 16 | elif dp[i] == dp[j] + 1: 17 | count[i] += count[j] 18 | if dp[i] > max_len: 19 | max_len = dp[i] 20 | res = count[i] 21 | elif dp[i] == max_len: 22 | res += count[i] 23 | return res -------------------------------------------------------------------------------- /6-动态规划/单串/Leetcode674. 最长连续递增子序列长度(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findLengthOfLCIS(self, nums: List[int]) -> int: 3 | if len(nums) < 2: return len(nums) 4 | dp = [1 for _ in range(len(nums))] 5 | # dp[0] = 1 6 | res = 1 7 | for i in range(1, len(nums)): 8 | if nums[i] > nums[i-1]: 9 | dp[i] = max(dp[i], dp[i-1]+1) 10 | res = max(res, dp[i]) 11 | return res -------------------------------------------------------------------------------- /6-动态规划/单串/找到最长的递增子序列.py: -------------------------------------------------------------------------------- 1 | def solver(a, n): 2 | if n < 2: 3 | print(n) 4 | # dp[i]表示以a[i]结尾的数组的最长递增子序列 5 | max_len = 1 6 | dp = [1 for _ in range(n)] 7 | pre_pos = [i for i in range(n)] 8 | for i in range(1, n): 9 | for j in range(i): 10 | if a[i] > a[j]: 11 | if dp[i] < dp[j] + 1: 12 | dp[i] = dp[j] + 1 13 | pre_pos[i] = j 14 | if max_len < dp[i]: max_len = dp[i] 15 | 16 | for i in range(n): 17 | if dp[i] == max_len: 18 | idx = i 19 | break 20 | 21 | res = [] 22 | while idx >= 0: 23 | if len(res) == max_len: break 24 | res.append(str(a[idx])) 25 | idx = pre_pos[idx] 26 | 27 | print(' '.join(res[::-1])) 28 | 29 | 30 | T = int(input()) 31 | for i in range(T): 32 | n = int(input()) 33 | a = list(map(int, input().split())) 34 | solver(a, n) -------------------------------------------------------------------------------- /6-动态规划/双串/Leetcode1143. 最长公共子序列(80).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestCommonSubsequence(self, text1: str, text2: str) -> int: 3 | if not text1 or not text2: return 0 4 | # dp[i][j]表示text1[:i]和text2[:j]的最长公共子序列长度 5 | # +0 是因为后面dp[i][j]=dp[i-0][j-0]+0 所以这里idx=0就相当于是空字符串 6 | # '' 和 其他字符串 或 其他字符串和’‘ 的最长公共子序列的长度都是0 7 | dp = [[0 for _ in range(len(text2)+1)]for _ in range(len(text1)+1)] 8 | for i in range(1, len(text1)+1): 9 | for j in range(1, len(text2)+1): 10 | if text1[i-1] == text2[j-1]: 11 | dp[i][j] = dp[i-1][j-1] + 1 12 | else: 13 | # 如果当前长度为i的text1子序列和长度为j的text2子序列最尾的字符不相等 14 | # 并不意味着这两个子序列就没有公共子序列了 15 | # 因为长度为i-1的text1子序列和长度为j的text2子序列可能是有公共子序列了 16 | # 长度为i的text1子序列和长度为j-1的text2子序列也可能有公共子序列 17 | dp[i][j] = max(dp[i-1][j], dp[i][j-1]) 18 | return dp[-1][-1] -------------------------------------------------------------------------------- /6-动态规划/双串/Leetcode115. 不同的子序列(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numDistinct(self, s: str, t: str) -> int: 3 | m, n = len(s), len(t) 4 | if m == 0: return 0 5 | if n == 0: return 1 6 | if m < n: return 0 7 | if m == n: return 1 if s == t else 0 8 | # dp[i][j]表示s[:i+1]中的出现t[:j+1]的子序列个数 9 | dp = [[0 for _ in range(n+1)]for _ in range(m+1)] 10 | # 第1行 s为空 => 0 第1列 t为空 => 1 11 | for i in range(m+1): 12 | dp[i][0] = 1 13 | for i in range(1, m+1): 14 | for j in range(1, n+1): 15 | if s[i-1] != t[j-1]: 16 | dp[i][j] = dp[i-1][j] 17 | else: 18 | dp[i][j] = dp[i-1][j] + dp[i-1][j-1] 19 | return dp[-1][-1] -------------------------------------------------------------------------------- /6-动态规划/双串/Leetcode718. 最长重复子数组(57).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findLength(self, nums1: List[int], nums2: List[int]) -> int: 3 | if not nums1 or not nums2: return 0 4 | # dp[i][j]表示nums1[:i]且以nums[i]结尾的数组和nums2[:j]且以nums2[j]结尾的数组的最长公共子数组长度 5 | dp = [[0 for _ in range(len(nums2)+1)]for _ in range(len(nums1)+1)] 6 | max_len = 0 7 | for i in range(1, len(nums1)+1): 8 | for j in range(1, len(nums2)+1): 9 | if nums1[i-1] == nums2[j-1]: 10 | dp[i][j] = dp[i-1][j-1] + 1 11 | # else: dp[i][j] = 0 12 | if max_len < dp[i][j]: max_len = dp[i][j] 13 | return max_len -------------------------------------------------------------------------------- /6-动态规划/双串/Leetcode72. 编辑距离(93).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minDistance(self, word1: str, word2: str) -> int: 3 | if not word1: return len(word2) 4 | if not word2: return len(word1) 5 | # dp[i][j]表示word1[:i]转换为word2[:j]的最少操作数 6 | dp = [[0 for _ in range(len(word2)+1)]for _ in range(len(word1)+1)] 7 | for i in range(1, len(word1)+1): 8 | dp[i][0] = i 9 | for j in range(1, len(word2)+1): 10 | dp[0][j] = j 11 | for i in range(1, len(word1)+1): 12 | for j in range(1, len(word2)+1): 13 | if word1[i-1] == word2[j-1]: 14 | dp[i][j] = dp[i-1][j-1] 15 | else: 16 | dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1 17 | return dp[-1][-1] -------------------------------------------------------------------------------- /6-动态规划/双串/Leetcode97. 交错字符串(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isInterleave(self, s1: str, s2: str, s3: str) -> bool: 3 | # https://www.bilibili.com/video/BV1RL411P7WQ?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | m, n, t = len(s1), len(s2), len(s3) 5 | if m + n != t: return False 6 | # dp[i][j] 表示s1[:i]和s2[:j]是否能交错组成s3[:i+j] 7 | dp = [[False for _ in range(n+1)]for _ in range(m+1)] 8 | dp[0][0] = True 9 | for i in range(1, m+1): 10 | # s1[:i] 能否拼成 s3[:i] 11 | dp[i][0] = dp[i-1][0] and s1[i-1] == s3[i-1] 12 | for j in range(1, n+1): 13 | # s2[:j] 能否拼成 s3[:j] 14 | dp[0][j] = dp[0][j-1] and s2[j-1] == s3[j-1] 15 | for i in range(1, m+1): 16 | for j in range(1, n+1): 17 | dp[i][j] = (dp[i-1][j] and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] and s2[j-1] == s3[i+j-1]) 18 | return dp[-1][-1] -------------------------------------------------------------------------------- /6-动态规划/普通动归/Leetcode264. 丑数 II(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def nthUglyNumber(self, n: int) -> int: 3 | # https://www.bilibili.com/video/BV19m4y1D7Za?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | if n == 1: return 1 5 | dp = [0] * n 6 | dp[0] = 1 7 | p2, p3, p5 = 0, 0, 0 8 | for i in range(1, n): 9 | dp[i] = min(dp[p2]*2, dp[p3]*3, dp[p5]*5) 10 | if dp[i] == dp[p2]*2: p2 += 1 11 | if dp[i] == dp[p3]*3: p3 += 1 12 | if dp[i] == dp[p5]*5: p5 += 1 13 | return dp[n-1] -------------------------------------------------------------------------------- /6-动态规划/普通动归/Leetcode32. 最长有效括号(65).py: -------------------------------------------------------------------------------- 1 | # 难 2 | # 视频学习:https://www.bilibili.com/video/BV1MZ4y1B7fB?from=search&seid=8232076784487285058&spm_id_from=333.337.0.0%2F&vd_source=5f6bbc1038b075757cb446f800f3cd56 3 | class Solution: 4 | def longestValidParentheses(self, s: str) -> int: 5 | if len(s) < 2: return 0 6 | # dp[i]表示以s[i]结尾的字符串最长有效括号长度 7 | dp = [0 for _ in range(len(s))] 8 | max_len = 0 9 | for i in range(1, len(s)): 10 | if s[i] == '(': dp[i] = 0 11 | else: 12 | if s[i-1] == '(': dp[i] = dp[i-2] + 2 13 | elif s[i-dp[i-1]-1] == '(' and (i-dp[i-1]-1)>=0: 14 | dp[i] = dp[i-1] + 2 + dp[i-dp[i-1]-2] 15 | if dp[i] > max_len: max_len = dp[i] 16 | return max_len -------------------------------------------------------------------------------- /6-动态规划/普通动归/Leetcode343. 整数拆分(14).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def integerBreak(self, n: int) -> int: 3 | if n <= 2: return 1 4 | # dp[i]表示将正整数i拆分成k个正整数,最大乘积 5 | dp = [0 for _ in range(n+1)] 6 | # dp[0]和dp[1]没用意义 7 | dp[2] = 1 # 1 1 8 | for i in range(3, n+1): 9 | for j in range(1, i): 10 | # dp[i] 历史的最大值 11 | # j * (i-j) 拆分成两个正整数 12 | # j * (i-j) 拆分成两个以上的正整数 13 | dp[i] = max(dp[i], j*(i-j), j*dp[i-j]) 14 | return dp[-1] -------------------------------------------------------------------------------- /6-动态规划/普通动归/Leetcode70. 爬楼梯(100).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 11:17 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def climbStairs(self, n: int) -> int: 8 | if n <= 2: return n 9 | # dp[i]表示到达第i个台阶有多少种不同的方法 10 | dp = [0 for _ in range(n+1)] 11 | dp[1] = 1 12 | dp[2] = 2 13 | for i in range(3, n+1): 14 | dp[i] = dp[i-1] + dp[i-2] 15 | return dp[-1] -------------------------------------------------------------------------------- /6-动态规划/普通动归/Leetcode96. 不同的二叉搜索树(20).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numTrees(self, n: int) -> int: 3 | if n < 2: return n 4 | # dp[i]表示第i个节点为根节点的二叉搜索树共有多少个 5 | dp = [0 for _ in range(n+1)] 6 | dp[0] = 1 # 0个节点有1种二叉搜索树 [] 7 | dp[1] = 1 # 1个节点有1种二叉搜索树 [root] 8 | for i in range(2, n+1): 9 | for j in range(1, i+1): # 最多根节点可以等于i 右子树可以为空的 10 | # 左子树 1~j-1 j-1-1+1 = j - 1 11 | # 右子树 j+1~i i-j-1+1 = i - j 12 | dp[i] += dp[j-1] * dp[i-j] 13 | return dp[-1] -------------------------------------------------------------------------------- /6-动态规划/普通动归/Leetcode剑指 Offer 46. 把数字翻译成字符串(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def translateNum(self, num: int) -> int: 3 | # https://www.bilibili.com/video/BV1Bz411i7cs?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | if not num: return 1 5 | num = str(num) 6 | # dp[i]表示num[:i]有多少种翻译方式 7 | # dp[5] num[:5] = 12258有多少种翻译方式 8 | dp = [0 for _ in range(len(num)+1)] 9 | dp[0] = 1 # dp[0]表示nums[:0]空串 1种 10 | dp[1] = 1 # dp[1]表示nums[:1] 1 1种 11 | for i in range(2, len(num)+1): 12 | # i=4时 num[i-2:i] = num[2:4] = 25 13 | if '10' <= num[i-2:i] <= '25': 14 | dp[i] = dp[i-1] + dp[i-2] 15 | # i=5时 num[i-2:i] = num[3:5] = 58 16 | else: 17 | dp[i] = dp[i-1] 18 | return dp[-1] -------------------------------------------------------------------------------- /6-动态规划/经典题型/买卖股票/Leetcode121. 买卖股票的最佳时机(173).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 10:58 4 | @github: https://github.com/HuKai97 5 | """ 6 | class Solution: 7 | def maxProfit(self, prices: List[int]) -> int: 8 | # 只能买卖一次 同一天只能买入或者卖出 9 | if len(prices) < 2: return 0 10 | # dp[i][0]: 表示第i天未持股最大的利润 11 | # dp[i][0]: 表示第i天持股最大的利润 12 | dp = [[0 for _ in range(2)]for _ in range(len(prices))] 13 | dp[0][0], dp[0][1] = 0, -prices[0] 14 | for i in range(1, len(prices)): 15 | dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) 16 | # dp[i-0][0]只能为0 因为只能买卖一次 17 | dp[i][1] = max(dp[i-1][1], -prices[i]) 18 | return dp[-1][0] 19 | -------------------------------------------------------------------------------- /6-动态规划/经典题型/买卖股票/Leetcode122. 买卖股票的最佳时机 II(44).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxProfit(self, prices: List[int]) -> int: 3 | # 可以买卖多次 同一天只能买入或者卖出 4 | if len(prices) < 2: return 0 5 | # dp[i][0]: 表示第i天未持股状态下的最大利润 6 | # dp[i][0]: 表示第i天持股状态下的最大利润 7 | dp = [[0 for _ in range(2)]for _ in range(len(prices))] 8 | dp[0][1] = -prices[0] 9 | for i in range(1, len(prices)): 10 | dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) 11 | dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i]) 12 | return dp[-1][0] -------------------------------------------------------------------------------- /6-动态规划/经典题型/买卖股票/Leetcode123. 买卖股票的最佳时机 III(29).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxProfit(self, prices: List[int]) -> int: 3 | # 最多两笔交易 4 | if len(prices) < 2: return 0 5 | # dp[i][j][k]: i第几天 j 0 1 是否持股 k 0 1 2目前完成的交易次数 6 | dp = [[[0 for _ in range(3)]for _ in range(2)]for _ in range(len(prices))] 7 | dp[0][0][0] = 0 8 | dp[0][0][1] = float('-inf') # 不可能事件 9 | dp[0][0][2] = float('-inf') # 不可能事件 10 | dp[0][1][0] = -prices[0] 11 | dp[0][1][1] = float('-inf') # 不可能事件 12 | dp[0][1][2] = float('-inf') # 不可能事件 13 | for i in range(1, len(prices)): 14 | # 第i天 未持股 没卖出去 15 | dp[i][0][0] = 0 16 | # 第i天 未持股 卖出一次 = max(今天卖的,以前卖的) 17 | dp[i][0][1] = max(dp[i-1][1][0]+prices[i], dp[i-1][0][1]) 18 | dp[i][0][2] = max(dp[i-1][1][1]+prices[i], dp[i-1][0][2]) 19 | # 第i天 持股 没卖出去 = max(今天持股,以前持股) 20 | dp[i][1][0] = max(dp[i-1][0][0]-prices[i], dp[i-1][1][0]) 21 | # 第i天 持股 卖出一次 = max(今天持股,以前持股) 22 | dp[i][1][1] = max(dp[i-1][0][1]-prices[i], dp[i-1][1][1]) 23 | dp[i][1][2] = float('-inf') # 不可能事件 最多两次交易 24 | return max(dp[-1][0][0], dp[-1][0][1], dp[-1][0][2]) -------------------------------------------------------------------------------- /6-动态规划/经典题型/买卖股票/Leetcode188. 买卖股票的最佳时机 IV(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxProfit(self, k: int, prices: List[int]) -> int: 3 | if len(prices) < 2: return 0 4 | # dp[i][j][t] i: 第i天 j: 0不持股 1持股 t: 当前完成t笔交易 5 | dp = [[[0 for _ in range(k+1)]for _ in range(2)]for _ in range(len(prices))] 6 | # base case 7 | dp[0][0][0] = 0 8 | dp[0][1][0] = -prices[0] 9 | for i in range(1, k+1): 10 | dp[0][0][i] = float('-inf') 11 | dp[0][1][i] = float('-inf') 12 | # 遍历 13 | for i in range(1, len(prices)): 14 | for j in range(k+1): 15 | if j == 0: 16 | dp[i][0][0] = 0 17 | dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][0][0]-prices[i]) 18 | else: 19 | dp[i][0][j] = max(dp[i-1][0][j], dp[i-1][1][j-1]+prices[i]) 20 | dp[i][1][j] = max(dp[i-1][1][j], dp[i-1][0][j]-prices[i]) 21 | res = 0 22 | for i in range(k+1): 23 | res = max(res, dp[-1][0][i]) 24 | return res -------------------------------------------------------------------------------- /6-动态规划/经典题型/打家劫舍/Leetcode198. 打家劫舍(43).py: -------------------------------------------------------------------------------- 1 | # 198. 打家劫舍 2 | class Solution: 3 | def rob(self, nums: List[int]) -> int: 4 | if len(nums) == 0: return 0 5 | if len(nums) == 1: return nums[0] 6 | if len(nums) == 2: return max(nums[0], nums[1]) 7 | # dp[i]:表示偷窃0~i房间最高的金额 8 | dp = [0 for _ in range(len(nums))] 9 | dp[0] = nums[0] 10 | dp[1] = max(nums[0], nums[1]) 11 | for i in range(2, len(nums)): 12 | dp[i] = max(dp[i-1], dp[i-2]+nums[i]) 13 | return dp[-1] -------------------------------------------------------------------------------- /6-动态规划/经典题型/打家劫舍/Leetcode213. 打家劫舍 II(20).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rob(self, nums: List[int]) -> int: 3 | def rob_line(num): 4 | n = len(num) 5 | if n == 0: return 0 6 | if n == 1: return num[0] 7 | dp = [0 for _ in range(n)] 8 | dp[0] = num[0] 9 | dp[1] = max(num[0], num[1]) 10 | for i in range(2, n): 11 | dp[i] = max(dp[i-2] + num[i], dp[i-1]) 12 | return dp[-1] 13 | if len(nums) == 0: return 0 14 | if len(nums) == 1: return nums[0] 15 | if len(nums) == 2: return max(nums[0], nums[1]) 16 | n1 = rob_line(nums[1:]) 17 | n2 = rob_line(nums[:-1]) 18 | n3 = rob_line(nums[1:-1]) 19 | return max(n1, n2, n3) -------------------------------------------------------------------------------- /6-动态规划/经典题型/打家劫舍/Leetcode337. 打家劫舍 III(11).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def rob(self, root: Optional[TreeNode]) -> int: 9 | if not root: return 0 10 | def robTree(root): 11 | if not root: return [0, 0] 12 | left = robTree(root.left) 13 | right = robTree(root.right) 14 | 15 | # 不偷root 16 | val1 = max(left) + max(right) 17 | # 偷root 18 | val2 = root.val + left[0] + right[0] 19 | 20 | return [val1, val2] 21 | 22 | return max(robTree(root)) -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/0-1背包/0-1背包.py: -------------------------------------------------------------------------------- 1 | # 0-1背包 2 | # 变形题1、找到重量为j的组合数目?装满背包有几种方法? 3 | # Leetcode494. 目标和(12) 4 | class Solution: 5 | def findTargetSumWays(self, nums: List[int], target: int) -> int: 6 | # 设数组中添加'-'元素的总和为neg 则添加'+'的总和为sum_nums-neg 7 | # (sum_nums-neg) - neg = target => bagweight = neg = (sum_nums-target)//2 8 | # 所以问题转换为:求出和为bagweight的个数 9 | sum_nums = sum(nums) 10 | if (sum_nums - target) % 2 == 1: return 0 11 | if target > sum_nums: return 0 12 | bagweight = (sum_nums - target) // 2 13 | dp = [0 for _ in range(bagweight + 1)] 14 | dp[0] = 1 15 | for i in range(len(nums)): # 遍历物品 重量 16 | for j in range(bagweight, nums[i]-1, -1): # 遍历背包容量 17 | if j >= nums[i]: # 当前背包容量必须大于当前物品重量 18 | dp[j] += dp[j-nums[i]] 19 | return dp[-1] 20 | 21 | # 变形题2、能否找到重量为j的组合?能否装满背包 22 | # Leetcode416、分割等和子集(11) 23 | class Solution: 24 | def canPartition(self, nums: List[int]) -> bool: 25 | if len(nums) < 2: return False 26 | if sum(nums) % 2 == 1: return False 27 | bagweight = sum(nums) // 2 28 | if max(nums) > bagweight: return False 29 | # dp[i][j]表示下标0~i的元素任意取,限定背包容量为target的情况下 最终最大价值是多少 30 | dp = [0 for _ in range(bagweight+1)] 31 | for i in range(len(nums)): 32 | for j in range(bagweight, nums[i]-1, -1): 33 | if j >= nums[i]: 34 | # 0、不放i 背包容量为j 最大和为dp[i-0][j] 35 | # 2、放i 背包容量为j-nums[i] 最大值为dp[i-0][j-nums[i]]+nums[i] 36 | dp[j] = max(dp[j], dp[j-nums[i]]+nums[i]) 37 | # 如果背包容量为bagWeight(目标和),前i个元素任取的情况下最大和恰好等于背包元素 38 | # 又因为dp[i][bagWeight] <= bagWeight 所以如果有等于的话 那么就满足条件 39 | if dp[bagweight] == bagweight: return True 40 | return False -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/0-1背包/Leetcode1049. 最后一块石头的重量 II(1).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def lastStoneWeightII(self, stones: List[int]) -> int: 3 | # 在stones前面加上+或- 使得式子最小 找到这个最小值 4 | # sum = pos - neg -> pos = sum - neg 5 | # sum - neg - neg 尽可能的小 6 | # bagweight = neg = sum // 2 neg就要在不超过sum//2的情况下尽可能的大 7 | sum_ = sum(stones) 8 | bagweight = sum_ // 2 9 | # dp[j]表示在stones[:i]中任选,背包容量为sum//2的情况下 最大的价值 10 | dp = [0 for _ in range(bagweight + 1)] 11 | for i in range(len(stones)): 12 | for j in range(bagweight, stones[i]-1, -1): 13 | dp[j] = max(dp[j], dp[j-stones[i]]+stones[i]) 14 | return sum_ - 2 * dp[-1] 15 | -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/0-1背包/Leetcode416. 分割等和子集(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canPartition(self, nums: List[int]) -> bool: 3 | # 1、二维数组 4 | if len(nums) < 2: return False # 小于2没法拆 5 | if sum(nums) % 2 == 1: return False # 奇数没法拆 6 | bagWeight = sum(nums) // 2 7 | if max(nums) > bagWeight: return False # 最大数大于一半 不可能有 8 | # # dp[i][j]表示nums[0:i]的物品任取,限定背包容量为bagWeight的情况下 最终的最大价值是多少/相加和的最大值(最大和是bagWeight) 9 | dp = [[0 for _ in range(bagWeight + 1)] for _ in range(len(nums))] 10 | for j in range(bagWeight, nums[0] - 1, -1): 11 | dp[0][j] = nums[0] 12 | for i in range(1, len(nums)): 13 | for j in range(1, bagWeight + 1): 14 | if j < nums[i]: 15 | dp[i][j] = dp[i - 1][j] # 容量小于nums[i] 16 | else: 17 | # 1、不放i 背包容量为j 最大和为dp[i-1][j] 18 | # 2、放i 背包容量为j-nums[i] 最大值为dp[i-1][j-nums[i]]+nums[i] 19 | dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]) 20 | # 如果背包容量为bagWeight(目标和),前i个元素任取的情况下最大和恰好等于背包元素 21 | # 又因为dp[i][bagWeight] <= bagWeight 所以如果有等于的话 那么就满足条件 22 | if dp[i][bagWeight] == bagWeight: return True 23 | return False 24 | 25 | # 2、一维数组 26 | if sum(nums) < 2: return False 27 | if sum(nums) % 2 == 1: return False 28 | bagweight = sum(nums) // 2 29 | if max(nums) > bagweight: return False 30 | dp = [0 for _ in range(bagweight + 1)] 31 | for i in range(len(nums)): 32 | for j in range(bagweight, nums[i] - 1, -1): 33 | # if j >= nums[i]: 34 | dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]) 35 | if dp[bagweight] == bagweight: return True 36 | return False 37 | 38 | -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/0-1背包/Leetcode494. 目标和(13).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findTargetSumWays(self, nums: List[int], target: int) -> int: 3 | sum_nums = sum(nums) 4 | if target > sum_nums: return 0 5 | # neg pos = sum_nums - neg target = pos - neg = sum_nums - neg - neg 6 | # neg = (sum_nums - target) // 2 7 | if (sum_nums - target) % 2 == 1: return 0 8 | bagWeight = (sum_nums - target) // 2 9 | # dp[j]表示从nums[:i]中任选 重量为j的物品 最多多少种不同的方法 10 | dp = [0 for _ in range(bagWeight + 1)] 11 | # 从nums中随机挑选物品 重量为0的数量=1种 什么也不装 12 | dp[0] = 1 13 | for i in range(len(nums)): 14 | for j in range(bagWeight, nums[i]-1, -1): 15 | # 如果背包里已经有一个数nums[i],那么背包的容量就变成了j-nums[i],所以在这种情况下还要装满背包就还有dp[j-nums[i]]个方法。 16 | # 那么对于每个nums[i],dp[j]+=dp[j-nums[i]],就可以找到用nums里的所有元素装满容量为j的背包的所有方法数 17 | dp[j] += dp[j-nums[i]] 18 | return dp[-1] -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/完全背包/Leetcode279. 完全平方数(13).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numSquares(self, n: int) -> int: 3 | # 完全背包:每个平方数可以使用多次 求最少的平方数个数 4 | # 物品:1 - floor(sqrt(n)) 背包重量:n 5 | nums = [i * i for i in range(1, floor(sqrt(n)) + 1)] 6 | dp = [float('inf') for _ in range(n+1)] 7 | dp[0] = 0 8 | # print(nums) # [1, 4, 9] 9 | for i in range(len(nums)): 10 | for j in range(nums[i], n+1): 11 | dp[j] = min(dp[j], dp[j-nums[i]]+1) 12 | return dp[-1] if dp[-1] != float('inf') else 0 -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/完全背包/Leetcode322. 零钱兑换(65).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def coinChange(self, coins: List[int], amount: int) -> int: 3 | # 完全背包问题 各种硬币数量不定 需要拼成amount 4 | if amount == 0: return 0 5 | if len(coins) == 0: return -1 6 | # dp[j]表示从coins中任选,总金额为j,需要的最少硬币数 7 | dp = [float('inf') for _ in range(amount+1)] 8 | dp[0] = 0 # 什么都不选 硬币数0 9 | for i in range(len(coins)): 10 | # 当j=coins[i]时:可以装得下,可以选择装或者不装中价值小的(物品数小的)进行转移 12 | # 即:dp[j]=min(dp[j],dp[j-coins[i]+0]) 13 | for j in range(coins[i], amount+1): 14 | dp[j] = min(dp[j], dp[j-coins[i]]+1) 15 | return dp[-1] if dp[-1] != float('inf') else -1 16 | -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/完全背包/Leetcode518. 零钱兑换 II(32).py: -------------------------------------------------------------------------------- 1 | # Leetcode518. 零钱兑换 II 2 | class Solution: 3 | def change(self, amount: int, coins: List[int]) -> int: 4 | if amount == 0 or len(coins) == 0: return 1 5 | # dp[j]表示从amount中任选,总金额为amount的方式多少种 6 | dp = [0 for _ in range(amount+1)] 7 | dp[0] = 1 # 什么都不选 1种方案 8 | for i in range(len(coins)): 9 | for j in range(coins[i], amount+1): 10 | dp[j] += dp[j-coins[i]] 11 | return dp[-1] 12 | -------------------------------------------------------------------------------- /6-动态规划/经典题型/背包问题/背包问题.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "# 0-1背包\n", 12 | "# 物品: nums bagweight\n", 13 | "dp = [看题目具体情况 for _ in range(bagweight + 1)]\n", 14 | "dp[0] = 看题目具体情况 有多少种=1 最大价值=0\n", 15 | "for i in range(len(nums)):\n", 16 | " for j in range(bagweight, nums[i]-1, -1):\n", 17 | " # 问题1\n", 18 | " if j >= nums[i]:\n", 19 | " dp[j] = max(dp[j], dp[j-nums[i]]+nums[i])\n", 20 | " # 问题2\n", 21 | " if j >= nums[i]: # 可加可不加\n", 22 | " dp[j] += dp[j-nums[i]]" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "outputs": [], 29 | "source": [ 30 | "# 完全背包\n", 31 | "# 物品: nums bagweight\n", 32 | "dp = [看题目具体情况 for _ in range(bagweight+1)]\n", 33 | "dp[0] = 看题目具体情况\n", 34 | "for i in range(len(nums)):\n", 35 | " for j in range(nums[i], bagweight+1):\n", 36 | " # 问题1\n", 37 | " if j >= nums[i]:\n", 38 | " dp[j] = min(dp[j], dp[j-nums[i]]+nums[i])\n", 39 | " # 问题2\n", 40 | " dp[j] += dp[j-nums[i]]" 41 | ], 42 | "metadata": { 43 | "collapsed": false, 44 | "pycharm": { 45 | "name": "#%%\n" 46 | } 47 | } 48 | } 49 | ], 50 | "metadata": { 51 | "kernelspec": { 52 | "display_name": "Python 3", 53 | "language": "python", 54 | "name": "python3" 55 | }, 56 | "language_info": { 57 | "codemirror_mode": { 58 | "name": "ipython", 59 | "version": 2 60 | }, 61 | "file_extension": ".py", 62 | "mimetype": "text/x-python", 63 | "name": "python", 64 | "nbconvert_exporter": "python", 65 | "pygments_lexer": "ipython2", 66 | "version": "2.7.6" 67 | } 68 | }, 69 | "nbformat": 4, 70 | "nbformat_minor": 0 71 | } -------------------------------------------------------------------------------- /6-动态规划/经典题型/路径问题/Leetcode120. 三角形最小路径和(21).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minimumTotal(self, triangle: List[List[int]]) -> int: 3 | # https://www.bilibili.com/video/BV1BA411T7SU?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | if not triangle: return 0 5 | m = len(triangle) 6 | # dp[i][j]表示从bittim->triangle[i][j]的最小路径和 7 | dp = triangle 8 | # base case: 初始化最下面一行 就等于triangle[-1][j] 9 | # 从倒数第二行开始遍历 10 | for i in range(m-2, -1, -1): 11 | for j in range(len(triangle[i])): 12 | # 如果从上开始遍历 dp[i][j] = min(dp[i-1][j], dp[i-1][j+1]) + tri[i][j] 13 | # 可能上一层完全就不存在dp[i-1][j+1] 所以这里为了方便直接从底开始 14 | dp[i][j] += min(dp[i+1][j], dp[i+1][j+1]) 15 | return dp[0][0] -------------------------------------------------------------------------------- /6-动态规划/经典题型/路径问题/Leetcode221. 最大正方形(51).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maximalSquare(self, matrix: List[List[str]]) -> int: 3 | m, n = len(matrix), len(matrix[0]) 4 | if m == 0 or n == 0: return 0 5 | max_edge = 0 6 | # dp[i][j]表示从matrix[0][0]到matrix[i][j]的最大正方形边长 7 | dp = [[0 for _ in range(n)]for _ in range(m)] 8 | for i in range(m): 9 | for j in range(n): 10 | if matrix[i][j] == '1': 11 | # 这里之所以要合在一起主要是因为需要比较max_edge 不像62那样不需要比较 12 | # 如果base case写在外面的话 无法比较max_edge 13 | if i == 0 or j == 0: dp[i][j] = 1 14 | else: dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1 15 | max_edge = max(max_edge, dp[i][j]) 16 | return max_edge ** 2 -------------------------------------------------------------------------------- /6-动态规划/经典题型/路径问题/Leetcode62. 不同路径(50).py: -------------------------------------------------------------------------------- 1 | # 62. 不同路径 2 | class Solution: 3 | def uniquePaths(self, m: int, n: int) -> int: 4 | if m == 1 or n == 1: return 1 5 | # dp[i][j]表示从00走到ij共多少条路径 6 | dp = [[0 for _ in range(n)]for _ in range(m)] 7 | for i in range(m): 8 | dp[i][0] = 1 9 | for j in range(n): 10 | dp[0][j] = 1 11 | for i in range(1, m): 12 | for j in range(1, n): 13 | dp[i][j] = dp[i-1][j] + dp[i][j-1] 14 | return dp[-1][-1] 15 | 16 | -------------------------------------------------------------------------------- /6-动态规划/经典题型/路径问题/Leetcode63. 不同路径 II(16).py: -------------------------------------------------------------------------------- 1 | # 63. 不同路径 II 2 | class Solution: 3 | def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: 4 | m, n = len(obstacleGrid), len(obstacleGrid[0]) 5 | if m == 0 or n == 0: return 0 6 | # dp[i][j]表示从00走到ij共多少条路径 7 | dp = [[0 for _ in range(n)]for _ in range(m)] 8 | for i in range(m): 9 | if obstacleGrid[i][0] == 1: break # 第一列只要有一个1后面就不需要比较了 10 | dp[i][0] = 1 11 | for j in range(n): 12 | if obstacleGrid[0][j] == 1: break # 第一行只要有一个1后面就不需要比较了 13 | dp[0][j] = 1 14 | for i in range(1, m): 15 | for j in range(1, n): 16 | if obstacleGrid[i][j] == 0: 17 | dp[i][j] = dp[i-1][j] + dp[i][j-1] 18 | return dp[-1][-1] -------------------------------------------------------------------------------- /6-动态规划/经典题型/路径问题/Leetcode64. 最小路径和(62).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minPathSum(self, grid: List[List[int]]) -> int: 3 | m, n = len(grid), len(grid[0]) 4 | if m == 0 or n == 0: return 0 5 | # dp[i][j]表示从dp[0][0]走到dp[i][j]最小路径和 6 | dp = copy.deepcopy(grid) 7 | for i in range(1, m): 8 | dp[i][0] += dp[i-1][0] 9 | for j in range(1, n): 10 | dp[0][j] += dp[0][j-1] 11 | for i in range(1, m): 12 | for j in range(1, n): 13 | dp[i][j] += min(dp[i][j-1], dp[i-1][j]) 14 | return dp[-1][-1] -------------------------------------------------------------------------------- /7-链表/Leetcode114. 二叉树展开为链表(24).py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | # https://www.bilibili.com/video/BV1334y1y7qk?spm_id_from=333.337.search-card.all.click 8 | class Solution: 9 | def flatten(self, root: Optional[TreeNode]) -> None: 10 | """ 11 | Do not return anything, modify root in-place instead. 12 | """ 13 | if not root: return None 14 | cur = root 15 | while cur: 16 | if cur.left: 17 | temp = cur.left 18 | while temp and temp.right: 19 | temp = temp.right 20 | temp.right = cur.right 21 | cur.right = cur.left 22 | cur.left = None 23 | cur = cur.right -------------------------------------------------------------------------------- /7-链表/Leetcode138. 复制带随机指针的链表(40).py: -------------------------------------------------------------------------------- 1 | """ 2 | # Definition for a Node. 3 | class Node: 4 | def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): 5 | self.val = int(x) 6 | self.next = next 7 | self.random = random 8 | """ 9 | 10 | class Solution: 11 | def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': 12 | # 复制链表中的指针都不应指向原链表中的节点 。 13 | if not head: return None 14 | mapp = {} 15 | # 先遍历一遍 复制节点 和 节点值 16 | cur = head 17 | while cur: 18 | mapp[cur] = Node(cur.val) 19 | cur = cur.next 20 | # 再遍历一遍 21 | cur = head 22 | while cur: 23 | if cur.next: mapp[cur].next = mapp[cur.next] 24 | if cur.random: mapp[cur].random = mapp[cur.random] 25 | cur = cur.next 26 | return mapp[head] -------------------------------------------------------------------------------- /7-链表/Leetcode141. 环形链表(172).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 8:49 4 | @github: https://github.com/HuKai97 5 | """ 6 | class ListNode: 7 | def __init__(self, x): 8 | self.val = x 9 | self.next = None 10 | class Solution: 11 | def hasCycle(self, head: Optional[ListNode]) -> bool: 12 | slow, fast = head, head 13 | while fast and fast.next: # 注意这里还有个fast.next条件 14 | slow = slow.next 15 | fast = fast.next.next 16 | if slow == fast: return True # 相等就相遇了 17 | return False -------------------------------------------------------------------------------- /7-链表/Leetcode142. 环形链表 II(139).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 9:27 4 | @github: https://github.com/HuKai97 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 | class Solution: 13 | def detectCycle(self, head: ListNode) -> ListNode: 14 | if not head: return None 15 | slow, fast = head, head 16 | while fast and fast.next: 17 | slow = slow.next 18 | fast = fast.next.next 19 | if slow == fast: 20 | fast = head 21 | while slow != fast: 22 | slow = slow.next 23 | fast = fast.next 24 | return slow 25 | return None -------------------------------------------------------------------------------- /7-链表/Leetcode143. 重排链表(112).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 11:01 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for singly-linked list. 7 | class Solution: 8 | def reorderList(self, head: Optional[ListNode]) -> None: 9 | """ 10 | Do not return anything, modify head in-place instead. 11 | """ 12 | def reverse(head): 13 | pre, cur = None, head 14 | while cur: 15 | temp = cur.next 16 | cur.next = pre 17 | pre = cur 18 | cur = temp 19 | return pre 20 | if not head or not head.next: return head 21 | dummy = ListNode(-1) 22 | rear = dummy 23 | slow, fast = head, head.next 24 | while fast and fast.next: 25 | slow = slow.next 26 | fast = fast.next.next 27 | head2 = slow.next 28 | slow.next = None 29 | head2 = reverse(head2) 30 | while head and head2: 31 | rear.next = head 32 | rear = rear.next 33 | head = head.next 34 | rear.next = head2 35 | rear = rear.next 36 | head2 = head2.next 37 | if head: rear.next = head 38 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode146. LRU缓存机制(396).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/31 22:24 4 | @github: https://github.com/HuKai97 5 | """ 6 | # https://www.bilibili.com/video/BV12z4y1o7jy?spm_id_from=333.337.search-card.all.click 7 | class ListNode: 8 | def __init__(self, key, val): 9 | self.key = key 10 | self.val = val 11 | self.pre = None 12 | self.next = None 13 | 14 | class LRUCache: 15 | def __init__(self, capacity: int): 16 | self.capacity = capacity 17 | self.cur_size = 0 18 | # 初始化hashmap key:key val:ListNode 方便get函数直接获取O(0) 19 | self.ListNodeMap = dict() 20 | # 初始化双向链表 新建头尾节点 21 | self.head = ListNode(-1, -1) 22 | self.tail = ListNode(-1, -1) 23 | self.head.next = self.tail 24 | self.tail.next = self.head 25 | 26 | def moveToHead(self, node): 27 | # remove from cur pos 28 | node.pre.next = node.next 29 | node.next.pre = node.pre 30 | # insert into self.head next 31 | node.next = self.head.next 32 | self.head.next.pre = node 33 | node.pre = self.head 34 | self.head.next = node 35 | 36 | def get(self, key: int) -> int: 37 | if key in self.ListNodeMap: 38 | node = self.ListNodeMap[key] 39 | self.moveToHead(node) 40 | return node.val 41 | else: return -1 42 | 43 | def addToHead(self, node): 44 | self.cur_size += 1 45 | # add node to self.head next 46 | node.next = self.head.next 47 | self.head.next.pre = node 48 | node.pre = self.head 49 | self.head.next = node 50 | # check capacity 51 | if self.cur_size > self.capacity: 52 | deleteNode = self.tail.pre 53 | deleteNode.pre.next = self.tail 54 | self.tail.pre = deleteNode.pre 55 | self.cur_size -= 1 56 | self.ListNodeMap.pop(deleteNode.key) 57 | 58 | def put(self, key: int, value: int) -> None: 59 | # 双向链表 -> O(0) 60 | if key in self.ListNodeMap: 61 | node = self.ListNodeMap[key] 62 | node.val = value 63 | self.moveToHead(node) 64 | else: 65 | node = ListNode(key, value) 66 | self.ListNodeMap[key] = node 67 | self.addToHead(node) -------------------------------------------------------------------------------- /7-链表/Leetcode160. 相交链表(160).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 8:58 4 | @github: https://github.com/HuKai97 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 | class Solution: 13 | def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode: 14 | # 0、如果相交 那么在第一次两个都交换了head后会必相交 return相交节点 15 | # 2、如果不相交 那么在第一次两者都交换了head后会同时走到链表末尾 同时为None return None 16 | if not headA or not headB: return None 17 | p, q = headA, headB 18 | while p != q: 19 | p = p.next if p else headB 20 | q = q.next if q else headA 21 | return p -------------------------------------------------------------------------------- /7-链表/Leetcode19. 删除链表的倒数第 N 个结点(100).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 11:09 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for singly-linked list. 7 | class ListNode: 8 | def __init__(self, val=0, next=None): 9 | self.val = val 10 | self.next = next 11 | class Solution: 12 | def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: 13 | if not head or n == 0: return None 14 | dummy = ListNode(-1, head) 15 | slow, fast = dummy, head 16 | for i in range(n): 17 | fast =fast.next 18 | while fast: 19 | slow = slow.next 20 | fast = fast.next 21 | slow.next = slow.next.next 22 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode2. 两数相加(91).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 9:26 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for singly-linked list. 7 | class ListNode: 8 | def __init__(self, val=0, next=None): 9 | self.val = val 10 | self.next = next 11 | class Solution: 12 | def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: 13 | p, q = l1, l2 14 | dummy = ListNode(-1, l1) 15 | rear = dummy 16 | # 2. 两数相加 17 | while p and q: 18 | p.val += q.val 19 | rear = p 20 | p, q = p.next, q.next 21 | if q: rear.next = q # 这里必须要用dummy 因为需要这个rear指针,如果p为空 就找不到了 22 | # 下面考虑进位 23 | pre, cur = dummy.next, dummy.next.next 24 | while cur: 25 | if pre.val >= 10: 26 | pre.val %= 10 27 | cur.val += 1 28 | pre = cur 29 | cur = cur.next 30 | # 考虑末位进位 31 | if pre.val >= 10: 32 | pre.val %= 10 33 | pre.next = ListNode(1) 34 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode206. 反转链表(487).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def reverseList(self, head: ListNode) -> ListNode: 8 | pre = None 9 | cur = head 10 | while cur: 11 | temp = cur.next 12 | cur.next = pre 13 | pre = cur 14 | cur = temp 15 | return pre -------------------------------------------------------------------------------- /7-链表/Leetcode21. 合并两个有序链表(200).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 9:53 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for singly-linked list. 7 | class ListNode: 8 | def __init__(self, val=0, next=None): 9 | self.val = val 10 | self.next = next 11 | class Solution: 12 | def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: 13 | if not list1: return list2 14 | if not list2: return list1 15 | dummy = ListNode(-1) 16 | rear = dummy 17 | p, q = list1, list2 18 | while p and q: 19 | if p.val <= q.val: 20 | rear.next = p 21 | p = p.next 22 | else: 23 | rear.next = q 24 | q = q.next 25 | rear = rear.next 26 | rear.next = p if p else q 27 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode23. 合并K个升序链表(140).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/28 9:39 4 | @github: https://github.com/HuKai97 5 | """ 6 | import heapq 7 | # Definition for singly-linked list. 8 | class ListNode: 9 | def __init__(self, val=0, next=None): 10 | self.val = val 11 | self.next = next 12 | class Solution: 13 | def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: 14 | if len(lists) == 0: return 15 | if len(lists) == 1: return lists[0] 16 | dummy = ListNode(-1) 17 | rear = dummy 18 | hp = [] 19 | for idx in range(len(lists)): 20 | if lists[idx]: 21 | heapq.heappush(hp, (lists[idx].val, idx)) 22 | lists[idx] = lists[idx].next 23 | while hp: 24 | val, idx = heapq.heappop(hp) 25 | rear.next = ListNode(val) 26 | rear = rear.next 27 | if lists[idx]: 28 | heapq.heappush(hp, (lists[idx].val, idx)) 29 | lists[idx] = lists[idx].next 30 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode234. 回文链表(61).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def isPalindrome(self, head: ListNode) -> bool: 8 | if not head: return True 9 | dummy = ListNode(-1) 10 | slow, fast = head, head 11 | while fast and fast.next: 12 | fast = fast.next.next 13 | temp = slow.next # 头插法将前半部分逆序 14 | slow.next = dummy.next 15 | dummy.next = slow 16 | slow = temp 17 | if fast: slow = slow.next 18 | p = dummy.next 19 | while p and slow: 20 | if p.val != slow.val: return False 21 | p = p.next 22 | slow = slow.next 23 | return True -------------------------------------------------------------------------------- /7-链表/Leetcode237. 删除链表中的节点(3).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.next = None 6 | 7 | class Solution: 8 | def deleteNode(self, node): 9 | """ 10 | :type node: ListNode 11 | :rtype: void Do not return anything, modify node in-place instead. 12 | """ 13 | node.val = node.next.val 14 | node.next = node.next.next -------------------------------------------------------------------------------- /7-链表/Leetcode24. 两两交换链表中的节点(45).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def swapPairs(self, head: ListNode) -> ListNode: 8 | if not head or not head.next: return head 9 | dummy = ListNode(-1) 10 | rear = dummy ## 新链表尾节点 11 | pre, cur = head, head.next 12 | while cur: 13 | # 前后两两交换位置 14 | temp = cur.next 15 | cur.next = pre 16 | pre.next = temp 17 | # 接到新链表末尾 18 | rear.next = cur 19 | rear = pre 20 | # 下一组 21 | pre = temp 22 | cur = temp.next if temp else None # 防止temp=None temp.next报错 23 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode25. K 个一组翻转链表(249).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/26 18:42 4 | @github: https://github.com/HuKai97 5 | """ 6 | # hard题 7 | # Definition for singly-linked list. 8 | class ListNode: 9 | def __init__(self, val=0, next=None): 10 | self.val = val 11 | self.next = next 12 | class Solution: 13 | def reverse(self, start, end): 14 | # 把start->end-1位置的节点逆序 15 | # return是的end-0 -> start 16 | pre, cur = None, start 17 | while cur != end: 18 | temp = cur.next 19 | cur.next = pre 20 | pre = cur 21 | cur = temp 22 | return pre 23 | def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: 24 | if not head: return None 25 | dummy = ListNode(-1) 26 | rear = dummy 27 | k_start, k_end = head, head 28 | while k_end: 29 | for i in range(k): 30 | if not k_end: 31 | rear.next = k_start 32 | return dummy.next 33 | k_end = k_end.next 34 | # return的是k_end-0 -> k_start 再下面的节点就是k_end(还没翻转) 35 | # k_end-0 -> k_start(翻转后) k_end(还没翻转) 36 | rear.next = self.reverse(k_start, k_end) 37 | rear = k_start 38 | k_start = k_end 39 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode328. 奇偶链表(26).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def oddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]: 8 | if not head or not head.next: return head 9 | head2 = head.next 10 | p, q = head, head2 11 | while q and q.next: 12 | p.next = q.next 13 | p = p.next 14 | q.next = p.next 15 | q = q.next 16 | p.next = head2 17 | return head -------------------------------------------------------------------------------- /7-链表/Leetcode445. 两数相加 II(24).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: 8 | def reverse(head): 9 | pre, cur = None, head 10 | while cur: 11 | temp = cur.next 12 | cur.next = pre 13 | pre = cur 14 | cur = temp 15 | return pre 16 | l1, l2 = reverse(l1), reverse(l2) 17 | dummy = ListNode(-1, l1) 18 | rear = dummy 19 | p, q = l1, l2 20 | while p and q: 21 | p.val += q.val 22 | rear = rear.next 23 | p = p.next 24 | q = q.next 25 | if q: rear.next = q 26 | 27 | pre, cur = dummy.next, dummy.next.next 28 | while cur: 29 | cur.val += (pre.val // 10) 30 | pre.val %= 10 31 | pre = pre.next 32 | cur = cur.next 33 | if pre.val >= 10: 34 | pre.val %= 10 35 | pre.next = ListNode(1) 36 | return reverse(dummy.next) 37 | -------------------------------------------------------------------------------- /7-链表/Leetcode61. 旋转链表(30).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | class ListNode: 3 | def __init__(self, val=0, next=None): 4 | self.val = val 5 | self.next = next 6 | class Solution: 7 | def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: 8 | if not head or not head.next or k == 0: return head 9 | cur = head 10 | length = 0 11 | while cur: 12 | cur = cur.next 13 | length += 1 14 | k %= length 15 | if k == 0: return head 16 | 17 | dummy = ListNode(-1, head) 18 | slow, fast = head, head 19 | pre = dummy 20 | for i in range(k): 21 | fast = fast.next 22 | while fast.next: 23 | pre = pre.next 24 | slow = slow.next 25 | fast = fast.next 26 | head2 = slow.next 27 | slow.next = None 28 | head2_tail = fast 29 | head2_tail.next = dummy.next 30 | dummy.next = head2 31 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode82. 删除排序链表中的重复元素 II(93).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/28 9:01 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for singly-linked list. 7 | class ListNode: 8 | def __init__(self, val=0, next=None): 9 | self.val = val 10 | self.next = next 11 | class Solution: 12 | def deleteDuplicates(self, head: ListNode) -> ListNode: 13 | dummy = ListNode(-101, head) 14 | pre, cur = dummy, head 15 | while cur and cur.next: 16 | if cur.val == cur.next.val: 17 | while cur.val == cur.next.val: 18 | cur = cur.next 19 | if not cur.next: break 20 | pre.next = cur.next 21 | cur = pre.next 22 | else: pre, cur = pre.next, cur.next 23 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode83. 删除排序链表中的重复元素(50).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def deleteDuplicates(self, head: ListNode) -> ListNode: 8 | if not head or not head.next: return head 9 | dummy = ListNode(-101, head) 10 | pre, cur = dummy, head 11 | while cur: 12 | if pre.val == cur.val: 13 | pre.next = cur.next 14 | else: 15 | pre = cur 16 | cur = cur.next 17 | return dummy.next -------------------------------------------------------------------------------- /7-链表/Leetcode86. 分隔链表(18).py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]: 8 | if not head or not head.next: return head 9 | dummy1, dummy2 = ListNode(-1), ListNode(-2) 10 | rear1, rear2 = dummy1, dummy2 11 | p = head 12 | while p: 13 | temp = p.next 14 | if p.val < x: 15 | rear1.next = p 16 | rear1 = rear1.next 17 | rear1.next = None 18 | else: 19 | rear2.next = p 20 | rear2 = rear2.next 21 | rear2.next = None 22 | p = temp 23 | rear1.next = dummy2.next 24 | return dummy1.next -------------------------------------------------------------------------------- /7-链表/Leetcode92. 反转链表 II(134).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/27 10:34 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for singly-linked list. 7 | class ListNode: 8 | def __init__(self, val=0, next=None): 9 | self.val = val 10 | self.next = next 11 | class Solution: 12 | def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode: 13 | dummy = ListNode(-1, head) 14 | pre = dummy 15 | last = head 16 | for _ in range(left-1): # 找到left前面一个节点 17 | pre = pre.next 18 | for _ in range(right): # 找到right后面一个节点 19 | last = last.next 20 | cur = pre.next 21 | pre.next = last 22 | for _ in range(right-left+1): # 尾插法 逆序 23 | temp = cur.next 24 | cur.next = pre.next 25 | pre.next = cur 26 | cur = temp 27 | return dummy.next -------------------------------------------------------------------------------- /7-链表/剑指 Offer 22. 链表中倒数第k个节点(88).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 9:28 4 | @github: https://github.com/HuKai97 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 | class Solution: 13 | def getKthFromEnd(self, head: ListNode, k: int) -> ListNode: 14 | fast = head 15 | for i in range(k): 16 | fast = fast.next 17 | slow = head 18 | while fast: 19 | slow = slow.next 20 | fast = fast.next 21 | return slow -------------------------------------------------------------------------------- /8-排序/Leetcode148. 排序链表(92).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/6/0 10:13 4 | @github: https://github.com/HuKai97 5 | """ 6 | # Definition for singly-linked list. 7 | class ListNode: 8 | def __init__(self, val=0, next=None): 9 | self.val = val 10 | self.next = next 11 | class Solution: 12 | def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: 13 | def merge(head1, head2): 14 | dummy = ListNode(-1) 15 | rear = dummy 16 | p, q = head1, head2 17 | while p and q: 18 | if p.val < q.val: 19 | rear.next = p 20 | rear = rear.next 21 | p = p.next 22 | else: 23 | rear.next = q 24 | rear = rear.next 25 | q = q.next 26 | rear.next = p if p else q 27 | return dummy.next 28 | def merge_sort(head): 29 | if not head or not head.next: return head 30 | dummy = ListNode(-1, head) 31 | slow, fast = dummy, head 32 | while fast and fast.next: 33 | slow = slow.next 34 | fast = fast.next.next 35 | head2 = slow.next 36 | slow.next = None 37 | left, right = merge_sort(head), merge_sort(head2) 38 | return merge(left, right) 39 | return merge_sort(head) -------------------------------------------------------------------------------- /8-排序/Leetcode179. 最大数(43).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def largestNumber(self, nums: List[int]) -> str: 3 | if not nums: return '' 4 | if len(nums) == 1: return str(nums[0]) 5 | def quick_sort(nums, left, right): 6 | if left >= right: return 7 | temp = left 8 | i, j = left, right 9 | while i < j: 10 | # 比较数字的第1位 11 | # 降序排列 12 | while i < j and (nums[i]+nums[temp]) > (nums[temp]+nums[i]): i += 1 13 | while i < j and (nums[j]+nums[temp]) <= (nums[temp]+nums[j]): j -= 1 14 | nums[i], nums[j] = nums[j], nums[i] 15 | nums[temp], nums[i] = nums[i], nums[temp] 16 | quick_sort(nums, left, temp-1) 17 | quick_sort(nums, temp+1, right) 18 | nums = list(map(str, nums)) # [10, 2] -> ['10', '2'] 19 | quick_sort(nums, 0, len(nums)-1) 20 | non_zero_location = 0 21 | # len(nums) - 0 至少还是要保留一位的 比如'00'最终输出'0' 22 | while non_zero_location < len(nums) - 1 and nums[non_zero_location] == '0': 23 | non_zero_location += 1 24 | return ''.join(nums[non_zero_location:]) 25 | -------------------------------------------------------------------------------- /8-排序/Leetcode215. 数组中的第K个最大元素(327).py: -------------------------------------------------------------------------------- 1 | """ 2 | @Author: HuKai 3 | @Date: 2022/5/25 8:49 4 | @github: https://github.com/HuKai97 5 | """ 6 | # 堆排序 7 | # 视频讲解:https://www.bilibili.com/video/av47196993?from=search&seid=4351042127032790802&rt=V%2FymTlOu4ow%2Fy4xxNWPUZ21pzQFbVqcJSjKcQx%2BMqj0%3D 8 | class Solution: 9 | def findKthLargest(self, nums: List[int], k: int) -> int: 10 | # 堆排序 求最大用小顶堆 求最小用大顶堆 11 | # 0、自己实现堆 12 | def heapify(tree, n, i): # 对以tree[i]为根节点的子树进行堆排序 13 | c1, c2 = 2 * i + 1, 2 * i + 2 14 | min_idx = i 15 | if c1 < n and tree[c1] < tree[min_idx]: 16 | min_idx = c1 17 | if c2 < n and tree[c2] < tree[min_idx]: 18 | min_idx = c2 19 | if min_idx != i: 20 | tree[i], tree[min_idx] = tree[min_idx], tree[i] 21 | heapify(tree, n, min_idx) 22 | 23 | def build_heap(tree, n): # 对整棵树进行建堆 24 | last_node = n - 1 25 | parent = (last_node - 1) // 2 26 | for i in range(parent, -1, -1): 27 | heapify(tree, n, i) 28 | 29 | hp = nums[:k] 30 | build_heap(hp, k) 31 | for i in range(k, len(nums)): 32 | if nums[i] > hp[0]: 33 | hp[0] = nums[i] 34 | heapify(hp, k, 0) 35 | return hp[0] 36 | 37 | # 2、掉包实现堆 38 | hp = nums[:k] 39 | heapq.heapify(hp) # 建立最小堆 40 | for i in range(k, len(nums)): 41 | if nums[i] > hp[0]: 42 | heapq.heappop(hp) # 弹出堆顶元素(最小值) 堆还是维护小顶堆 43 | heapq.heappush(hp, nums[i]) # push进一个元素 堆还是维护小顶堆 44 | return hp[0] 45 | -------------------------------------------------------------------------------- /8-排序/Leetcode347. 前 K 个高频元素(18).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def topKFrequent(self, nums: List[int], k: int) -> List[int]: 3 | def heapify(tree, n, i): 4 | c1, c2 = 2*i+1, 2*i+2 5 | min_idx = i 6 | if c1 < n and tree[c1][1] < tree[min_idx][1]: 7 | min_idx = c1 8 | if c2 < n and tree[c2][1] < tree[min_idx][1]: 9 | min_idx = c2 10 | if min_idx != i: 11 | tree[i], tree[min_idx] = tree[min_idx], tree[i] 12 | heapify(tree, n, min_idx) 13 | def build_heap(tree, n): 14 | last_node = n - 1 15 | parent = (last_node - 1) // 2 16 | for i in range(parent, -1, -1): 17 | heapify(tree, n, i) 18 | mapp = defaultdict(int) 19 | for i in range(len(nums)): 20 | mapp[nums[i]] += 1 # defaultdict(, {1: 3, 2: 2, 3: 1}) 21 | mapp = list(mapp.items()) # [(1, 3), (2, 2), (3, 1)] 22 | hp = mapp[:k] 23 | build_heap(hp, k) 24 | for i in range(k, len(mapp)): 25 | if mapp[i][1] > hp[0][1]: 26 | hp[0] = mapp[i] 27 | heapify(hp, k, 0) 28 | return [x[0] for x in hp] -------------------------------------------------------------------------------- /8-排序/Leetcode剑指 Offer 45. 把数组排成最小的数(17).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minNumber(self, nums: List[int]) -> str: 3 | def quick_sort(nums, left, right): 4 | if left >= right: return 5 | temp = left 6 | i, j = left, right 7 | while i < j: 8 | # 为什么j在前? 9 | while i < j and (nums[j] + nums[temp]) >= (nums[temp] + nums[j]): j -= 1 10 | while i < j and (nums[i] + nums[temp]) <= (nums[temp] + nums[i]): i += 1 11 | nums[i], nums[j] = nums[j], nums[i] 12 | nums[temp], nums[i] = nums[i], nums[temp] 13 | quick_sort(nums, left, i - 1) 14 | quick_sort(nums, i + 1, right) 15 | if not nums: return '' 16 | nums = [str(num) for num in nums] 17 | quick_sort(nums, 0, len(nums) - 1) 18 | return ''.join(nums) 19 | -------------------------------------------------------------------------------- /9-图/Dijkstra.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 输入:邻接矩阵, 3 | 输出,源点V0到各点的最小距离 4 | ''' 5 | def Dijkstra(graph,v0): 6 | n=len(graph) 7 | # final保存那些已经被选定的点,D保存当前已经被选定点的最小路径, 8 | final,D=[0]*n,[0]*n 9 | for i in range(n): 10 | D[i]=graph[v0][i] 11 | D[v0] = 0 12 | final[v0] = 1 13 | for v in range(1,n): 14 | min = float("Inf") 15 | for w in range(0,n): # 实现最小优先队列的效果 16 | if not final[w] and D[w] bool: 3 | # https://www.bilibili.com/video/BV1Xp4y1Y7FJ?spm_id_from=333.337.search-card.all.click&vd_source=5f6bbc1038b075757cb446f800f3cd56 4 | # 入度:有哪些顶点指向这个顶点 5 | indegree = [0 for _ in range(numCourses)] # 入度表 6 | edges = [[] for _ in range(numCourses)] # 临接矩阵 7 | # node2 -> node1 8 | for node1, node2 in prerequisites: 9 | indegree[node1] += 1 10 | edges[node2].append(node1) 11 | queue = [] 12 | for i in range(len(indegree)): 13 | if indegree[i] == 0: 14 | queue.append(i) 15 | while queue: 16 | pop_node = queue.pop(0) 17 | numCourses -= 1 18 | next_nodes = edges[pop_node] 19 | for next_node in next_nodes: 20 | indegree[next_node] -= 1 21 | if indegree[next_node] == 0: 22 | queue.append(next_node) 23 | return numCourses == 0 24 | -------------------------------------------------------------------------------- /9-图/Leetcode210. 课程表 II(16).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: 3 | indegress = [0 for _ in range(numCourses)] # 入度表 4 | edges = [[] for _ in range(numCourses)] # 临接矩阵 5 | res = [] 6 | for node1, node2 in prerequisites: # node2 -> node1 7 | indegress[node1] += 1 8 | edges[node2].append(node1) 9 | queue = [] 10 | for i in range(numCourses): 11 | if indegress[i] == 0: queue.append(i) 12 | while queue: 13 | pop_node = queue.pop(0) 14 | res.append(pop_node) 15 | next_nodes = edges[pop_node] 16 | for next_node in next_nodes: 17 | indegress[next_node] -= 1 18 | if indegress[next_node] == 0: 19 | queue.append(next_node) 20 | if numCourses != len(res): res = [] 21 | return res -------------------------------------------------------------------------------- /9-图/Leetcode547. 省份数量(11).py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findCircleNum(self, isConnected: List[List[int]]) -> int: 3 | n = len(isConnected) 4 | edges = [[] for _ in range(n)] 5 | for i in range(n): 6 | for j in range(n): 7 | if i != j and isConnected[i][j] == 1: 8 | if j not in edges[i]: edges[i].append(j) 9 | if i not in edges[j]: edges[j].append(i) 10 | count = 0 11 | used = [False for _ in range(n)] 12 | for node in range(n): 13 | if used[node] == False: 14 | used[node] = True 15 | queue = [node] 16 | while queue: 17 | cur_node = queue.pop(0) 18 | for next_node in edges[cur_node]: 19 | if used[next_node] == False: 20 | used[next_node] = True 21 | queue.append(next_node) 22 | count += 1 23 | return count 24 | 25 | 26 | # dfs 27 | class Solution: 28 | def findCircleNum(self, isConnected: List[List[int]]) -> int: 29 | def dfs(idx, used, edges): 30 | if used[idx] == True: return 31 | used[idx] = True 32 | next_nodes = edges[idx] 33 | for next_node in next_nodes: 34 | dfs(next_node, used, edges) 35 | 36 | 37 | n = len(isConnected) 38 | edges = [[]for _ in range(n)] 39 | for i in range(n): 40 | for j in range(n): 41 | if i != j and isConnected[i][j] == 1: 42 | if j not in edges[i]: edges[i].append(j) 43 | if i not in edges[j]: edges[j].append(i) 44 | count = 0 45 | queue = [] 46 | used = [False for _ in range(n)] 47 | for i in range(n): 48 | if used[i] == False: 49 | dfs(i, used, edges) 50 | count += 1 51 | return count -------------------------------------------------------------------------------- /9-图/Leetcode841. 钥匙和房间.py: -------------------------------------------------------------------------------- 1 | # bfs 2 | class Solution: 3 | def canVisitAllRooms(self, rooms: List[List[int]]) -> bool: 4 | queue = [0] 5 | used = [False for _ in range(len(rooms))] 6 | used[0] = True 7 | while queue: 8 | cur = queue.pop(0) 9 | next_nodes = rooms[cur] 10 | for next_node in next_nodes: 11 | if used[next_node] == False: 12 | used[next_node] = True 13 | queue.append(next_node) 14 | for falg in used: 15 | if falg == False: return False 16 | return True 17 | 18 | 19 | # dfs 20 | class Solution: 21 | def canVisitAllRooms(self, rooms: List[List[int]]) -> bool: 22 | def dfs(idx, used): 23 | if used[idx] == True: return 24 | used[idx] = True 25 | next_nodes = rooms[idx] 26 | for next_node in next_nodes: 27 | dfs(next_node, used) 28 | 29 | used = [False for _ in range(len(rooms))] 30 | dfs(0, used) 31 | for i in range(len(used)): 32 | if used[i] == False: return False 33 | return True --------------------------------------------------------------------------------