8月22日打卡 📅 发布时间:2026/8/25 14:48:59 👁 浏览次数: LeetCode 49字母异位词分组1. 核心思想把字母相同、顺序不同的字符串分到一起。例如eat → aet tea → aet ate → aet排序后相同所以属于同一组。2. 解题方法使用哈希表dict 排序key.join(sorted(s))把排序后的字符串当作这个字符串的“身份证”。然后ifkeynotinres:res[key][]res[key].append(s)相同key的字符串就会自动进入同一个列表。3. 最终代码classSolution:defgroupAnagrams(self,strs:List[str])-List[List[str]]:res{}forsinstrs:key.join(sorted(s))ifkeynotinres:res[key][]res[key].append(s)returnlist(res.values())4. 你这次遇到的两个问题①和res[key][]是赋值。res[key][]是比较不能用来创建列表。②values()res.values()获取字典里的所有 value。所以最后list(res.values())得到题目要求的结果。⭐ 一句话记忆排序得到“身份证”字典根据身份证分组。这道题重点不是字符串本身而是学会 **“提取特征 → 作为 key → 哈希表分组”**。你这次代码写得完全正确而且思路已经掌握了。简单总结这道题LeetCode 539最小时间差1. 核心思路把时间23:59转换成一天中的分钟数23 × 60 59 1439这样时间就从字符串变成了数字方便计算。2. 解题步骤时间字符串 ↓ 转换成分钟数 ↓ 排序 ↓ 比较相邻时间的差值 ↓ 再比较“最后一个 → 第一个”的跨天差值 ↓ 取最小值3. 你的代码中的关键部分转换时间hourint(time[:2])minuteint(time[3:])timehour*60minute排序res.sort()比较相邻时间foriinrange(1,len(res)):ansmin(ans,res[i]-res[i-1])最后别忘了跨午夜ansmin(ans,1440-(res[-1]-res[0]))因为一天有24 × 60 1440 分钟4. 为什么只比较相邻排序以后10 → 30 → 5010和50的距离是 40但中间有30所以30 - 10 20 50 - 30 20因此最小差值一定出现在排序后的相邻元素之间。5. 这道题最重要的坑⚠️不要忘记首尾跨天。例如23:59 → 00:00转换1439 → 0普通差值1439但真正只差1440 - 1439 1所以要额外计算1440-(res[-1]-res[0])⭐ 一句话记忆时间转分钟 → 排序 → 比相邻 → 别忘首尾跨天。你这道题目前的代码已经没有问题可以作为自己的标准答案记下来。当然可以。你这份代码写对了而且已经真正把 BFS 的层序遍历模板写出来了。LeetCode 513找树左下角的值一、核心思路题目要求找到二叉树最底层最左边的节点。使用BFS广度优先搜索/ 层序遍历。思路一层一层遍历 ↓ 每一层记录第一个节点 ↓ 不断向下一层 ↓ 最后一次记录的节点 ↓ 就是最底层最左边的节点二、你的代码核心部分whilequeue:foriinrange(len(queue)):这里你已经掌握了一个很重要的技巧len(queue)表示当前这一层有多少个节点。然后ifi0:ansnode.val因为i 0就是当前这一层的第一个节点也就是最左边的节点。所以每一层都会更新一次ans最后一层更新的那个值就是答案。三、队列的三个关键操作你这道题需要记住queuedeque([root])建立队列把根节点放进去。nodequeue.popleft()从队列前面取出节点。queue.append(node.left)queue.append(node.right)把下一层的左右孩子放进队列。四、你的代码可以记成这个模板queuedeque([root])ansroot.valwhilequeue:foriinrange(len(queue)):nodequeue.popleft()ifi0:ansnode.valifnode.left:queue.append(node.left)ifnode.right:queue.append(node.right)returnans⭐ 一句话总结BFS 一层一层遍历每层取第一个节点最后一层的第一个节点就是答案。这道题你真正学会的是二叉树 BFS 层序遍历模板以后遇到“按层处理二叉树”的题目都可以想到这个框架。当然这道题可以简单整理成下面这份笔记。LeetCode 814二叉树剪枝1. 题目要求删除二叉树中所有不包含1的子树。也就是说如果一棵子树里面没有任何 1 ↓ 删除整棵子树2. 核心思路递归 后序遍历对于当前节点root先处理左子树 → 再处理右子树 → 最后处理自己。root.leftself.pruneTree(root.left)root.rightself.pruneTree(root.right)处理完左右子树之后如果root.val0并且root.leftisNoneroot.rightisNone说明当前节点是0而且下面已经没有任何节点了。所以这棵子树里面肯定没有1直接删除returnNone否则returnroot3. 最终代码classSolution:defpruneTree(self,root:Optional[TreeNode])-Optional[TreeNode]:ifrootisNone:returnNoneroot.leftself.pruneTree(root.left)root.rightself.pruneTree(root.right)ifroot.val0androot.leftisNoneandroot.rightisNone:returnNonereturnroot4. ⭐ 最重要的一句话先递归处理左右子树再判断当前节点是否应该删除。这就是典型的后序遍历左 → 右 → 根5. 这道题你要记住的知识点814 二叉树剪枝 ↓ 递归 ↓ 后序遍历 ↓ 先处理左右孩子 ↓ 如果当前节点是 0 且左右都为空 ↓ return None删除另外你这道题刚才遇到的几个 Python 小问题也值得记一下# 赋值# 判断相等isNone# 判断是否为空以及self.pruneTree()# 注意函数名拼写这道题的算法思路你已经写出来了现在主要就是把 Python 语法细节写准确。当然这道题简单总结成一份笔记LeetCode 897递增顺序搜索树1. 题目要求把一棵二叉搜索树 BST重新排列成1 \ 2 \ 3 \ 4 \ 5要求按从小到大排列每个节点的left None用right连接下一个节点2. 核心知识 ⭐BST 的中序遍历是从小到大的顺序。中序遍历左 → 根 → 右所以BST ↓ 中序遍历 ↓ 1 → 2 → 3 → 4 → 5...3. 核心代码inorder(node.left)node.leftNonecur.rightnode curcur.right inorder(node.right)理解成① 先去左边 ↓ ② 当前节点的左边断掉 ↓ ③ 把当前节点接到 cur 后面 ↓ ④ cur 往后移动 ↓ ⑤ 再去右边4.cur是什么把cur理解成目前已经排好的链表的最后一个节点。例如1 → 2 → 3 ↑ cur现在来了4cur.right 4 cur 4变成1 → 2 → 3 → 4 ↑ cur5.dummy是什么dummyTreeNode(0)curdummydummy是一个假的头节点方便我们统一连接。最后returndummy.right因为真正的第一个节点在dummy.right。6.nonlocal cur因为cur在外层curdummy但是我们要在inorder()里面修改它curcur.right所以需要nonlocalcur意思就是使用并修改外层的那个cur。7. 这道题最终模板defincreasingBST(self,root):dummyTreeNode(0)curdummydefinorder(node):nonlocalcurifnodeisNone:returninorder(node.left)node.leftNonecur.rightnode curcur.right inorder(node.right)inorder(root)returndummy.right⭐ 一句话记忆897 BST 中序遍历 cur逐个连接节点。另外你这道题最后自己写出来时犯的错误也要记住定义了inorder()不代表它会自动执行必须写inorder(root)这属于递归题非常常见的错误。当然这道题可以简单记成下面这份笔记。LeetCode 653两数之和 IV1. 题目给一棵二叉搜索树和目标值k判断树中是否存在两个不同节点a b k有就返回True否则False。2. 核心思路 ⭐DFS 遍历二叉树 set记录已经出现的数字。遍历到当前节点node时k-node.val就是我们需要寻找的另一个数字。如果它已经在seen中ifk-node.valinseen:returnTrue说明当前值 之前出现的值 k3. 代码模板seenset()defdfs(node):ifnodeisNone:returnFalseifk-node.valinseen:returnTrueseen.add(node.val)returndfs(node.left)ordfs(node.right)returndfs(root)4. 遍历顺序你这份代码采用的是根 → 左 → 右也就是前序遍历。但这道题并不要求一定前序核心是把所有节点遍历一遍同时用set查找需要的数字。5. 你这次遇到的两个 Python 错误一定记住False# ✅false# ❌以及set.add()# ✅list.append()# ✅所以seenset()seen.add(node.val)⭐ 一句话记忆653 遍历二叉树 set对于当前值x寻找k-x。这个题本质上就是把你之前学过的**“两数之和”**搬到了二叉树上。当然这道题简单总结成一份笔记LeetCode 69x 的平方根1. 题目给一个非负整数x返回√x 的整数部分例如√8 ≈ 2.828返回22. 你的最初思路从0开始一个一个试foriinrange(x):寻找最大的i满足i × i x这个思路是正确的但是x很大时会很慢。3. 更好的思路二分查找 ⭐因为0² 1² 2² 3² 4² ...是越来越大的。我们可以通过mid * mid和x比较mid² x → mid 太大 → right mid - 1 mid² x → mid 可以 → 记录 mid → 继续向右找更大的4. 核心代码left0rightx ans0whileleftright:mid(leftright)//2ifmid*midx:ansmid leftmid1else:rightmid-1returnans5. 最重要的知识点这道题不是让你真正学习“怎么计算平方根”而是让你学习当答案具有单调性可以考虑二分查找。判断条件mid² x满足小的数字可能满足 ↓ 达到某个位置 ↓ 后面的数字全部不满足所以可以二分。⭐ 一句话记忆69 找最大的i使i² x→ 具有单调性 → 二分查找。1122. 数组的相对排序——简单笔记1. 核心思路这道题就是先统计arr1中每个数字出现几次然后按照arr2的顺序依次放入答案最后把arr2没出现的数字从小到大放进去。可以记成计数 → 按 arr2 排 → 剩余排序2. 关键代码count{}forxinarr1:count[x]count.get(x,0)1统计每个数字出现次数。例如arr1 [2, 3, 2, 1, 3] count { 2: 2, 3: 2, 1: 1 }3. 按照arr2排序forxinarr2:foriinrange(count[x]):res.append(x)delcount[x]比如arr2 [3, 2, 1]就按照3 → 3 2 → 2 1 → 1的顺序放入答案。count[x]表示x 还剩多少个。del count[x]表示x 已经处理完了把它从字典中删除。4. 处理剩下的数字forxinsorted(count):foriinrange(count[x]):res.append(x)这里的count中只剩下arr2 没出现过的数字。例如count{7:2,19:1}那么sorted(count)得到[7, 19]最后放进去即可。⭐ 这道题最值得记住的 Python 知识点sorted()和sort()sorted(count)返回一个新的排序结果原来的count还是字典。所以推荐forxinsorted(count):而不要写countsorted(count)因为这样会把原来的字典变成列表之后count[x]就不能再表示“x 出现了几次”了。最终模板classSolution:defrelativeSortArray(self,arr1,arr2):count{}res[]# ① 统计次数forxinarr1:count[x]count.get(x,0)1# ② 按 arr2 顺序forxinarr2:foriinrange(count[x]):res.append(x)delcount[x]# ③ 剩余数字升序forxinsorted(count):foriinrange(count[x]):res.append(x)returnres一句话记忆哈希表统计次数arr2决定前半部分顺序sorted(count)决定剩余部分顺序。