一、两数之和
1. 题面
1. 两数之和
难度:简单
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9输出:[0,1]解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。示例 2:
输入:nums = [3,2,4], target = 6输出:[1,2]示例 3:
输入:nums = [3,3], target = 6输出:[0,1]提示:
2 <= nums.length <= 10^4-10^9 <= nums[i] <= 10^9-10^9 <= target <= 10^9- 只会存在一个有效答案
进阶: 你可以想出一个时间复杂度小于 O(n^2) 的算法吗?
2. 解法
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 本题是基础哈希表空间换时间,第一次做的时候没有加else,虽然这题无所谓,但是别的题可能会有区别。
- 还有一个细节,我用了dict,实际上覆盖了内置的dict,还是用mp比较好
二、字母的同分异构词
1. 题面
49. 字母异位词分组
难度:中等
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
解释:
- 在 strs 中没有字符串可以通过重新排列来形成
"bat"。 - 字符串
"nat"和"tan"是字母异位词,因为它们可以重新排列以形成彼此。 - 字符串
"ate","eat"和"tea"是字母异位词,因为它们可以重新排列以形成彼此。
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
提示:
1 <= strs.length <= 10^40 <= strs[i].length <= 100strs[i]仅包含小写字母
2. 解法1-{}
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 解法2-defaultdict
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 实际上做到这里的时候想到用这种方法,但是还是有点小梗塞。容易出错的点:list不能作为key,需要tuple;ord函数别忘了;mp.values()的用法,返回values的迭代器,用list转为答案数组
- 如果使用默认数组,不需要先判key空产生[]再append,直接用defaultdict(list),等于设置了键值的值默认为list的默认值空列表;同理,这里设置为int就是默认值为0
- 注意输入处理,因为默认复制力扣的输入是有"的,而想去掉双引号,需要用单引号包裹来strip('"')。
三、最长连续序列
1. 题面
128. 最长连续序列
难度:中等
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]输出:4解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]输出:9示例 3:
输入:nums = [1,0,1,2]输出:3提示:
0 <= nums.length <= 10^5-10^9 <= nums[i] <= 10^9
2. 解法
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题肯定也是第一时间想到空间换时间,但是没想到哈希存什么。主要是害怕for套for复杂度超过on,但是其实里面是有限次循环,还是on。
- 不用set的话问了题友,也是哈希打一遍标记,然后前后找。
四、移动零
1. 题面
283. 移动零
难度:简单
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]输出: [1,3,12,0,0]示例 2:
输入: nums = [0]输出: [0]提示 :
1 <= nums.length <= 10^4-2^31 <= nums[i] <= 2^31 - 1
**进阶:**你能尽量减少完成的操作次数吗?
2. 解法
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
2. 反思
- 没啥好说的,就是简单的移动插入,跟插入排序有点像,需要注意的是while循环下面别忘了移动变量,这个经常忘记。
五、盛水最多的容器
1. 题面
11. 盛最多水的容器
难度:中等
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
示例 1:

输入:[1,8,6,2,5,4,8,3,7]输出:49解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。示例 2:
输入:height = [1,1]输出:1提示:
n == height.length2 <= n <= 10^50 <= height[i] <= 10^4
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题老是幻视成单调栈,但是实际上不是需求"第一个比xx大/小"的量,卡了半天不知道怎么写。
- 但是这题实际上是一个贪心,理论依据是"移动高的那边一定不可能得到更优解",所以只能移动矮的那边去保留希望。这个解释还是让人感觉懵懵的。
- 询问码u,最好的解释是"移动小的那根不一定能让水更多,但是大的那根肯定会变少",因为移动大的那根,小的那根被限制住了,无论如何都不会变大,反而使宽度减小。
六、三数之和
1. 题面
15. 三数之和
难度:中等
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]输出:[[-1,-1,2],[-1,0,1]]解释:nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。注意,输出的顺序和三元组的顺序并不重要。示例 2:
输入:nums = [0,1,1]输出:[]解释:唯一可能的三元组和不为 0 。示例 3:
输入:nums = [0,0,0]输出:[[0,0,0]]解释:唯一可能的三元组和为 0 。提示:
3 <= nums.length <= 3000-10^5 <= nums[i] <= 10^5
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
七、接雨水
1. 题面
42. 接雨水
难度:困难
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]输出:6解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。示例 2:
输入:height = [4,2,0,3,2,5]输出:9提示:
n == height.length1 <= n <= 2 * 10^40 <= height[i] <= 10^5
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 实际解题的思路已经写的很清楚了,重要的就是单调栈的思想。
- 注意这里不需要全部弹出,右侧可能没有更高的边界;需要注意的是左侧可能会没有水洼,所以要保护一下空栈。
- 这里right、right_val就是当前的i、h,更简的话可以不定义right相关变量
八、无重复字符的最长子串
1. 题面
3. 无重复字符的最长子串
难度:中等
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = "abcabcbb"输出: 3解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab" 也是正确答案。示例 2:
输入: s = "bbbbb"输出: 1解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。示例 3:
输入: s = "pwwkew"输出: 3解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。提示:
0 <= s.length <= 5 * 10^4s由英文字母、数字、符号和空格组成
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 我其实感觉滑动窗口的思路比较简单,但是比较考验码力,特别是边界和条件判断的地方,特别容易绕晕。
- 我第一版有很多疏漏,现在是更新过的版本。易错点1:left移动的条件不对,应该写在while里面的是"不合法"的条件,移动left直到合法;易错点2:不能用len(window),即使哈希值归0了,键值对还在。
⑨、找到字符串中所有字母异位词
1. 题面
438. 找到字符串中所有字母异位词
难度:中等
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
示例 1:
输入: s = "cbaebabacd", p = "abc"输出: [0,6]解释:起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。示例 2:
输入: s = "abab", p = "ab"输出: [0,1,2]解释:起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。提示:
1 <= s.length, p.length <= 3 * 10^4s和p仅包含小写字母
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题是非常值得反复练习的滑动窗口题目,我重写的时候又弄错了,将right - left == len(p)作为while条件,这样还要先移动right,非常复杂容易出错。正确的一体化思路,应当是将valid个数合格作为左边收缩的起点,收缩一直进行到valid不满足为止。
- 左右的操作实际上是对称的,取值、移动,右侧先动哈希再看valid,左侧先看valid再动哈希。其中的区别在于,left是看先满足再丢弃,right是先拿取再看是否满足。
十、和为k的子数组
1. 题面
560. 和为 K 的子数组
难度:中等
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2输出:2示例 2:
输入:nums = [1,2,3], k = 3输出:2提示:
1 <= nums.length <= 2 * 10^4-1000 <= nums[i] <= 1000-10^7 <= k <= 10^7
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 和前面相比算是比较简单的滑动窗口,不需要哈希表只需要维持一个total
- 但是这题意义在于,告诉我们滑动窗口是用来求"连续子部分的与顺序无关属性"时使用(有点废话因为顺序有关就直接遍历了)
十一、滑动窗口最大值
1. 题面
239. 滑动窗口最大值
难度:困难
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3输出:[3,3,5,5,6,7]解释:滑动窗口的位置 最大值--------------- -----[1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7示例 2:
输入:nums = [1], k = 1输出:[1]提示:
1 <= nums.length <= 10^5-10^4 <= nums[i] <= 10^41 <= k <= nums.length
2. 解法1 - 单调队列
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 解法2 - 堆
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 这题一开始想到的还是滑动窗口,但是不知道怎么维护max的回退,一开始想到用栈,但是栈没办法处理退出的就是最大值,第二个大值在哪这个问题。
- 这题的破局关键其中之一就是存储下标。对于可能有重复值的题目,存下标是最安全稳妥的方法。
- 这题的标准思路是单调队列,单调队列常被用来解决"有效性窗口"的问题,比如这题,就是维持了一系列候选有效的max值,left移动就看左边的第一个值有没有过期(也就是看最大值有没有过期)
- 同样可以用堆来解决,两者思路差不多,都是存储候选,left移动看看候选最大值有没有过期,过期让老二上。(但是复杂度跌到onlogn了)
- 特别注意,这里无论是队列长度还是堆长度,都跟窗口的长度无关。所以我们判断窗口是否合法,还是要用下标来判断。
十二、最小覆盖子串
1. 题面
76. 最小覆盖子串
难度:困难
给定两个字符串 s 和 t,长度分别是 m 和 n,返回 s 中的 最短窗口 子串 ,使得该子串包含 t 中的每一个字符( 包括重复字符 )。如果没有这样的子串,返回空字符串 ""。
测试用例保证答案唯一。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"输出:"BANC"解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。示例 2:
输入:s = "a", t = "a"输出:"a"解释:整个字符串 s 是最小覆盖子串。示例 3:
输入: s = "a", t = "aa"输出: ""解释: t 中两个字符 'a' 均应包含在 s 的子串中,因此没有符合条件的子字符串,返回空字符串。提示:
m == s.lengthn == t.length1 <= m, n <= 10^5s和t由英文字母组成
进阶: 你能设计一个在 O(m + n) 时间内解决此问题的算法吗?
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 滑动窗口果然容易码错。虽然对称的left、right操作已经记熟了,但是仍然忘记先看need再哈希。
- 满足条件再收缩,收缩前就已经满足的,在收缩代码里拿答案;收缩后才合法的,在收缩完成后拿答案。两种情况取决于写的while,一定要注意分辨。
十三、最大子数组和
1. 题面
53. 最大子数组和
难度:中等
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]输出:6解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。示例 2:
输入:nums = [1]输出:1示例 3:
输入:nums = [5,4,-1,7,8]输出:23提示:
1 <= nums.length <= 10^5-10^4 <= nums[i] <= 10^4
进阶: 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题被叫做Kadane算法,他其实是一种动态规划的思想的优化简化,用于处理另起炉灶还是继续加入的选择哪个更好,然后维持一个全局的最优ans
十四、合并区间
1. 题面
56. 合并区间
难度:中等
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]输出:[[1,6],[8,10],[15,18]]解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].示例 2:
输入:intervals = [[1,4],[4,5]]输出:[[1,5]]解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。示例 3:
输入:intervals = [[4,7],[1,4]]输出:[[1,7]]解释:区间 [1,4] 和 [4,7] 可被视为重叠区间。提示:
1 <= intervals.length <= 10^4intervals[i].length == 20 <= starti <= endi <= 10^4
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题重合需要注意可能传进去之前的,传入的左端点还不一定按时间排序,需要自己排序一下。
- 排序算法intervals.sort(key = lambda x
0 )可以简写成intervals.sort()。因为python的sort可以默认按照第一项排序。
十五、轮转数组
1. 题面
189. 轮转数组
难度:中等
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3输出: [5,6,7,1,2,3,4]解释:向右轮转 1 步: [7,1,2,3,4,5,6]向右轮转 2 步: [6,7,1,2,3,4,5]向右轮转 3 步: [5,6,7,1,2,3,4]示例 2:
输入:nums = [-1,-100,3,99], k = 2输出:[3,99,-1,-100]解释:向右轮转 1 步: [99,-1,-100,3]向右轮转 2 步: [3,99,-1,-100]提示:
1 <= nums.length <= 10^5-2^31 <= nums[i] <= 2^31 - 10 <= k <= 10^5
进阶:
- 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
- 你可以使用空间复杂度为
O(1)的 原地 算法解决这个问题吗?
2.题解1 - 切片
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3.题解2 - 三次翻转
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 题解3 - 环状替换
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
5. 反思
- 如果不限制空间,这题cpp也能直接建立队列做。python切片的复杂度是O(k),会创建新列表
- 但是本题想考察的重点其实是你能不能写出O(1)的算法,也就是真的原地。
- 三次翻转是比较常规的方法,后面链表题也是这么写的。
- 环状替换比较绕,比较硬核,到时候有空再看看吧。
十六、除自身以外数组的乘积
1. 题面
238. 除了自身以外数组的乘积
难度:中等
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除了 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法, 且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]输出: [24,12,8,6]示例 2:
输入: nums = [-1,1,0,-3,3]输出: [0,0,9,0,0]提示:
2 <= nums.length <= 10^5-30 <= nums[i] <= 30- 输入 保证 数组
answer[i]在 32 位 整数范围内
进阶: 你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)
2. 题解1 - 前缀后缀积O(n)
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - 空间O(1)
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 经典前缀后缀题,需要注意的是不常用的后缀怎么设置数组(边界问题)
十七、缺失的第一个正数
1. 题面
41. 缺失的第一个正数
难度:困难
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]输出:3解释:范围 [1,2] 中的数字都在数组中。示例 2:
输入:nums = [3,4,-1,1]输出:2解释:1 在数组中,但 2 没有。示例 3:
输入:nums = [7,8,9,11,12]输出:1解释:最小的正数 1 没有出现。提示:
1 <= nums.length <= 10^5-2^31 <= nums[i] <= 2^31 - 1
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题不给你用额外的空间,但是要想到下标和数值本身是有关系的,直接用下标来对应就行了。
十八、矩阵置零
1. 题面
73. 矩阵置零
难度:中等
给定一个 _m_ x _n_ 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法 。
示例 1:

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]输出:[[1,0,1],[0,0,0],[1,0,1]]示例 2:

输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]提示:
m == matrix.lengthn == matrix[0].length1 <= m, n <= 200-2^31 <= matrix[i][j] <= 2^31 - 1
进阶:
- 一个直观的解决方案是使用
O(_m__n_)的额外空间,但这并不是一个好的解决方案。 - 一个简单的改进方案是使用
O(_m_ + _n_)的额外空间,但这仍然不是最好的解决方案。 - 你能想出一个仅使用常量空间的解决方案吗?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解 - 常数空间
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 也是限制空间,这时候一定要活用原本的结构。
十九、螺旋矩阵
54. 螺旋矩阵
难度:中等
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]输出:[1,2,3,6,9,8,7,4,5]示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]输出:[1,2,3,4,8,12,11,10,9,5,6,7]提示:
m == matrix.lengthn == matrix[i].length1 <= m, n <= 10-100 <= matrix[i][j] <= 100
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
二十、旋转图像
1. 题面
48. 旋转图像
难度:中等
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。 请不要 使用另一个矩阵来旋转图像。
示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]输出:[[7,4,1],[8,5,2],[9,6,3]]示例 2:

输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]提示:
n == matrix.length == matrix[i].length1 <= n <= 20-1000 <= matrix[i][j] <= 1000
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
二十一、搜索二维矩阵 II
240. 搜索二维矩阵 II
难度:中等
编写一个高效的算法来搜索 _m_ x _n_ 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
- 每行的元素从左到右升序排列。
- 每列的元素从上到下升序排列。
示例 1:

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5输出:true示例 2:

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20输出:false提示:
m == matrix.lengthn == matrix[i].length1 <= n, m <= 300-10^9 <= matrix[i][j] <= 10^9- 每行的所有元素从左到右升序排列
- 每列的所有元素从上到下升序排列
-10^9 <= target <= 10^9
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 其实建议直接记住,另外注意输入的时候最后target的处理方式,即我们先拿到所有数据,然后再提取需要的量(用切片)
二十二、相交链表
160. 相交链表
难度:简单
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交 :
题目数据 保证 整个链式结构中不存在环。
注意 ,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal- 相交的起始节点的值。如果不存在相交节点,这一值为0listA- 第一个链表listB- 第二个链表skipA- 在listA中(从头节点开始)跳到交叉节点的节点数skipB- 在listB中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3输出:Intersected at '8'解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。示例 2:
输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1输出:Intersected at '2'解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2输出:No intersection解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。这两个链表不相交,因此返回 null 。提示:
listA中节点数目为mlistB中节点数目为n1 <= m, n <= 3 * 10^41 <= Node.val <= 10^50 <= skipA <= m0 <= skipB <= n- 如果
listA和listB没有交点,intersectVal为0 - 如果
listA和listB有交点,intersectVal == listA[skipA] == listB[skipB]
进阶: 你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 如果是正常做思路非常简单的,但是如果要用ACM模式做,就要熟练掌握怎么写这些额外函数、怎么建链表,貌似精力都放在这上面来了。这题没让输出链表,不然还要写一个print_linked_list函数。
二十三、反转链表
1.题面
206. 反转链表
难度:简单
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:

输入:head = [1,2,3,4,5]输出:[5,4,3,2,1]示例 2:

输入:head = [1,2]输出:[2,1]示例 3:
输入:head = []输出:[]提示:
- 链表中节点的数目范围是
[0, 5000] -5000 <= Node.val <= 5000
进阶: 链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题多重赋值是Python最优雅的翻转链表方案,但是这一句要注意顺序问题。左边第一个目标是 curr.next,它会先把"旧 curr 的 next"改掉,然后再更新 prev 和 curr,这个顺序才是对的。
二十四、回文链表
1. 题面
234. 回文链表
难度:简单
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:

输入:head = [1,2,2,1]输出:true示例 2:

输入:head = [1,2]输出:false提示:
- 链表中节点数目在范围
[1, 10^5]内 0 <= Node.val <= 9
进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 本题是找中点、翻转的合并。关键点在于根据fast的位置,可以判断出链表的奇偶,从而决定从什么位置开始翻转后半部分。
二十五、环形链表
1. 题面
141. 环形链表
难度:简单
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:

输入:head = [3,2,0,-4], pos = 1输出:true解释:链表中有一个环,其尾部连接到第二个节点。示例 2:

输入:head = [1,2], pos = 0输出:true解释:链表中有一个环,其尾部连接到第一个节点。示例 3:

输入:head = [1], pos = -1输出:false解释:链表中没有环。提示:
- 链表中节点的数目范围是
[0, 10^4] -10^5 <= Node.val <= 10^5pos为-1或者链表中的一个 有效索引 。
进阶: 你能用 O(1)(即,常量)内存解决此问题吗?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 注意这题的ACM模式,在建链表的时候也建一个nodes数组,这样方便我们直接看pos的位置。
二十六、环形链表 II
1. 题面
142. 环形链表 II
难度:中等
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置( 索引从 0 开始 )。如果 pos 是 -1,则在该链表中没有环。 注意:pos 不作为参数进行传递 ,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:

输入:head = [3,2,0,-4], pos = 1输出:返回索引为 1 的链表节点解释:链表中有一个环,其尾部连接到第二个节点。示例 2:

输入:head = [1,2], pos = 0输出:返回索引为 0 的链表节点解释:链表中有一个环,其尾部连接到第一个节点。示例 3:

输入:head = [1], pos = -1输出:返回 null解释:链表中没有环。提示:
- 链表中节点的数目范围在范围
[0, 10^4]内 -10^5 <= Node.val <= 10^5pos的值为-1或者链表中的一个有效索引
进阶: 你是否可以使用 O(1) 空间解决此题?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 没啥好说的,记结论
二十七、合并两个有序链表
1. 题面
21. 合并两个有序链表
难度:简单
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]输出:[1,1,2,3,4,4]示例 2:
输入:l1 = [], l2 = []输出:[]示例 3:
输入:l1 = [], l2 = [0]输出:[0]提示:
- 两个链表的节点数目范围是
[0, 50] -100 <= Node.val <= 100l1和l2均按 非递减顺序 排列
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
二十八、两数相加
1. 题面
2. 两数相加
难度:中等
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]输出:[7,0,8]解释:342 + 465 = 807.示例 2:
输入:l1 = [0], l2 = [0]输出:[0]示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]输出:[8,9,9,9,0,0,0,1]提示:
- 每个链表中的节点数在范围
[1, 100]内 0 <= Node.val <= 9- 题目数据保证列表表示的数字不含前导零
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 依旧直接背板,一定要熟练。易错点两个,一个是l1不为空才能next,另一个是carry和total都是本轮算本轮的,千万别+=
二十九、删除链表的倒数第 N 个结点
1.题面
19. 删除链表的倒数第 N 个结点
难度:中等
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:

输入:head = [1,2,3,4,5], n = 2输出:[1,2,3,5]示例 2:
输入:head = [1], n = 1输出:[]示例 3:
输入:head = [1,2], n = 1输出:[1]提示:
- 链表中结点的数目为
sz 1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
进阶: 你能尝试使用一趟扫描实现吗?
2.题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 注意点只有一个,就是删除节点要加dummy,因为头也可能被删
三十、两两交换列表中的节点
1. 题面
24. 两两交换链表中的节点
难度:中等
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:

输入:head = [1,2,3,4]输出:[2,1,4,3]示例 2:
输入:head = []输出:[]示例 3:
输入:head = [1]输出:[1]提示:
- 链表中节点的数目在范围
[0, 100]内 0 <= Node.val <= 100
2. 题解1 - 直接翻转
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - 递归处理
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 这题第一种解法在解决k个翻转的时候也很给力,递归的解法最容易理解代码最简洁,都需要掌握
三十一、k个一组翻转链表
1. 题面
25. K 个一组翻转链表
难度:困难
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例 1:

输入:head = [1,2,3,4,5], k = 2输出:[2,1,4,3,5]示例 2:

输入:head = [1,2,3,4,5], k = 3输出:[3,2,1,4,5]提示:
- 链表中的节点数目为
n 1 <= k <= n <= 50000 <= Node.val <= 1000
进阶: 你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - 递归
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 和上提一样,一个直接做,一个递归。直接做有助于思考三指针翻转后的指针都在哪,递归则是容易理解好做的思路。
三十二、随机链表的复制
1. 题面
138. 随机链表的复制
难度:中等
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝 。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。 复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示Node.val的整数。random_index:随机指针指向的节点索引(范围从0到n-1);如果不指向任何节点,则为null。
你的代码 只 接受原链表的头节点 head 作为传入参数。
示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]示例 2:

输入:head = [[1,1],[2,1]]输出:[[1,1],[2,1]]示例 3:

输入:head = [[3,null],[3,0],[3,null]]输出:[[3,null],[3,0],[3,null]]提示:
0 <= n <= 1000-10^4 <= Node.val <= 10^4Node.random为null或指向链表中的节点。
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 重点看solution部分,但是也可以看一下python的厉害。先用replace换成可以解析的字面量,然后用ast.literal_eval(…),可以直接解析,将字符串形式的嵌套列表,转化为真列表。
三十三、排序链表
1. 题面
148. 排序链表
难度:中等
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
示例 1:

输入:head = [4,2,1,3]输出:[1,2,3,4]示例 2:

输入:head = [-1,5,3,4,0]输出:[-1,0,3,4,5]示例 3:
输入:head = []输出:[]提示:
- 链表中节点的数目在范围
[0, 5 * 10^4]内 -10^5 <= Node.val <= 10^5
**进阶:**你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
2. 题解1 - 数组复制
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - 归并排序
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这一题想看的是解法二,解法一纯钻空子。注意这里有三步,首先设置递归出口,然后快慢指针寻找中点(注意这里的fast设置在head.next的起点,就可以自然让slow停在mid前面,不用借助dummy),最后递归排序两边。后面是标准的排序两个有序链表的过程。
- 然后就是力扣里面函数本身带self,记得递归的时候self.func
三十四、合并 K 个升序链表
1. 题面
23. 合并 K 个升序链表
难度:困难
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]输出:[1,1,2,3,4,4,5,6]解释:链表数组如下:[ 1->4->5, 1->3->4, 2->6]将它们合并到一个有序链表中得到。1->1->2->3->4->4->5->6示例 2:
输入:lists = []输出:[]示例 3:
输入:lists = [[]]输出:[]提示:
k == lists.length0 <= k <= 10^40 <= lists[i].length <= 500-10^4 <= lists[i][j] <= 10^4lists[i]按 升序 排列lists[i].length的总和不超过10^4
2. 题解1 - 堆排序
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - 分治
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 第一种方案是用堆自动排序,体现了堆可以存很多种不同的结果;第二种则是分治算法的体现,理解稍微复杂,实际上就是分成两边做让递归数尽可能矮。本质上也是先分两边不断递归排完left、right,然后进行两个有序链表合并的归并。
三十五、LRU缓存
1. 题面
146. LRU 缓存
难度:中等
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity)以 正整数 作为容量capacity初始化 LRU 缓存int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。void put(int key, int value)如果关键字key已经存在,则变更其数据值value;如果不存在,则向缓存中插入该组key-value。如果插入操作导致关键字数量超过capacity,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例:
输入["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"][[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]输出[null, null, null, 1, null, -1, null, -1, 3, 4]
解释LRUCache lRUCache = new LRUCache(2);lRUCache.put(1, 1); // 缓存是 {1=1}lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}lRUCache.get(1); // 返回 1lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}lRUCache.get(2); // 返回 -1 (未找到)lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}lRUCache.get(1); // 返回 -1 (未找到)lRUCache.get(3); // 返回 3lRUCache.get(4); // 返回 4提示:
1 <= capacity <= 30000 <= key <= 100000 <= value <= 10^5- 最多调用
2 * 10^5次get和put
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题代码看似复杂,实际上就是打一个双链表板子。
三十六、二叉树的中序遍历
1. 题面
94. 二叉树的中序遍历
难度:简单
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例 1:

输入:root = [1,null,2,3]输出:[1,3,2]示例 2:
输入:root = []输出:[]示例 3:
输入:root = [1]输出:[1]提示:
- 树中节点数目在范围
[0, 100]内 -100 <= Node.val <= 100
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 中序遍历不难,主要是ACM模式读取和建树。ast读取列表,replace把null换成None。
- 板子的易错点:层序建树要时刻用i监督是否超过data长度,打印层序的时候别忘了先判空再入队,然后就是空树的各种边界保护。
三十七、二叉树的最大深度
1. 题面
104. 二叉树的最大深度
难度:简单
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:

输入:root = [3,9,20,null,null,15,7]输出:3示例 2:
输入:root = [1,null,2]输出:2提示:
- 树中节点的数量在
[0, 10^4]区间内。 -100 <= Node.val <= 100
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
三十八、翻转二叉树
1. 题面
226. 翻转二叉树
难度:简单
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
示例 1:

输入:root = [4,2,7,1,3,6,9]输出:[4,7,2,9,6,3,1]示例 2:

输入:root = [2,1,3]输出:[2,3,1]示例 3:
输入:root = []输出:[]提示:
- 树中节点数目范围在
[0, 100]内 -100 <= Node.val <= 100
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 有时候会担心翻转之后dfs会不会乱了,或者需要调整成先right后left。其实不用,dfs只是为了遍历,即使左右换了,还是可以遍历,只不过遍历顺序变了。
三十九、对称二叉树
1. 题面
101. 对称二叉树
难度:简单
给你一个二叉树的根节点 root , 检查它是否轴对称。
示例 1:

输入:root = [1,2,2,3,4,4,3]输出:true示例 2:

输入:root = [1,2,2,null,3,null,3]输出:false提示:
- 树中节点数目在范围
[1, 1000]内 -100 <= Node.val <= 100
进阶: 你可以运用递归和迭代两种方法解决这个问题吗?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 第一次写懵逼,第二次写想看左侧左根右和右侧右根左是否一样,样例虽然能过,但是忽略了其实一个顺序是确定不了树的。
四十、二叉树的直径
1. 题面
543. 二叉树的直径
难度:简单
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
两节点之间路径的 长度 由它们之间边数表示。
示例 1:

输入:root = [1,2,3,4,5]输出:3解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。示例 2:
输入:root = [1,2]输出:1提示:
- 树中节点数目在范围
[1, 10^4]内 -100 <= Node.val <= 100
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题有一个dfs直接计算最大深度,可以仔细看一下写法。
- 中间比较更新一个nonlocal量是常用的递归更新全局量。
四十一、二叉树的层序遍历
1. 题面
102. 二叉树的层序遍历
难度:中等
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:

输入:root = [3,9,20,null,null,15,7]输出:[[3],[9,20],[15,7]]示例 2:
输入:root = [1]输出:[[1]]示例 3:
输入:root = []输出:[]提示:
- 树中节点数目在范围
[0, 2000]内 -1000 <= Node.val <= 1000
2. 题解1 - 队列法
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - dfs+depth
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 题解3 - IDDFS
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
5. 反思
- 这题我记得面试考过一次,不用队列BFS,所以就总结一下所有广搜的方法。
四十二、将有序数组转换为二叉搜索树
1. 题面
108. 将有序数组转换为二叉搜索树
难度:简单
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。
示例 1:

输入:nums = [-10,-3,0,5,9]输出:[0,-3,9,-10,null,5]解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:示例 2:

输入:nums = [1,3]输出:[3,1]解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。提示:
1 <= nums.length <= 10^4-10^4 <= nums[i] <= 10^4nums按 严格递增 顺序排列
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 一开始以为要写ACL树,吓死我了。这一题主要注意两点,一个是两半递归的时候,切片的位置要避开root,然后mid从长度或者从长度-1开始切都是对的,正好得到的就是示例两种答案(偏左和偏右)。
- 另一点是这题需要打印空,升级了一下print,具体写法是不在入队前检查None,在加ans时检查None。
四十三、验证二叉搜索树
1. 题面
98. 验证二叉搜索树
难度:中等
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 严格小于 当前节点的数。
- 节点的右子树只包含 严格大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:

输入:root = [2,1,3]输出:true示例 2:

输入:root = [5,1,4,null,null,3,6]输出:false解释:根节点的值是 5 ,但是右子节点的值是 4 。提示:
- 树中节点数目范围在
[1, 10^4]内 -2^31 <= Node.val <= 2^31 - 1
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题易错点是容易只考虑当前父子直接递归,BST的约束是跨越整个树的,这样会漏掉跨层约束。
- 真正的做法是一直维持上下界,往左走要更新上界(因为左边都要小),往右走更新上界,遍历一遍全部判断。
四十四、二叉搜索树中第 K 小的元素
1. 题面
230. 二叉搜索树中第 K 小的元素
难度:中等
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(k 从 1 开始计数)。
示例 1:

输入:root = [3,1,4,null,2], k = 1输出:1示例 2:

输入:root = [5,3,6,2,4,null,null,1], k = 3输出:3提示:
- 树中的节点数为
n。 1 <= k <= n <= 10^40 <= Node.val <= 10^4
进阶: 如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?
2. 题解1
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - 进阶
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 进阶方法运用了python动态绑定属性的方法,很巧妙。
四十五、二叉树的右视图
1. 题面
199. 二叉树的右视图
难度:中等
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例 1:
**输入:**root = [1,2,3,null,5,null,4]
输出: [1,3,4]
解释:

示例 2:
**输入:**root = [1,2,3,4,null,null,null,5]
输出:[1,3,4,5]
解释:

示例 3:
输入: root = [1,null,3]
输出: [1,3]
示例 4:
**输入:**root = []
输出: []
提示:
- 二叉树的节点个数的范围是
[0,100] -100 <= Node.val <= 100
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
四十六、二叉树展开为链表
1. 题面
114. 二叉树展开为链表
难度:中等
给你二叉树的根结点 root ,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode,其中right子指针指向链表中下一个结点,而左子指针始终为null。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:

输入:root = [1,2,5,3,4,null,6]输出:[1,null,2,null,3,null,4,null,5,null,6]示例 2:
输入:root = []输出:[]示例 3:
输入:root = [0]输出:[0]提示:
- 树中结点数在范围
[0, 2000]内 -100 <= Node.val <= 100
进阶: 你可以使用原地算法(O(1) 额外空间)展开这棵树吗?
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解 - 进阶
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
4. 反思
- 第二种原地结果比较难想,多看看板子。
四十七、从前序与中序遍历序列构造二叉树
1. 题面
105. 从前序与中序遍历序列构造二叉树
难度:中等
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的 先序遍历 , inorder 是同一棵树的 中序遍历 ,请构造二叉树并返回其根节点。
示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]输出: [3,9,20,null,null,15,7]示例 2:
输入: preorder = [-1], inorder = [-1]输出: [-1]提示:
1 <= preorder.length <= 3000inorder.length == preorder.length-3000 <= preorder[i], inorder[i] <= 3000preorder和inorder均 无重复 元素inorder均出现在preorderpreorder保证 为二叉树的前序遍历序列inorder保证 为二叉树的中序遍历序列
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题的难点在切片的位置判断,需要多练习。
四十八、路径总和 III
1. 题面
437. 路径总和 III
难度:中等
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例 1:

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8输出:3解释:和等于 8 的路径有 3 条,如图所示。示例 2:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22输出:3提示:
- 二叉树的节点个数的范围是
[0,1000] -10^9 <= Node.val <= 10^9-1000 <= targetSum <= 1000
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题好难,得用两个递归。首先是找从这个节点开始,递归找有没有路径。然后递归每个节点加上当前和左右的路径和。能自己写出这题的递归已经对树的递归非常熟练了。
四十九、二叉树的最近公共祖先
1. 题面
236. 二叉树的最近公共祖先
难度:中等
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:"对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大( 一个节点也可以是它自己的祖先 )。"
示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1输出:3解释:节点 5 和节点 1 的最近公共祖先是节点 3 。示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4输出:5解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。示例 3:
输入:root = [1,2], p = 1, q = 2输出:1提示:
- 树中节点数目在范围
[2, 10^5]内。 -10^9 <= Node.val <= 10^9- 所有
Node.val互不相同。 p != qp和q均存在于给定的二叉树中。
2. 题解 - parent属性
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 题解2 - 递归
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这题第二种LCA算法是最优雅的解法,请记住板子。
五十、二叉树中的最大路径和
1. 题面
124. 二叉树中的最大路径和
难度:困难
二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
示例 1:

输入:root = [1,2,3]输出:6解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6示例 2:

输入:root = [-10,9,20,null,null,15,7]输出:42解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42提示:
- 树中节点数目范围是
[1, 3 * 10^4] -1000 <= Node.val <= 1000
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 我草,好难,暂时不看,回头理解背板
五十一、岛屿数量
1. 题面
200. 岛屿数量
难度:中等
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [ ['1','1','1','1','0'], ['1','1','0','1','0'], ['1','1','0','0','0'], ['0','0','0','0','0']]输出:1示例 2:
输入:grid = [ ['1','1','0','0','0'], ['1','1','0','0','0'], ['0','0','1','0','0'], ['0','0','0','1','1']]输出:3提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 300grid[i][j]的值为'0'或'1'
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- dfs在图里的经典用法
五十二、腐烂的橘子
1. 题面
994. 腐烂的橘子
难度:1433
在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:
- 值
0代表空单元格; - 值
1代表新鲜橘子; - 值
2代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。
示例 1:

输入:grid = [[2,1,1],[1,1,0],[0,1,1]]输出:4示例 2:
输入:grid = [[2,1,1],[0,1,1],[1,0,1]]输出:-1解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个方向上。示例 3:
输入:grid = [[0,2]]输出:0解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 10grid[i][j]仅为0、1或2
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 这种题看起来思路简单,但是比较考验你的写法和边界条件,建议平时多敲练习。
五十三、课程表
1. 题面
207. 课程表
难度:中等
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
- 例如,先修课程对
[0, 1]表示:想要学习课程0,你需要先完成课程1。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]输出:true解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]输出:false解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。提示:
1 <= numCourses <= 20000 <= prerequisites.length <= 5000prerequisites[i].length == 20 <= ai, bi < numCoursesprerequisites[i]中的所有课程对 互不相同
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 拓扑排序给有向图判环是经典算法。
五十四、实现 Trie (前缀树)
1. 题面
208. 实现 Trie (前缀树)
难度:中等
Trie (发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie()初始化前缀树对象。void insert(String word)向前缀树中插入字符串word。boolean search(String word)如果字符串word在前缀树中,返回true(即,在检索之前已经插入);否则,返回false。boolean startsWith(String prefix)如果之前已经插入的字符串word的前缀之一为prefix,返回true;否则,返回false。
示例:
输入["Trie", "insert", "search", "search", "startsWith", "insert", "search"][[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]输出[null, null, true, false, true, null, true]
解释Trie trie = new Trie();trie.insert("apple");trie.search("apple"); // 返回 Truetrie.search("app"); // 返回 Falsetrie.startsWith("app"); // 返回 Truetrie.insert("app");trie.search("app"); // 返回 True提示:
1 <= word.length, prefix.length <= 2000word和prefix仅由小写英文字母组成insert、search和startsWith调用次数 总计 不超过3 * 10^4次
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
3. 反思
- 记住思路了背板很简单
五十五、全排列
1. 题面
46. 全排列
难度:中等
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2:
输入:nums = [0,1]输出:[[0,1],[1,0]]示例 3:
输入:nums = [1]输出:[[1]]提示:
1 <= nums.length <= 6-10 <= nums[i] <= 10nums中的所有整数 互不相同
2. 题解
Frozen Editor
默认折叠,点击展开查看完整代码
展开代码
部分信息可能已经过时



