96.不同的二叉搜索树
状态:不会,重做
题目
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 | 1 1 2 3 3 |
解析
动态规划
以1,2,...,n
为节点组成的BST有G[n]
种,
G[n]=F(1,n)+F(2,n)+...+F(i,n)+...+F(n,n)
其中,F(i,n)
表示以i为根节点组成的BST个数,
然而,F(i,n)=G[i-1] * G[n-i]
注意,G[0]=1
以n=3为例,G[3]=F(1,3)+F(2,3)+F(3,3)
,
1 | F(1,3)=G[0] * G[2] |
代码
1 | class Solution(object): |
413. 等差数列划分
状态:亟待优化
题目
如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。
例如,以下数列为等差数列:
1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
以下数列不是等差数列。
1, 1, 2, 5, 7
数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P<Q<N 。
如果满足以下条件,则称子数组(P, Q)为等差数组:
元素 A[P], A[p + 1], …, A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。
函数要返回数组 A 中所有为等差数组的子数组个数。
示例:
A = [1, 2, 3, 4]
返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。
version1
过程:
成绩:
解析
审题一定要清楚:
- 给定的数组A不一定是等差数列,不要让示例先入为止;
- 等差子数列,题目中明确要求:元素 A[P], A[p + 1], …, A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。即表明,子数列是在原数列中不间隔选出的,且至少3个元素。
基于以上分析,先枚举出子数列的所有情形,再判断该子数列是否为等差,但是超时的缘故,在枚举子数列的过程中,若判定该子数列不等差,那么可以包含该子数列的数列肯定也不等差。优化这一判断条件后才勉强通过时间限制。
代码
1 | class Solution(object): |
标准答案
以A=[1,2,3,4,5,6]为例:
10=1+2+3+4
在枚举子数列时,判断A[i-2],A[i-1],A[i]是否为等差数列,若是则根据前一个循环是否也是等差决定是否叠加。
1 | class Solution(object): |
120. 三角形最小路径和
状态:排名85%,未考虑空间复杂度
题目
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
1 | [ |
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
version1
过程:
成绩:
解析
参考之前矩阵的题目,从最左上角开始,每次只能往下或者往右走,最佳的解题思路就是算出每走一步后的结果,依次迭代。
那么,这里的思路也是一样的,这里的限制条件是只能走相邻的点。
代码
1 | class Solution(object): |
标准答案
关键在于空间复杂度:
我的解法是至上而下的,需要修改数组中的每一个元素。现在有一种解法是至下而上,只写存最后一行的内层数组空间,这种解法更节约空间。
代码
1 | class Solution(object): |
62. 不同路径
状态:排名95.86%
题目
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
1 | 输入: m = 3, n = 2 |
示例 2:
1 | 输入: m = 7, n = 3 |
version1
过程:
成绩:
解析
第一次提交失败:未考虑周全矩阵的形式。
思路和之前矩阵求最小路径和以及三角形最小路径和是一样的,计算每个节点的路径可能数,最终答案即为最后一个点的值。
代码
1 | class Solution: |
712. 两个字符串的最小ASCII删除和
状态:不会做
题目
给定两个字符串s1, s2,找到使两个字符串相等所需删除字符的ASCII值的最小和。
示例 1:
输入: s1 = “sea”, s2 = "eat"
输出: 231
解释: 在 “sea” 中删除 “s” 并将 “s” 的值(115)加入总和。
在 “eat” 中删除 “t” 并将 116 加入总和。
结束时,两个字符串相等,115 + 116 = 231 就是符合条件的最小和。
示例 2:
输入: s1 = “delete”, s2 = "leet"
输出: 403
解释: 在 “delete” 中删除 “dee” 字符串变成 “let”,
将 100[d]+101[e]+101[e] 加入总和。在 “leet” 中删除 “e” 将 101[e] 加入总和。
结束时,两个字符串都等于 “let”,结果即为 100+101+101+101 = 403 。
如果改为将两个字符串转换为 “lee” 或 “eet”,我们会得到 433 或 417 的结果,比答案更大。
注意:
0 < s1.length, s2.length <= 1000。
所有字符串中的字符ASCII值在[97, 122]之间。
最佳答案
这题是经典题目“最长公共子串LCS”的变形题目。
在弄懂LCS之后,这题就很好理解了。
LCS:
1 | 初始化dp矩阵: |
标准代码
1 | class Solution: |
647. 回文子串
状态:排名23.26%
题目
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。
示例 1:
1 | 输入: "abc" |
示例 2:
1 | 输入: "aaa" |
注意:
输入的字符串长度不会超过1000。
version1
过程:
成绩:
解析
首先一定搞明白回文字符串的意思,回文即正着读反着读都一样。
所以第一次提交失败。
第二次暴力求解,成绩亟待提高。
代码
1 | class Solution: |
303. 区域和检索 - 数组不可变
状态:排名41.91%,待提高
题目
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
示例:
1 | 给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange() |
说明:
你可以假设数组不可变。
会多次调用 sumRange 方法。
version1
过程:
成绩:
解析
这题思路很简单。
代码
1 | class NumArray: |
标准答案
这题主要是要用动态规划的思想,题目要求的sumRange(i,j),是可以在遍历数组元素的时候进行叠加求和,一次性求出新的叠加数组。
代码
1 | class NumArray: |
343. 整数拆分
状态: 排名99%
题目
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。
version1
过程:
version2
过程:
成绩:
解析
第二次再写,硬生生地记住了结论,解题关键要尽可能多拆分为3,若剩下1,则要将3和1转换为2和2。
标准答案
这题主要还是没搞懂解题思路,解题的关键是神奇的质数2和质数3。
代码
1 | from functools import reduce |
638. 大礼包
状态:未通过,有思路但是有难点无法克服
题目
在LeetCode商店中, 有许多在售的物品。
然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。
现给定每个物品的价格,每个大礼包包含物品的清单,以及待购物品清单。请输出确切完成待购清单的最低花费。
每个大礼包的由一个数组中的一组数据描述,最后一个数字代表大礼包的价格,其他数字分别表示内含的其他种类物品的数量。
任意大礼包可无限次购买。
示例 1:
1 | 输入: [2,5], [[3,0,5],[1,2,10]], [3,2] |
示例 2:
1 | 输入: [2,3,4], [[1,1,0,4],[2,2,1,9]], [1,2,1] |
说明:
最多6种物品, 100种大礼包。
每种物品,你最多只需要购买6个。
你不可以购买超出待购清单的物品,即使更便宜。
version1
过程:
本地编译器上未调通,有思路,但是不知道如何解决不同大礼包优惠程度的对比。
标准答案
深度优先搜索,主要就是用大礼包或者原价购买的价格比较,不仅要比较不同大礼包之间的差别,还要比较与原价之间的价格。大礼包的比较肯定要用for循环,然后保存下来方便与后续比较。
代码
1 | class Solution: |
312. 戳气球
状态:未通过,思路错误
题目
有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
现在要求你戳破所有的气球。
1 | 每当你戳破一个气球 i 时,你可以获得 nums[left] * nums[i] * nums[right] 个硬币。 |
这里的 left 和 right 代表和 i 相邻的两个气球的序号。
注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。
求所能获得硬币的最大数量。
说明:
1 | 你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的所以并不能被戳破。 |
示例:
1 | 输入: [3,1,5,8] |
version1
过程:
整个思考过程就是错误的。忧伤···
标准答案
代码
1 | class Solution(object): |
740.删除与获得点数
95.不同的二叉搜索树II
121.买卖股票的最佳时机
状态:未通过,超时
题目
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例 1:
1 | 输入: [7,1,5,3,6,4] |
示例 2:
1 | 输入: [7,6,4,3,1] |
version1
过程:
思路有,就是实现起来不够动态规划。所以会导致很慢,超时不通过。
标准答案
标准答案是只需要一次遍历,求出答案,复杂度是O(N)。而我第一次版本是不止一次遍历的,不仅是明显写出的第一次循环还是隐性的求min,max(其实也是遍历)。
代码
1 | class Solution: |
931.下降路径最小和
状态:排名98%
题目
给定一个方形整数数组 A,我们想要得到通过 A 的下降路径的最小和。
下降路径可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列。
示例:
1 | 输入:[[1,2,3],[4,5,6],[7,8,9]] |
提示:
1 | 1 <= A.length == A[0].length <= 100 |
version1
过程:
成绩:
这种类型的题目已经掌握了,笔试是不会怕了。
相似题目有120,62。
代码
1 | class Solution: |
70.爬楼梯
状态:未通过
题目
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
1 | 输入: 2 |
示例 2:
1 | 输入: 3 |
标准答案
爬楼梯是一道经典的动态规划类型题目。
这道题目是逆推。比如,要想爬10级楼梯,可以1次爬1级,也可以1次爬2级。如果现在考虑,只剩最后一步,那么有可能是最后一步爬1级,也有可能是最后一步爬2级。如果最后一步爬1级,那么问题转换为爬9级台阶的方式,同理可知,如果最后一步爬2级,就等价于秋节爬8级台阶的方法。
数学表达式:
F(10)=F(9)+F(8)
F(9)=F(8)+F(7)
…
代码
1 | class Solution: |
152. 乘积最大子序列
状态:未通过
题目
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
1 | 输入: [2,3,-2,4] |
示例 2:
1 | 输入: [-2,0,-1] |
标准答案
动态规划的题目做多了,直觉告诉我这题应该一次遍历即可得到答案。
连续子序列的最大值可能因为符号的变化,最大值瞬间变为最小值,最小值瞬间变为最大值,故这题的关键是记录当前遍历状态下的最大值与最小值。
代码
1 | class Solution: |
122. 买卖股票的最佳时机 II
状态:未通过
题目
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
1 | 输入: [7,1,5,3,6,4] |
示例 2:
1 | 输入: [1,2,3,4,5] |
示例 3:
1 | 输入: [7,6,4,3,1] |
标准答案
这题的题目背景与121.买卖股票的最佳时机
非常相似。但是不要搞混淆,题目给出的条件一点点的差异也可能导致解法大大不同。这题没有不允许同一天操作买入与卖出的交易行为,而121题则明确提出不可以。
代码
1 | class Solution: |
200. 岛屿的个数
状态:未通过
题目
给定一个由’1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。
一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。
你可以假设网格的四个边均被水包围。
示例 1:
1 | 输入: |
示例 2:
1 | 输入: |
标准答案
方法一:
深度优先搜索,在每次遍历中尽可能深入地迭代遍历。
在目标函数中调用自建的dfs函数,dfs函数中递归调用dfs函数本身。
方法二:
并查集,利用树形结构,记录每个连通块的情况,而连通块的个数则代表岛屿的个数。
DFS代码
1 | class Solution: |
并查集
1 | class UnionFind(object): |
695. 岛屿的最大面积
状态:排名53.56%
题目
给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)
示例 1:
1 | [[0,0,1,0,0,0,0,1,0,0,0,0,0], |
示例 2:
1 | [[0,0,0,0,0,0,0,0]] |
version1
过程:
成绩:
与200. 岛屿的个数
类似,采用深度优先搜索的思想,200那题是计算岛屿的个数,这题计算岛屿的大小,大同小异。
代码
1 | class Solution: |
300. 最长上升子序列
状态:不会
题目
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
1 | 输入: [10,9,2,5,3,7,101,9,18] |
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
标准答案
动态规划,设置两个指针i,j以及一个长度与给定数组相同的数组T,核心公式:T[i]=max(T[i],T[j]+1)
。
具体讲解见Youtube:https://www.youtube.com/watch?v=CE2b_-XfVDk
代码
1 | class Solution: |
416. 分割等和子集
状态:不会
题目
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
1 | 输入: [1, 5, 11, 5] |
示例 2:
1 | 输入: [1, 2, 3, 5] |
标准答案
0/1背包问题,详细讲解见:
https://www.youtube.com/watch?v=8LusJS5-AGo
这题背包的重量为sum/2,比背包问题简单,没有价值信息,更不用求价值最大值。
代码
1 | class Solution: |
322. 零钱兑换
状态:不会
题目
给定不同面额的硬币 coins 和一个总金额 amount。
编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
1 | 输入: coins = [1, 2, 5], amount = 11 |
示例 2:
1 | 输入: coins = [2], amount = 3 |
说明:
你可以认为每种硬币的数量是无限的。
标准答案
经典0/1背包问题
的变种类型——完全背包,与01背包不同在于每种物品可以不止使用一次,物品个数是无限的。
解题关键在于当未计算或者使用该硬币,硬币总数量下降的就更新dp[i]
。
代码
1 | class Solution: |
377. 组合总和 Ⅳ
状态:不会
题目
给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
示例:
1 | nums = [1, 2, 3] |
请注意,顺序不同的序列被视作不同的组合。
因此输出为 7。
标准答案
经典0/1背包问题
的变种类型——完全背包,与Leetcode322相似,都是物品可以无限次使用,但是dp数组里记录的是不同组合个数。
代码
1 | class Solution: |
474. 一和零
状态:不会
题目
在计算机界中,我们总是追求用有限的资源获取最大的收益。
现在,假设你分别支配着 m 个 0 和 n 个 1。另外,还有一个仅包含 0 和 1 字符串的数组。
你的任务是使用给定的 m 个 0 和 n 个 1 ,找到能拼出存在于数组中的字符串的最大数量。每个 0 和 1 至多被使用一次。
注意:
给定 0 和 1 的数量都不会超过 100。
给定字符串数组的长度不会超过 600。
示例 1:
输入: Array = {“10”, “0001”, “111001”, “1”, “0”}, m = 5, n = 3
输出: 4
解释: 总共 4 个字符串可以通过 5 个 0 和 3 个 1 拼出,即 “10”,“0001”,“1”,“0” 。
示例 2:
输入: Array = {“10”, “0”, “1”}, m = 1, n = 1
输出: 2
解释: 你可以拼出 “10”,但之后就没有剩余数字了。更好的选择是拼出 “0” 和 “1” 。
标准答案
经典0/1背包问题
的变种类型——多维背包问题,不仅考虑物品的重量,而是要衡量物品的重量和体积,并且这里求的不是背包的最大价值,而是可能性中包含的最大字符串个数。
代码
1 | class Solution: |
139. 单词拆分
状态:不会
题目
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:
1 | 输入: s = "leetcode", wordDict = ["leet", "code"] |
示例 2:
1 | 输入: s = "applepenapple", wordDict = ["apple", "pen"] |
示例 3:
1 | 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] |
标准答案
属于完全背包问题
,考察的标准不再是体重,价值之类的数值型标准了,而是字符串。dp数组记录当前位置i是否可分。
代码
1 | class Solution: |
494. 目标和
状态:不会
题目
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。
对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例 1:
1 | 输入: nums: [1, 1, 1, 1, 1], S: 3 |
注意:
数组的长度不会超过20,并且数组中的值全为正数。
初始的数组的和不会超过1000。
保证返回的最终结果为32位整数。
标准答案
这题要根据公式推导转换为背包问题
在nums中,有部分正数和负数。p表示所有正数的集合,q表示所有负数的集合,那么有sum§ - sum(q) = S,
sum§ + sum(q) = sum(all)
以上两个公式转换可得:
sum§ = (sum(all) + S)/2
那么,这个问题转换为单纯的背包价值问题。选择将不同价值的物品放入背包与否。
代码
1 | class Solution: |
547. 朋友圈
状态:不会
题目
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。
如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。
所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。
如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。
你必须输出所有学生中的已知的朋友圈总数。
示例 1:
1 | 输入: |
示例 2:
1 | 输入: |
标准答案
这题有两种解题思路,一个是DFS,另一个是并查集。
DFS:
依次遍历每个人,查看当前这个人所有的关系网,如果这个人有新朋友,添加到已查看集合中去,并再次查看这个新朋友的关系网,依次循环往复。
DFS代码
1 | class Solution: |
并查集代码
1 | class UnionFind(object): |
128. 最长连续序列
状态:第二遍通过11.11%
题目
给定一个未排序的整数数组,找出最长连续序列的长度。
要求算法的时间复杂度为 O(n)。
示例:
1 | 输入: [100, 4, 200, 1, 3, 2] |
version2
过程:
成绩:
代码
1 | class Solution: |
标准答案
首先对数组去重即集合,遍历集合中的每个元素,并弹出该元素,找出比这个元素小1或者大1的元素,并分别更新连续值标记。
标准代码
1 | class Solution: |
130. 被围绕的区域
状态:不会
题目
给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
示例:
1 | X X X X |
运行你的函数后,矩阵变为:
1 | X X X X |
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。
任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。
如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
标准答案
这一题与200. 岛屿的个数
以及695. 岛屿的最大面积
背景类似,解题关键在于找到边界,题目中的关键信息是把除边界以外的‘O’改成‘X’,那么就应该找到边界‘O’,不仅是四周的‘O’,还包括与边界‘O’相连的‘O’,这里就用到了深度优先搜索。
标准代码
1 | class Solution: |
3. 无重复字符的最长子串
状态:不会
题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
1 | 输入: "abcabcbb" |
示例 2:
1 | 输入: "bbbbb" |
示例 3:
1 | 输入: "pwwkew" |
标准答案
题目要求找到最大的不重复子串,运用双指针方法,详细解释见
https://zhuanlan.zhihu.com/p/36074066
代码
1 | class Solution: |
684. 冗余连接
状态:不会
题目
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。
附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。
答案边[u, v] 应满足相同的格式 u < v。
示例 1:
1 | 输入: [[1,2], [1,3], [2,3]] |
示例 2:
1 | 输入: [[1,2], [2,3], [3,4], [1,4], [1,5]] |
注意:
输入的二维数组大小在 3 到 1000。
二维数组中的整数在1到N之间,其中N是输入数组的大小。
标准答案
题目看着就很烦,他想表达的意思是找出使得图连通的最后一条边,删去这个边,输入的图就不再连通了,这一题参考博客
http://wulc.me/2017/10/12/LeetCode 解题报告(684,685,721)-并查集介绍及应用/
可以更好地理解并查集这种数据结构,并提到了路径压缩和按等级合并,这两个步骤可以降低算法的时间复杂度。
代码
1 | class UnionFindSet(object): |
685. 冗余连接 II
状态:不会
题目
在本问题中,有根树指满足以下条件的有向图。
该树只有一个根节点,所有其他节点都是该根节点的后继。
每一个节点只有一个父节点,除了根节点没有父节点。
输入一个有向图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。
附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。
每一个边的元素是一对[u, v],用以表示有向图中连接顶点 u and v和顶点的边,其中父节点u是子节点v的一个父节点。
返回一条能删除的边,使得剩下的图是有N个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。
示例 1:
1 | 输入: [[1,2], [1,3], [2,3]] |
示例 2:
1 | 输入: [[1,2], [2,3], [3,4], [4,1], [1,5]] |
注意:
二维数组大小的在3到1000范围内。
二维数组中的每个整数在1到N之间,其中 N 是二维数组的大小。
标准答案
这一题与Leetcode 684
相似,不过难度升级,从无向图变为有向图,要返回的边使得删除这条边之后输入的有向图变为一颗合格树。 而 合格树要求每个节点只有一个父节点,根据这个要求可以先筛选一遍,看图中是否存在多个父节点的点,那么要删除的边就在里面。
参考博客:
http://wulc.me/2017/10/12/LeetCode 解题报告(684,685,721)-并查集介绍及应用/
代码
1 | class UnionFindSet(object): |
721. 账户合并
状态:不会
题目
给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,
其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。
现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。
请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。
一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。
合并帐户后,按以下格式返回帐户:
每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。
例子 1:
1 | Input: |
注意:
accounts的长度将在[1,1000]的范围内。
accounts[i]的长度将在[1,10]的范围内。
accounts[i][j]的长度将在[1,30]的范围内。
标准答案
使用并查集或者DFS,这道题目里用到了路径压缩(找到u所在的树根v以后,把从u到v的路径上所有点的父亲都设置为v),注意parent是父节点并不是根节点。怎么去并,去并谁也是要想清楚的,比如这一题我们要合并的是账号,那么遍历账号,根据邮箱一样这个筛选条件去合并。
代码
1 | from collections import defaultdict |
763. 划分字母区间
状态:不会
题目
字符串 S 由小写字母组成。
我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。
返回一个表示每个字符串片段的长度的列表。
示例 1:
1 | 输入: S = "ababcbacadefegdehijhklij" |
注意:
1 | S的长度在[1, 500]之间。 |
标准答案
记录每个字符出现的最后一个索引,遍历字符串时,将每个字符与记录索引对比从而求得每个字符区间。
代码
1 | class Solution: |
93. 复原IP地址
状态:不会
题目
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
示例:
1 | 输入: "25525511135" |
标准答案
DFS
代码
1 | class Solution: |
393. UTF-8 编码验证
状态:空间复杂度未通过
题目
UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:
对于 1 字节的字符,字节的第一位设为0,后面7位为这个符号的unicode码。
对于 n 字节的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为0,后面字节的前两位一律设为10。
剩下的没有提及的二进制位,全部为这个符号的unicode码。
这是 UTF-8 编码的工作方式:
Char. number range | UTF-8 octet sequence |
---|---|
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
给定一个表示数据的整数数组,返回它是否为有效的 utf-8 编码。
注意:
输入是整数数组。只有每个整数的最低 8 个有效位用来存储数据。这意味着每个整数只表示 1 字节的数据。
示例 1:
1 | data = [197, 130, 1], 表示 8 位的序列: 11000101 10000010 00000001. |
示例 2:
1 | data = [235, 140, 4], 表示 8 位的序列: 11101011 10001100 00000100. |
代码
1 | class Solution: |
851. 喧闹和富有
状态:不会
题目
在一组 N 个人(编号为 0, 1, 2, …, N-1)中,每个人都有不同数目的钱,以及不同程度的安静(quietness)。
为了方便起见,我们将编号为 x 的人简称为 "person x "。
如果能够肯定 person x 比 person y 更有钱的话,我们会说 richer[i] = [x, y] 。注意 richer 可能只是有效观察的一个子集。
另外,如果 person x 的安静程度为 q ,我们会说 quiet[x] = q 。
现在,返回答案 answer ,其中 answer[x] = y 的前提是,在所有拥有的钱不少于 person x 的人中,person y 是最安静的人(也就是安静值 quiet[y] 最小的人)。
示例:
1 | 输入:richer = [[1,0],[2,1],[3,1],[3,7],[4,3],[5,3],[6,3]], quiet = [3,2,5,4,6,1,7,0] |
提示:
1 <= quiet.length = N <= 500
0 <= quiet[i] < N,所有 quiet[i] 都不相同。
0 <= richer.length <= N * (N-1) / 2
0 <= richer[i][j] < N
richer[i][0] != richer[i][1]
richer[i] 都是不同的。
对 richer 的观察在逻辑上是一致的。
标准答案
综合运用动态规划和记忆存储,这道题最佳答案秒在对安静数组进行排序,导致首先遍历最安静的人,那么这个人对应的答案就是自己,因为符合比自己富有(包括自己),并且最安静的要求。
紧接着遍历比这个人穷的人,并且标记每个穷人的答案就是这个人,因为符合相对富有且最安静的要求。
代码
1 | import collections |
5. 最长回文子串
状态:不会
题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
1 | 输入: "babad" |
示例 2:
1 | 输入: "cbbd" |
标准答案
回文子串一共有两种类型:
1.aba
2.abba
按照这两种类型的组成特点,依次从内到外遍历。利用记忆存储当前状态下的最长回文子串。
代码
1 | class Solution(object): |
63. 不同路径 II
状态:排名85.07%
题目
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。
示例 1:
1 | 输入: |
标准答案
62. 不同路径
的衍生版本,主要难点在于边界条件,在for循环之后要特别考虑有一些特殊情况容易漏掉。
代码
1 | class Solution(object): |
39. 组合总和
状态:不会
题目
给定一个无重复元素的数组candidates
和一个目标数target
,找出candidates
中所有可以使数字和为target
的组合。
candidates
中的数字可以无限制重复被选取。
说明:
所有数字(包括target
)都是正整数。
解集不能包含重复的组合。
示例 1:
1 | 输入: candidates = [2,3,6,7], target = 7, |
示例 2:
1 | 输入: candidates = [2,3,5], target = 8, |
标准答案
主要还是递归,不过递归中加入更多的限制条件。
参考:回溯算法+剪枝
代码
1 | import java.util.List; |
40. 组合总和II
状态:不会
题目
给定一个数组candidates
和一个目标数target
,找出candidates
中所有可以使数字和为target
的组合。
candidates
中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
1 | 输入: candidates = [10,1,2,7,6,1,5], target = 8, |
示例 2:
1 | 输入: candidates = [2,5,2,1,2], target = 5, |
标准答案
39. 组合总和
的衍生版本,主要不同在于每个数字只能使用一次。
参考自liweiwei1419的详细解答。
代码
1 | import java.util.ArrayList; |