最近又开始回顾LeetCode算法题了,以前写在LeetCode各道题上,这里打算在博客上体系化汇总一下。
一、动态规划(DP):反复求解局部,以求解整体。
先思考第1个和第2个的结果,再思考第3个和前2个的关系,来得到第3个,依次往复。
如果只需保存有限个结果,可以用轮动数组(或者称滚动数组)减小空间复杂度。
爬楼梯:每次爬1阶或2阶,求到达n阶的方法个数。
第1阶有1种方法。第2阶有2种方法。第3阶有1+2种方法。
结果符合斐波那契数列(1,2,3,5…),可以用动态规划(DP)。
可以用轮动数组,只记录当前涉及的3个数。
斐波那契数:
0,1,1,2,3…。用动态规划。可以用轮动数组。
打家劫舍:不能打劫相邻的两家,求能打劫到的最大金额。
只有1家,金额确定。有2家,选择最大金额的一家。
2家以上,如果打劫倒数第一家,就是倒数第1家的金额+轮到倒数第3家时能打劫到的最大金额,和轮到倒数第2家时能打劫到的最大金额,中的最大金额。
不同路径:每次向下或向右一格,求从左上格到达右下格的方法数。
第1行的每1格每一格只有1种方法到达。第1列的每1格只有1种方法到达。
到达第m行第n列的方法数是到达第m-1行第n列的方法数+到达第m行第n-1列的方法数,也就是到达上面格子的方法数+到达左面格子的方法数。
不同路径 II:格子上有障碍物。
第1行上,从第一个开始,有障碍物就不再向后遍历,方法数是0,否则就是1,继续向后遍历。第1列也是。
不是第1行,不是第1列,有障碍物,方法数是0,不是障碍物,方法数是到达上面格子的方法数+到达左面格子的方法数。
最长公共子序列:序列相同即可,不一定在字符串中相邻。
2个字符串只有一个字符,如果字符相同,最长公共子序列的长度是1,否则是0。
2个字符串一个是1个字符,1个多于1个字符,多于1个字符的字符串的第2个字符开始,每个字符依次和另一个字符串的唯一字符对比,相同就是1,不同就是之前的最长公共子序列长度。
2个字符串都多于1个字符,都从第2个字符开始,对比字符是否相同,相同就在之前都减少1个字符的最长公共子序列长度基础上+1,不同就是各自-1字符的情况下取最大值。
分割回文串 II:求将字符串分割成回文的最少分割次数。
设置默认值,也是悲观值,n个字符分割n-1次。
从2个字符开始求最少分割次数。每个字符串在不拆分的情况下如果是回文,结果是0;如果分割成最后一个和之前的部分,结果是之前部分的结果+1和整体已有结果的最小值;
如果拆成其它情况,判断后一部分是否是回文,是回文,结果就是前一部分的结果+1和整体已有结果的最小值。
使用对撞指针判断是否是回文,在左侧指针索引小于右侧指针索引的前提下,对比两个指针位置的值是否相同,相同就将左右指针向中间移动一位,继续对比值是否相同,全部相同就是回文。
二、图:顶点和边构成,分有向图和无向图。有根的无闭环部分的无向连通图是树。
深度优先搜索是递归方式,深入到底后回退,深入到下一个底。
广度优先搜索是循环方式,搜索当前层级后,搜索下一个层级。
岛屿数量:‘0’、'1’组成的二维数组中找到独立的’1’构成的块。
遍历二维数组,每次遇到’1’就向上下左右四个方向深度优先搜索或者广度优先搜索,将’1’变成’0’,这样就不会去影响找出下一个岛屿。
全排列:
深度优先搜索。
克隆图:深克隆无向连通图。
Map暂存已经克隆的节点,防止多次克隆。
子集:求集合的所有子集。
动态规划(在已有集合基础上加上新的元素)或者二进制方式(每个元素分存在与不存在,遍历2^n种可能)。或者深度优先搜索或者广度优先搜索二叉树。
三、字符串
翻转字符串里的单词:字符串中的单词整体翻转,不保留字符串前后空格。
字符串trim后用空格分割,然后用空格倒序拼接。
反转字符串中的单词 III:
翻转字符串中的每个单词。
整数转罗马数字:
按照个十百千的硬编码方式转换,或者逐步递减,用贪心算法,每一步能得到最终解的一部分。
四、散列
和为K的子数组:连续的子数组的和是K,求子数组的数量。(子数组是指内部连续元素构成的数组。)
前缀和 + 一个和为k的子数组 = 前缀和‘
两层循环,确定起点和终点,枚举所有的可能。
或者,一层循环,确定终点,前缀和的差如果是K,两个前缀和数组的差异部分就是和为K的连续子数组;将前面的前缀和以及出现次数存储下来,当前的前缀和减去K,所得值在前面的前缀和中出现了几次,就有几个和是K的连续子数组。
无重复字符的最长子串:求无重复字符的最长子串长度。
求每个字符开始的无重复字符的最长子串,存储最大长度。使用滑动窗口,复用已有成果。