大概是非会员的前 300 道题,重复刷了好几遍,目前整理出其中第一眼看过去容易没有思路,或者做的过程中容易错的题,希望能帮到你。
其中股票、九皇后这种理解了之后就没什么可讨论的题就不列举了。
-
无重复字符的最长子串 (窗口头一次只能删一个)
-
寻找两个正序数组的中位数 (每次截一半)
-
两数相除(最恶心的 int 边界问题,一种解法是转为 long,比较省事,另外一种是全转成负数,这种 case 需要考虑特别多的边界情况,left = 1, right = MAX,然后二分)
-
下一个排列 (主要是小数和大数怎么找以及找完记得 reverse 一下)
-
最长有效括号(dp选择为以 i 结尾最长的,同时对arr[i - 1 - dp[i - 1]]做判断)
-
插入区间 (比较恶心的点是在循环过程中需要判断是否add,未add则需要比较插入区间的 index 0 和 当前打算插入的区间的 index 0 谁小,小的先插入)
-
排列序列 (开局记得 k - 1,然后需要一个辅助数组 aux[0] = 1, aux[i] = aux[i - 1] * i)
-
x 的平方根 (通过这种方式判断 x / mid == mid)
-
最小覆盖子串(这道题和滑动窗口类似,用个map 和 count 来判断窗口是否满足要求)
-
柱状图中最大的矩形 (先往deque 里加个 -1 当左边界,最后加个 -1 把 deque 清空)
-
扰乱字符串 (三维数组的dp,最后一维为 len + 1, boolean[][][] dp = new boolean[len][len][len + 1]; dp[i][j][b] && dp[i + b][j + b][k - b] || dp[i][j + k - b][b] && dp[i + b][j][k - b])
-
格雷编码 (循环 n 次,子循环为 从后往前遍历遍历到的 + 1 << 第 i 次循环位)
-
二叉树的锯齿形层序遍历 (只用一个 deque 很麻烦,每次循环都用一个 auxDeque,循环结束把 deque 指向它就行)
-
从前序与中序遍历序列构造二叉树 (用一个 map 快速定位中序遍历中和前序遍历对应的位置)
-
二叉树的层序遍历,自底向上 (使用 dfs 中序遍历 用 一个空 list 做 res,当 depth > res.size 的时候,list 里 add 一个 list,因为中序遍历,所以每次遍历到的顺序就是从左到右)
-
将有序数组转换为二叉搜索树 (二分构建,左边是左子树,右边是右子树)
-
有序链表转换二叉搜索树 (使用中序遍历)
-
平衡二叉树 (用 -1 来做标志当前已经不满足平衡二叉树了,否则返回 左右子树最大高度 + 1)
-
二叉树的最小深度 (层序遍历,当前循环的某个 node left right 都为 null 的时候 return)
-
二叉树展开为链表 (先序遍历,但是需要是 右 - 中 - 左,避免右节点丢失)
-
不同的子序列 (if (c1 == c2) dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];)
-
杨辉三角 (每轮循环 add 一个 1,从后往前遍历)
-
二叉树中的最大路径和 (dfs, 当前节点的值 = Max (left, right) + cur)
-
单词接龙 (深度遍历,没什么说的,很考察基本功的一道题 beginSet,endSet,checkSet 用好即可)
-
最长连续序列(while(set.contains(num + 1)))
-
分割回文串 (先用 dp 构建 ,然后深度搜索)
-
分割回文串求最小分割次数 (先用 dp 构建 ,再用 dp 找结果,找结果的 dp 状态转移方程为 if (auxDp[j][i])dp[i] = Math.min(value, dp[j - 1] + 1) )
-
分发糖果 (incLen, descLen, prevIncLen, 然后比较 arr[i] 和 arr[i - 1] 的值)
-
单词拆分 (如果只问能不能拼接出来字符串那么用 dp,需要结果的话用 dfs)
-
重排链表 (L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …, 快慢指针,然后 reverse slow 后边的链表)
-
轮转数组 (用 do while 循环写,同时循环过程中维护一个 count,交换够次数 break)
-
数字范围按位与 ([left, right] ,返回此区间内所有数字 按位与 的结果,无限删去末尾 1 直到 right <= left)
-
快乐数 (如果用 set 的话不够优雅,快慢指针才是最牛逼的方案。。。)
-
计数质数(判断是不是质数,int i = 2 ; i * i <= x; i++ ,看 x / 2 是不是 = 0,然后可以遍历解决,但是遍历太慢了,我们可以用一个辅助数组 aux[n] , 最初认为所有数都可能是质数,遍历到 i 的时候,如果 i 是质数,那么大于 i 的整倍数 2i,3i, 一定不是质数 , 可以从 i * i 开始标记,因为 2i 在 2 的时候已经标记过了)
-
课程表 (根据 prerequisite 构建一个 map 以及一个 aux 数组来判断每个课学习之前需要学的别的课的总数,然后把 aux 数组中为 0 的课加到 deque 里,不断遍历,当 aux 对应的课为 0 的 时候也可以加到 deque 里,如此不断循环,知道 学习的课够了,如果要判断要学的课)
-
长度最小的子数组 (满足其总和大于等于 target 的长度最小的 连续子数组,用滑动窗口有一点非常重要,就是 total > target 的时候左端点有可能不是真正的左端点,还可以继续删)
-
最短回文串 (这里先构建辅助 dp,然后找 i = 0的时候最长的回文串,截取剩下的翻转一下然后添加到头部就可以了)
-
数组中的第K个最大元素(用快排的思路找)
-
存在重复元素 (abs(i - j) <= indexDiff,abs(nums[i] - nums[j]) <= valueDiff,主要是根据 valueDiff 建一个桶,id == nums[i] / 桶 size,负数记得 + 1,然后看桶里有没有值或者 id - 1 和 id + 1 的桶有没有值能对应上,超过 indexSize 的时候移除对应桶)
-
最大正方形( dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1 ; )
-
完全二叉树的节点个数(通过遍历求解是最 low 的方法,可以利用完全二叉树的特性,计算左右子树的高度,相等的时候说明左树必满 countNodes(root.right) + (1 << left), 不相等的时候说明右数必满)
-
矩形面积 (难点就是计算重叠区域面积,int w = Math.min(ax2, bx2) - Math.max(ax1, bx1) ; int h = Math.min(ay2, by2) - Math.max(ay1, by1); )
-
基本计算器 (负数的计算是通过加一个 0 然后正常算,当准备入栈 )的时候需要把栈内( 之前的全弹出来,涉及到优先级运算的时候加一个 map 过来,判断 sign 栈内符号的优先级高的时候需要先算下栈内然后在 offerLast)
-
多数元素 II (给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素,核心是选择两个候选数)
-
二叉搜索树的最近公共祖先 (同样要利用二叉搜索树的特性,当前 node.val < p.val && node.val < q.val,则目标在右子树等)
-
二叉树的最近公共祖先 (上一题的升级版,dfs 找 node == p || node == q,任意相等则返回 node,否则返回 null,当一个 node 的 left 和 right 都不是空的时候就是目标点了,任一不为空返回对应的不为空 node)
-
除自身以外数组的乘积 (维护两个数组,一个是左边数的乘积一个是右边数的乘积)
-
滑动窗口最大值 (用双向队列来维护,每次有大值的时候就把双向队列内比大值小的全 pop 出去, 每划一步判断队列头部的 index 用不用清除)
-
只出现一次的数字 III (全部异或完然后用 int v2 = (v1 == Integer.MIN_VALUE) ? v1 : (v1 & (-v1)) ; 取出最低位的 1)
-
丑数 II (丑数 就是质因子只包含 2、3 和 5 的正整数, 给你一个整数 n ,请你找出并返回第 n 个 丑数 ,用一个 dp 存结果,然后三个初始指针 index 都是 0,分别判断对应的 dp[i] * 2, *3, *5 的值是不是比较小,最小的指针 index + 1)
-
H 指数 (升序排列,然后从后往前遍历直到遍历的值小于当前已遍历长度停止,275 也类似就是加个
-
完全平方数 (for (int j = 1; j * j <= i; j++) min = Math.min(min, dp[i - j * j]); dp[i] = min + 1)