树
递归
1. 树的高度
2. (判断是否)平衡树
3. 两节点的最长路径
4. 翻转树
|
|
5. 归并两棵树
|
|
6. 判断路径和是否等于一个数
|
|
7. 统计路径和等于一个数的路径数量
路径不一定以 root 开头,也不一定以 leaf 结尾,但是必须连续。
思路:在函数里定义一个withRoot的函数,在withRoot函数体里也进行递归调用
8. 子树
思路:在函数里定义一个withRoot的函数,在withRoot函数体里也进行递归调用
9. 树的对称
|
|
有点像8
10. 最小路径
树的根节点到叶子节点的最小路径长度
思路:注意和求树的高度不同
|
|
11. 统计左叶子节点的和
|
|
12. 相同节点值的最大路径长度
给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。
注意:两个节点之间的路径长度由它们之间的边数表示。
5
/ \
4 5
/ \ \
1 1 5 输出2
1
/ \
4 5
/ \ \
4 4 5 输出2
不好想。。
13. 间隔遍历
如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
|
|
14. 找出二叉树中第二小的节点
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。
给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。
|
|
层次遍历
1. 一棵树每层节点的平均数
2. 得到左下角的节点
|
|
思路:层次遍历进队时先进右节点。
前中后序遍历
1. 非递归实现二叉树的前序遍历
写法有点像层次遍历的写法,只不过用的是栈
2. 非递归实现二叉树的后序遍历
在非递归前序遍历基础上改造:
前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。
3. 非递归实现二叉树的中序遍历
思路:定义一个要伸到最左边的指针和一个栈,2层while循环。步骤:先伸到最左边,然后出栈入栈。
二叉查找树(BST)
1. 修剪二叉查找树
给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。
|
|
思路:递归
2. 寻找二叉查找树的第 k 个元素
思路:1中序遍历(不使用额外空间,中序递归遍历的过程中就去找k)2递归解法()
3. 把二叉查找树每个节点的值都加上比它大的节点的值
给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
例如:
输入: 二叉搜索树:
5
/ \
2 13
输出: 转换为累加树:
18
/ \
20 13
思路:递归。先遍历右子树。
4. 二叉查找树的最近公共祖先(LCA)
|
|
|
|
5. 二叉树的最近公共祖先(LCA)
|
|
6. 从有序数组中构造二叉查找树
思路:递归,构造过程类似二分查找。数组索引中点即为根节点。
7. 根据有序链表构造平衡的二叉查找树
思路:递归,同上。找链表的中点需要定义一组快慢指针。
8. 在二叉查找树中寻找两个节点,使它们的和为一个给定值
653. Two Sum IV - Input is a BST (Easy)
|
|
使用中序遍历得到有序数组之后,再利用双指针对数组进行查找。
9. 在二叉查找树中查找两个节点之差的最小绝对值
|
|
利用二叉查找树的中序遍历为有序的性质,计算中序遍历中临近的两个节点之差的绝对值,取最小值。(在中序遍历递归过程中就进行最小值的比较)
10. 寻找二叉查找树中出现次数最多的值
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
假定 BST 有如下定义:
结点左子树中所含结点的值小于等于当前结点的值
结点右子树中所含结点的值大于等于当前结点的值
左子树和右子树都是二叉搜索树
提示:如果众数超过1个,不需考虑输出顺序
进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)
|
|
思路:中序遍历
trie todo
1树的高度2平衡树3两节点的最长路径 在递归过程中都要算树的高度1 + Math.max(l, r);注意带根节点和不带根节点(6,7,8);注意递归函数的参数是左右节点即2个参数时的递归出口条件
if (t1 == null && t2 == null) return true;if (t1 == null || t2 == null) return false;
(8,9)层次遍历写法;前中后序遍历非递归写法。
中序遍历在BST中的应用(2,8,9,10);有序数组(链表)构造平衡二叉搜索树(6,7);LCA(4,5)
分治
1. 给表达式加括号
给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含 +, - 以及 * 。
示例 1:
输入: “2-1-1”
输出: [0, 2]
解释:
((2-1)-1) = 0
(2-(1-1)) = 2
思路:分治,递归。遍历运算符,以运算符左右2边的可能性做组合。
2. 不同的二叉搜索树
给定一个数字 n,要求生成所有值为 1…n 的二叉搜索树。
|
|
解法二 递归
解法一完全没有用到查找二叉树的性质,暴力尝试了所有可能从而造成了重复。我们可以利用一下查找二叉树的性质。左子树的所有值小于根节点,右子树的所有值大于根节点。
所以如果求 1…n 的所有可能。
我们只需要把 1 作为根节点,[ ] 空作为左子树,[ 2 … n ] 的所有可能作为右子树。
2 作为根节点,[ 1 ] 作为左子树,[ 3…n ] 的所有可能作为右子树。
3 作为根节点,[ 1 2 ] 的所有可能作为左子树,[ 4 … n ] 的所有可能作为右子树,然后左子树和右子树两两组合。
4 作为根节点,[ 1 2 3 ] 的所有可能作为左子树,[ 5 … n ] 的所有可能作为右子树,然后左子树和右子树两两组合。
…
n 作为根节点,[ 1… n ] 的所有可能作为左子树,[ ] 作为右子树。
至于,[ 2 … n ] 的所有可能以及 [ 4 … n ] 以及其他情况的所有可能,可以利用上边的方法,把每个数字作为根节点,然后把所有可能的左子树和右子树组合起来即可。
如果只有一个数字,那么所有可能就是一种情况,把该数字作为一棵树。而如果是 [ ],那就返回 null。
|
|
动态规划
斐波那契数列
爬楼梯,抢劫,环形抢劫(将首尾节点各自切掉之后得到2种情况后比大小)
4. 信件错排
题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。
定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况:1:i==k,i!=k;
dp[i] = (i-1)dp[i-2] + (i-1)dp[i-1]
母牛生产
dp[ i ]=dp[i-1]+dp[i-3]
矩阵路径
1. 矩阵的最小路径和2. 矩阵的总路径数
题目描述:求从矩阵的左上角到右下角的最小路径和,每次只能向右和向下移动。
2道题不用额外空间的解法没看
数组区间
1. 数组区间和
|
|
求区间 i ~ j 的和,可以转换为 sum[j + 1] - sum[i],其中 sum[i] 为 0 ~ i - 1 的和。
2. 数组中等差递增子区间的个数
|
|
dp[i] 表示以 A[i] 为结尾的等差递增子区间的个数。
当 A[i] - A[i-1] == A[i-1] - A[i-2],那么 [A[i-2], A[i-1], A[i]] 构成一个等差递增子区间。而且在以 A[i-1] 为结尾的递增子区间的后面再加上一个 A[i],一样可以构成新的递增子区间。
|
|
综上,在 A[i] - A[i-1] == A[i-1] - A[i-2] 时,dp[i] = dp[i-1] + 1。
因为递增子区间不一定以最后一个元素为结尾,可以是任意一个元素结尾,因此需要返回 dp 数组累加的结果。
cht注:抄的,当时不会做。。。。。
分割整数
1. 分割整数的最大乘积
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
题目描述:For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).
思路:2层for循环,注意如果不拆分也有可能比拆分的乘积大。
2. 按平方数来分割整数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...
)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
题目描述:For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
思路:我觉得我的解法更不错。
3. 分割整数构成字母字符串
一条包含字母 A-Z 的消息通过以下方式进行了编码:
‘A’ -> 1
‘B’ -> 2
…
‘Z’ -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1:
输入: “12”
输出: 2
解释: 它可以解码为 “AB”(1 2)或者 “L”(12)。
示例 2:
输入: “226”
输出: 3
解释: 它可以解码为 “BZ” (2 26), “VF” (22 6), 或者 “BBF” (2 2 6) 。
抄的,当时不会做。
思路:dp[i]+=dp[i-1] ;如果当前数字与前一位组合的数字处于10-26时,再加dp[i-2]
最长递增子序列
1. 最长递增子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。
思路:两层for循环,第二层循环里面遍历找到所有比当前数小的数,做memo[i]的重新赋值(和最大值比较再赋值)
2. 一组整数对能够构成的最长链
给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。
现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。
给定一个对数集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
示例 :
输入: [[1,2], [2,3], [3,4]]
输出: 2
解释: 最长的数对链是 [1,2] -> [3,4]
思路:先排序,然后和上面一题一样。
3. 最长摆动子序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
思路:定义一个up和一个down,GitHub解法o(n);【牛逼的】
最长公共子序列
思路:对于两个子序列 S1 和 S2,找出它们最长的公共子序列。
定义一个二维数组 dp 用来存储最长公共子序列的长度,其中 dp[i][j] 表示 S1 的前 i 个字符与 S2 的前 j 个字符最长公共子序列的长度。考虑 S1i 与 S2j 值是否相等,分为两种情况:
- 当 S1i==S2j 时,那么就能在 S1 的前 i-1 个字符与 S2 的前 j-1 个字符最长公共子序列的基础上再加上 S1i 这个值,最长公共子序列长度加 1,即 dp[i][j] = dp[i-1][j-1] + 1。
- 当 S1i != S2j 时,此时最长公共子序列为 S1 的前 i-1 个字符和 S2 的前 j 个字符最长公共子序列,或者 S1 的前 i 个字符和 S2 的前 j-1 个字符最长公共子序列,取它们的最大者,即 dp[i][j] = max{ dp[i-1][j], dp[i][j-1] }。
综上,最长公共子序列的状态转移方程为:
与最长递增子序列不同的是,而最长递增子序列中 dp[N] 不是最终解,因为以 Sn 为结尾的最长递增子序列不一定是整个序列最长递增子序列,需要遍历一遍 dp 数组找到最大者。
0-1 背包
|
|
1. 划分数组为和相等的两部分
可以看成一个背包大小为 sum/2 的 0-1 背包问题。只不过这里不要求求出最大的value,而是看能不能等于一个指定的value,因此函数返回boolean
部分函数体:
|
|
2. 改变一组数的正负号使得它们的和为一给定数
|
|
该问题可以转换为 Subset Sum 问题,从而使用 0-1 背包的方法来求解。
可以将这组数看成两部分,P 和 N,其中 P 使用正号,N 使用负号,有以下推导:
|
|
因此只要找到一个子集,令它们都取正号,并且和等于 (target + sum(nums))/2,就证明存在解。
思路:1dp2dfs
3. 01 字符构成最多的字符串
在计算机界中,我们总是追求用有限的资源获取最大的收益。
现在,假设你分别支配着 m 个 0 和 n 个 1。另外,还有一个仅包含 0 和 1 字符串的数组。
你的任务是使用给定的 m 个 0 和 n 个 1 ,找到能拼出存在于数组中的字符串的最大数量。每个 0 和 1 至多被使用一次。
注意:
给定 0 和 1 的数量都不会超过 100。
给定字符串数组的长度不会超过 600。
|
|
这是一个多维费用的 0-1 背包问题,有两个背包大小,0 的数量和 1 的数量
不看了,自己玩吧
4. 找零钱的最少硬币数
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
|
|
和背包没啥关系吧 直接用dp,徒手想。
5. 找零钱的硬币数组合
|
|
|
|
也不必刻意想成他所说的背包基础上的问题
6. 字符串按单词列表分割
todo
7. 组合总和
给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
todo
股票交易
所有状态
|
|
状态转移框架
|
|
初始值
|
|
总结
|
|
字符串编辑
1. 删除两个字符串的字符使它们相等
给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
|
|
思路:可以转换为求两个字符串的最长公共子序列问题。
斐波拉切数列
爬楼梯,抢劫,环形抢劫(将首尾节点各自切掉之后得到2种情况后比大小)
数组区间
数组中等差递增子区间的个数:在 A[i] - A[i-1] == A[i-1] - A[i-2] 时,dp[i] = dp[i-1] + 1。
分治整数
分割整数的最大乘积:2层for循环,注意如果不拆分也有可能比拆分的乘积大;思路:dp[i]+=dp[i-1] ;分割整数构成字母字符串:如果当前数字与前一位组合的数字处于10-26时,再加dp[i-2];最长递增子序列思路:两层for循环,第二层循环里面遍历找到所有比当前数小的数,做memo[i]的重新赋值(和最大值比较再赋值); 一组整数对能够构成的最长链:先排序,然后和上面一题一样。最长摆动子序列:思路:定义一个up和一个down,GitHub解法o(n);【牛逼的】;最长公共子序列:考虑2种情况;
0-1 背包
dp [i] [j] = max(dp [i-1] [j],dp [i-1] [j-w] + v)【空间优化:dp [ j ] = max(dp [j] , dp [i-1] + v)】
划分数组为和相等的两部分:可以看成一个背包大小为 sum/2 的 0-1 背包问题。改变一组数的正负号使得它们的和为一给定数:可以转换为 Subset Sum 问题;找零钱的最少硬币数; 找零钱的硬币数组合;
股票交易
搜索
BFS
1. 计算在网格中从原点到特定点的最短路径长度
|
|
题目描述:1 表示可以经过某个位置,求解从 (0, 0) 位置到 (tr, tc) 位置的最短路径长度。
思路:BFS 就像层次遍历似的
2. 组成整数的最小平方数数量
|
|
可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。
要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。
思路:注意队列加标记
3. 最短单词路径(单词接龙)
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
|
|
思路:步骤和准备工作比较多,但终究还是BFS,注意图的节点存的是字符串在wordlist里的索引,不是字符串本身。
DFS
1. 查找最大的连通面积
给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)
示例 1:
|
|
对于上面这个给定矩阵应返回 6
。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。
思路:递归,标记
2. 矩阵中的连通分量数目
|
|
求岛屿的数量
思路:递归,标记,与上题不同的是DFS 不返回值,作用仅仅是把可以连通的标记置为‘0’
3. 好友关系的连通分量数目
|
|
题目描述:好友关系可以看成是一个无向图,例如第 0 个人与第 1 个人是好友,那么 M[0][1] 和 M[1][0] 的值都为 1。
思路:递归,标记;与上题不同的是:上题是个无向图,这里是有向图,主函数只要一层循环,还需要一个一维数组。
4. 填充封闭区域
给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
思路:先填充最外侧,剩下的就是里侧了。(把外侧的O换成T,最后遍历时把所有O换成X,所有T换成O)
5. 能到达的太平洋和大西洋的区域
给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。
规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。
请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。
|
|
思路:先填充最外侧for (int i = 0; i < m; i++) {...}
for (int i = 0; i < n; i++) {...}
回溯
1. 数字键盘组合
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
|
|
思路:递归的函数:先写出口条件,再写循环,循环里:先写追加元素,然后递归调用,最后删除(回退);我的解法由于函数最后一个参数传的是s+new str 这种形式,所以不用回退。【also in mk】。我的解法需要一个全局的list在递归出口处add(s),s来保存当前已有元素。
2. IP 地址划分
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
示例:
输入: “25525511135”
输出: [“255.255.11.135”, “255.255.111.35”]
思路:【also in mk】需要一个全局list:ans 在递归出口处add一个叫cur的List,cur来保存当前已有元素
// 该题可以看成如何分割串(分割完之后再split)
|
|
3. 在矩阵中寻找字符串
【also in mk】
|
|
|
|
需要一个访问标记数组
4. 输出二叉树中所有从根到叶子的路径
|
|
思路:我的解法没有回退的操作,是因为:我在传字符串时是 以 s + newstr 的形式。而GitHub解法需要一个全局的List:paths,在isLead() 为true 时 add 一个叫values的list。
5. 排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
思路:可以看到需要一个全局的list 在递归出口时 add(curList),一个当前元素的list:curList。一个访问标记数组
6. 含有相同元素求排列
|
|
数组元素可能含有相同的元素,进行排列时就有可能出现重复的排列,要求重复的排列只返回一个。
在实现上,和 Permutations 不同的是要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。
7. 组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
|
|
|
|
|
|
8. 组合求和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
|
|
9. 含有相同元素的组合求和
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
|
|
注意:和6一样:先排序,在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。
10. 1-9 数字的组合求和
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
11. 子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
also in mk
总结:对于解集不能包含重复的组合,像11,10,9(还有8,7);for循环都得从start开始
12. 含有相同元素求子集
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
相比11,先排序,在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。
13. 分割字符串使得每个部分都是回文数
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
|
|
思路:也是for(i=start…..)
14. 数独
todo
15. N 皇后
返回所有解法
思路:定义一个列访问位数组,一个正对角线访问位数组(i+j),一个斜对角线访问位数组(i-j)