手机九宫格键盘上,数字 2 到 9 各对应几个字母(2 对应 abc,7 对应 pqrs,依此类推)。给定一个由 2 到 9 组成的数字串,返回它能表示的所有字母组合,顺序不限。这是回溯的入门题:每一位从它对应的字母里选一个,选完一位递归到下一位,走到底就收集一个结果,返回时撤销
给定一个整数数组和目标值 target,找出所有和为 target 的不重复四元组。做法是三数之和的延伸:排序后用两层循环固定前两个数,再在右侧用双指针找后两个数,时间 O(n³)。这道题相比三数之和多了一个真实的陷阱:数组元素和 target 的范围都到 10 亿,四个数相加可
给定一个链表的头结点和整数 n,删除链表的倒数第 n 个结点,返回新的头结点。倒数第 n 个不能直接定位,最直接的想法是先数出链表长度 L,再走 L - n 步找到它的前一个结点。更常见的写法是快慢指针:让 fast 先走 n 步,然后 fast 和 slow 一起走,fast
给定一个只包含 `()[]{}` 六种字符的字符串,判断括号是否有效:左括号要用同类型的右括号闭合,并且要以正确的顺序闭合。最后一条"顺序"是这道题的核心:最近打开的括号必须最先被关闭,这正是栈的后进先出。本文用真实数据展示"只数每种括号数量"这个常见的错误想法为什么不行,实测栈
把两个升序链表合并成一个升序链表,新链表由原来两个链表的结点拼接而成。做法和归并排序的合并步骤一样:两个指针各指向一条链表的当前结点,每次取较小的接到结果后面,直到一条用完,再把另一条剩下的整体接上。本文用真实数据验证一个常见的说法:递归写法更简洁,但它在 Python 里会撞上
给定 n 对括号,生成所有合法的括号组合。合法的意思是:每个右括号都能和前面某个未配对的左括号匹配。暴力做法是枚举全部 2^(2n) 个由 `(` 和 `)` 组成的串,再逐个检查;更好的做法是回溯,在生成的过程中只走合法的分支:左括号还没用满 n 个就可以放,右括号只有在"已放
给定 k 个升序链表,把它们合并成一个升序链表。最直接的想法是依次把每条链表合并到结果里,但结果越来越长,前面的结点会被反复比较。常见的更好做法有两种:用最小堆每次取出 k 个链表头里最小的;或者分治,两两合并再逐轮合并。本文选分治,因为它只需要上一题的"合并两个链表",五种语言
给定一个链表,把相邻的两个结点两两交换,返回新的头结点。要求真的交换结点,不能只交换结点里的值。链表长度是奇数时,最后一个结点保持原位。这道题没有算法上的技巧,全部难点在指针操作:每交换一对要改三个指针,顺序错了链表就断了。本文用真实的错误版本展示漏掉一步指针会得到什么,给出五种
给定一个链表,每 k 个结点一组进行翻转,返回新的头结点。如果最后剩下的结点不足 k 个,保持原样。它是反转链表的扩展:把链表切成若干段,每段单独反转再接回去。难点全在"接回去":每一组翻转之后,头变成尾、尾变成头,要正确连接前一组的尾和后一组的头。本文用真实数据展示"最后不足
给定一个升序数组,原地删除重复元素,使每个值只出现一次,返回去重之后的长度 k;数组前 k 个位置放去重后的元素,后面的内容不重要。核心是双指针:一个指针 `i` 扫描整个数组,一个指针 `k` 记录去重部分的末尾。本文实测原地写入的双指针与"遇到重复就 `del` 掉"的做法的
给定一个数组和一个值 val,原地移除数组中所有等于 val 的元素,返回剩下的元素个数 k;前 k 个位置放剩下的元素,顺序可以任意,后面的内容不重要。这是双指针的入门题:一个指针 `i` 扫描,一个指针 `k` 指向下一个要写入的位置。本文除了给出五种语言的实现,还比较了另一
在字符串 `haystack` 里找到字符串 `needle` 第一次出现的起点下标,找不到返回 -1。最直接的做法是对每个起点逐字符比较,最坏是 O(n·m);KMP 算法在失配时不回退 `haystack` 的指针,只依据 `needle` 自身的前缀结构把匹配长度退回去,总