├── .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 |
4 |
5 |
21 |
22 |
23 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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
--------------------------------------------------------------------------------