给定一个数组和一个值 val,原地移除数组中所有等于 val 的元素,返回剩下的元素个数 k;前 k 个位置放剩下的元素,顺序可以任意,后面的内容不重要。这是双指针的入门题:一个指针 `i` 扫描,一个指针 `k` 指向下一个要写入的位置。本文除了给出五种语言的实现,还比较了另一
在字符串 `haystack` 里找到字符串 `needle` 第一次出现的起点下标,找不到返回 -1。最直接的做法是对每个起点逐字符比较,最坏是 O(n·m);KMP 算法在失配时不回退 `haystack` 的指针,只依据 `needle` 自身的前缀结构把匹配长度退回去,总
给定两个 32 位有符号整数,不使用乘法、除法和取模运算,计算它们相除的商,结果向零取整(截断小数部分)。如果商超过 32 位有符号整数的范围,就返回边界值。直接的想法是一次减一个除数,数商的个数,但商可能大到 21 亿,太慢。标准做法是把"减一次除数"升级成"减去除数的 2 的
给定一个字符串 s 和一组长度相同的单词,找出 s 中所有的子串起点:从该起点开始的一段,恰好是这组单词以任意顺序拼接起来的结果(每个单词恰好用一次)。暴力做法是对每个起点,把接下来的一段切成单词,和给定的单词组比较,时间约 O(n · m · w)(m 是单词个数,w 是单词长
给定一个整数数组,把它原地改成字典序里的下一个更大的排列;如果已经是最大的排列,就改成最小的(升序)。做法是四步:从右往左找第一个升序对 `nums[i] < nums[i+1]`;再从右往左找第一个比 `nums[i]` 大的数 `nums[j]`;交换二者;最后把 `i` 之
给定一个只包含 `(` 和 `)` 的字符串,找出最长的有效(格式正确且连续)括号子串的长度。做法有好几种:栈、动态规划、正反两次计数。这道题容易写错的地方是:有效的括号段之间可能被没有配上的括号隔开,也可能几段相邻拼成更长的一段,所以不能只统计配对了多少对。本文用真实数据展示"
一个升序、元素互不相同的数组,在某个位置被旋转了一下(例如 `[0,1,2,4,5,6,7]` 变成 `[4,5,6,7,0,1,2]`)。给定一个目标值,在 O(log n) 时间内找到它的下标,找不到返回 -1。关键观察:把旋转数组从中间劈开,左右两半里至少有一半是有序的。每
给定一个升序数组和一个目标值,返回目标值出现的第一个和最后一个下标;不存在返回 `[-1, -1]`。要求 O(log n)。这道题的关键是别用"二分找到任意一个,再线性向两边扩展":整个数组都是目标值时,扩展会退化成 O(n)。正确做法是做两次二分,分别找"第一个大于等于 ta
给定一个升序、元素互不相同的数组和一个目标值:如果目标在数组里就返回它的下标,否则返回它按顺序应该插入的位置。换个说法,就是"第一个大于等于 target 的位置",都比 target 小就是数组长度。这是二分查找最标准的形态,也是后面所有二分变体的基础:左闭右开区间 `[lo,
给定一个 9×9 的数独棋盘,只需要判断已经填入的数字是否有效:每一行、每一列、每一个 3×3 的宫里,数字 1 到 9 都不能重复。棋盘不一定能被解出来,只检查已填的部分有没有冲突。做法是扫描一遍,用三组位掩码分别记录每行、每列、每个宫里出现过的数字,遇到冲突就返回。本文用真实
给定一个部分填好的 9×9 数独,把它填完。数独的规则是每行、每列、每个 3×3 宫里数字 1 到 9 各出现一次。做法是回溯:找一个空格,试填一个合法的数字,递归地去填后面的空格,走不通就撤销、换下一个数字。朴素的回溯按固定顺序填空格,能做但很慢;关键的加速是每一步先挑「候选数
外观数列的第一项是 `1`,之后每一项是对前一项的"读法":把前一项里连续相同的数字分成一组,读作"个数加数字"。`1` 读作"一个 1",所以第二项是 `11`;`11` 读作"两个 1",第三项是 `21`;`21` 读作"一个 2,一个 1",第四项是 `1211`。给定