93. Restore IP Addresses
恢复IP地址。原题
1 | Input: "25525511135" |
方法一:需要注意0的情况。
1 | def restoreIpAddresses(self, s: str) -> List[str]: |
131. Palindrome Partitioning
回文串切分。原题
1 | Input: "aab" |
方法一:原始回溯。
1 | def partition(self, s: str) -> List[List[str]]: |
1 | def partition(self, s: str) -> List[List[str]]: |
77. Combinations
实现组合。原题
1 | Input: n = 4, k = 2 |
方法一:回溯。700ms, 比Solution中的慢了100ms.
1 | def combine(self, n: int, k: int) -> List[List[int]]: |
方法二:Solution中的递归。
1 | def combine(self, n: int, k: int) -> List[List[int]]: |
1 | def combine(self, n: int, k: int) -> List[List[int]]: |
39. Combination Sum
和77类似,找出和为指定数值的组合,候选数字可以重复使用。原题
1 | Input: candidates = [2,3,5], target = 8, |
方法一:将候选组排序,并记录当前的索引值。
1 | def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: |
方法二:时隔一年又重做一遍,感觉方法更加精炼了。sort感觉没啥必要。
1 | def combinationSum(self, cn: List[int], target: int) -> List[List[int]]: |
40. Combination Sum II
组合求和,和39类似,区别在于候选数字中可能有重复数字,但每个数字只能使用一次。原题
麻烦的地方在于如何保证结果是非重复的。当j==i
时,cn[j]==cn[j-1]
说明前面刚刚用了和这个一样的数字。可以继续使用。比如[1,1,6]
。当j!=i
,cn[j]==cn[j-1]
时,说明想以j开头同样找数组,这样肯定会找出一个重复的。
1 | def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: |
216. Combination Sum III
组合求和,从1~9选出k个不重复的数,和为n。原题
1 | Input: k = 3, n = 9 |
1 | def combinationSum3(self, k: int, n: int) -> List[List[int]]: |
方法二:递归,使用last
作为上限。
1 | def combinationSum3(self, k: int, n: int) -> List[List[int]]: |
78. Subsets
输出给定无重复集合的子集组合。原题
1 | Input: nums = [1,2,3] |
方法一:常规写法。
1 | def subsets(self, nums: List[int]) -> List[List[int]]: |
1 | def subsets(self, nums: List[int]) -> List[List[int]]: |
方法三:使用索引来做。
1 | def subsets(self, nums: List[int]) -> List[List[int]]: |
90. Subsets II
和78类似,区别在于给定的数组有重复元素。原题
方法一:用到了40题中解法去重的代码。
1 | def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: |
79. Word Search
矩阵中的路径。原题
1 | board = |
1 | def exist(self, g: List[List[str]], word: str) -> bool: |
200. Number of Islands
小岛的个数。原题
1 | Input: |
方法一:常规写法。
1 | def numIslands(self, grid: List[List[str]]) -> int: |
方法二:当登录一座岛屿时,使这座岛屿下沉,变为’0’。不明白为什么比上个方法慢了20ms。
1 | def numIslands(self, grid: List[List[str]]) -> int: |
1254. Number of Closed Islands
和200类似,但是与边界相连的岛不能算了。原题
1 | Input: grid = [[1,1,1,1,1,1,1,0],[1,0,0,0,0,1,1,0],[1,0,1,0,1,1,1,0],[1,0,0,0,0,1,0,1],[1,1,1,1,1,1,1,0]] |
方法一:下沉法。先把边界的岛屿处理,然后再累计。
1 | def closedIsland(self, g: List[List[int]]) -> int: |
130. Surrounded Regions
将四周被包围的O
翻转成X
,边缘不算包围。原题
方法一:传统的方法在延伸的时候判断不出是否到达边界。所以这里先得到边界的点,然后从边界往里延伸,将所有的O
变为S
,第二次遍历时,将S
恢复成O
,其他设为X
。
1 | def solve(self, board: List[List[str]]) -> None: |
417. Pacific Atlantic Water Flow
太平洋大西洋水流向,假设陆地上可以沿着高度不高于的地方流淌,求能同时流入两个海洋的陆地坐标。原题
1 | Given the following 5x5 matrix: |
方法一:一开始我以为从左上的点只能往右或下方向找陆地,事实上还是要寻找四个方向。这应该是最暴力的解法了。但是看了几个高票答案都没我这个快。
1 | def pacificAtlantic(self, matrix: List[List[int]]) -> List[List[int]]: |
526. Beautiful Arrangement
完美安排,给定1~N的数字,生成一种排列,是第i位的数字能被i整除,或者i能被第i位的数字整除,索引从1开始。原题
1 | Input: 2 |
方法一:常规写法,1300ms,beats50%.
1 | def countArrangement(self, N: int) -> int: |
方法二:去掉了方法一中一些无用的空间和操作。1000ms, beats 67%.
1 | def countArrangement(self, N: int) -> int: |
1 | def countArrangement(self, N: int) -> int: |
1 | cache = {} |
22. Generate Parentheses
生成n对合法的括号。原题
1 | [ |
方法一:常规写法。
1 | def generateParenthesis(self, n: int) -> List[str]: |
1 | def generateParenthesis(self, n: int) -> List[str]: |
89. Gray Code
灰码问题。给定一个n表示n位。从0开始每次改变一个位的数字,产生所有的组合。原题
1 | Input: 2 |
方法一:回溯法。一定要有顺序,一开始我误以为n位的进制0或1的组合。但是不行,因为不能从01
跨越到10
。这个问题我想了很久没有想出来如何解决,看到discuss中一个java版本的受到了启发。
1 | def grayCode(self, n: int) -> List[int]: |
1 | 0 1 11 110 |
1 | def grayCode(self, n: int) -> List[int]: |
842. Split Array into Fibonacci Sequence
一串数组组成的字符串拆成斐波那契数列。原题
1 | Input: "123456579" |
ps: 此题第一天做的时候想了很久,提交了n个错误答案,后来第二天做的时候,20分钟就做出来了。
方法一:第一次AC的答案。需要注意几个点。首先只有当元素结果大于2才算数列;主循环中range(1, len(rest)+1)
否则会丢失最后元素;因为0的存在,所以要考虑0不能作为数字的开头;最后题中要求每个数字要在int32
范围内。224ms.
1 | def splitIntoFibonacci(self, S: str) -> List[int]: |
优化二:添加一个flag,在找到结果后返回。188ms, beats 26%.
1 | def splitIntoFibonacci(self, S: str) -> List[int]: |
优化三:每个元素既然小于2**31-1
那么,最长长度为10。在主循环中加入此条件可以提升到52ms。
1 | def splitIntoFibonacci(self, S: str) -> List[int]: |
1 | def splitIntoFibonacci(self, S: str) -> List[int]: |
1 | def splitIntoFibonacci(self, S: str) -> List[int]: |
46. Permutations
数组全排列。原题
1 | Input: [1,2,3] |
1 | def permute(self, nums): |
1 | def permute(self, nums: 'List[int]') -> 'List[List[int]]': |
方法二:iteratively. 思想为拿出一个数字插入到现有排序中的各个位置。
1 | def permute(self, nums): |
47. Permutations II
全排列并去重。原题
1 | Input: [1,1,2] |
思路:当然可以使用set
来去重,或者考虑一种迭代的方式。
展开。拿着每个数字向上一个结果中插入到每一个位置。
1 | def permuteUnique(self, nums): |
输入nums=[1, 2, 3]
1 | j 0 - [] + [1] + [] |
理解一下是如何去重的,我们输入nums=[1, 2, 1]
1 | j 0 - [] + [1] + [] |
1 | def permuteUnique(self, nums): |
1 | def permuteUnique(self, nums: List[int]) -> List[List[int]]: |
1079. Letter Tile Possibilities
本来是一道回溯的题,结果测试用例过于简单,所以暴力法就解出来了。原题
1 | Input: "AAB" |
1 | def numTilePossibilities(self, tiles: str) -> int: |
1415. The k-th Lexicographical String of All Happy Strings of Length n
列出第k个快乐字符串(a,b,c)组成并没有连续相同的字母。原题
方法一:回溯法。列出所有的值,排序索引。
1 | def getHappyString(self, n: int, k: int) -> str: |
1 | def getHappyString(self, n: int, k: int) -> str: |
1255. Maximum Score Words Formed by Letters
给定一些单词,每个字母有得分,不过每个字母用多少是有限制的。问最大得分是多少,一个典型的背包问题。原题
1 | Input: words = ["dog","cat","dad","good"], letters = ["a","a","c","d","d","d","g","o","o"], score = [1,0,9,5,0,0,3,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0] |
方法一:回溯。累加单词的分数可以提前退出递归。
1 | def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: |
方法二:这个和我一开始想的方法很像。
1 | def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: |
1239. Maximum Length of a Concatenated String with Unique Characters
最长的不重复的字符串组合。原题
1 | Input: arr = ["un","iq","ue"] |
方法一:回溯法。
1 | def maxLength(self, arr: List[str]) -> int: |
方法二:Lee215的解法。append的方式,会将每步累加的追加的ans中。
1 | def maxLength(self, arr: List[str]) -> int: |
51. N-Queens
著名的N皇后问题,将N个皇后摆在N*N的棋盘中,直线,斜线不能同时存在2个皇后。问所有的摆放位置。原题
方法一:调了一次就AC了。记录第一次AC的方法。
1 | def solveNQueens(self, n: int) -> List[List[str]]: |
方法二:row是不必要的,因为遍历行是不会有重复的。
1 | def solveNQueens(self, n: int) -> List[List[str]]: |
1 | def solveNQueens(self, n: int) -> List[List[str]]: |
52. N-Queens II
和上边一样的N皇后问题,要求最后有多少种。原题
方法一:不再需要一个数组来记录棋盘了,只需要计数就行。然后通过一个数组标记每行的棋子的纵坐标。而且无需重置,因为值会覆盖之前的。本质上还是回溯方法。
1 | def totalNQueens(self, n: int) -> int: |
方法二:位运算,空间上节省了一个一维数组,虽然数组长度不会很长。时间上快了一倍。
1 | def totalNQueens(self, n: int) -> int: |
37. Sudoku Solver
解9*9的数独。原题
方法一:首次AC的方法,嗨呀,递归调用时忘记写return True了,调了半天。
1 | def solveSudoku(self, g: List[List[str]]) -> None: |
980. Unique Paths III
二维数组中有0,1,2,-1四种格子,要求从1开始走到2,经过所有的0,一共有多少种走法。
方法一:回溯法,思路很清晰,很快就写出来了,方向变量写错了调了半天。只需要关注0的个数就行。有优化的地方,因为1和2只有一个,所以可以先遍历一次找出0个个数和1的位置。
1 | def uniquePathsIII(self, g: List[List[int]]) -> int: |
491. Increasing Subsequences
求一个数组的所有的长度2以上的递增子序列。
方法一:联系了几道题,求最大长度的是用的dp,不适合这个。这里发现和subsets的组合有点像。于是用了78题中的方法二。因为数组本身最大也就15长度,所以方法并不很耗时。1 | def findSubsequences(self, nums: List[int]) -> List[List[int]]: |
方法二:我想用90的方法直接去重,试了一下,发现90题依赖数组有序才能去重,所以这里并不适用。只能最后通过转化tuple,set再去重。效率只有方法一的一半。
1 | def findSubsequences(self, nums: List[int]) -> List[List[int]]: |
1 | def findSubsequences(self, nums: List[int]) -> List[List[int]]: |
面试题 08.09. 括号
求n对括号的合法组合。
方法一:回溯,我的方法。
1 | def generateParenthesis(self, n: int) -> List[str]: |
方法二:大神的方法,学习了,使用and代替if。
1 | def generateParenthesis(self, n: int) -> List[str]: |
1655. Distribute Repeating Integers
分配重复的数字,根据quantity
中每个顾客的需求,给每个人分配quantity[i]
个相同的元素。
1 | Input: nums = [1,2,3,4], quantity = [2] |
方法一:暴力回溯法。作为竞赛的4题没有做上来。比赛的时候想用一种贪心的方式来做,这样做是不对的。这是Lee215竞赛时的方法。
1 | def canDistribute(self, nums: List[int], quantity: List[int]) -> bool: |
1718. Construct the Lexicographically Largest Valid Sequence
给你一个整数 n ,请你找到满足下面条件的一个序列:
整数 1 在序列中只出现一次。
2 到 n 之间每个整数都恰好出现两次。
对于每个 2 到 n 之间的整数 i ,两个 i 之间出现的距离恰好为 i 。
序列里面两个数 a[i] 和 a[j] 之间的 距离 ,我们定义为它们下标绝对值之差 |j - i| 。请你返回满足上述条件中 字典序最大 的序列。
1 | 输入:n = 3 |
方法一:比赛时没时间做,思路是正确的,有一个判断很关键,没有这个判断代码将会超时。里面有贪心的思想,优先拿最大的数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def constructDistancedSequence(self, n: int) -> List[int]:
can = list(range(1, n+1))
res = [-1] * (n*2 - 1)
def backtrack(i, left):
if not left:
return True
if res[i] != -1: # 没有这个优化,代码将会超时。
return backtrack(i+1, left)
for j in range(len(left)-1, -1, -1):
d = left[j]
r = i if d==1 else i+d
if r<len(res) and res[r]==-1:
res[i] = res[r] = d
if backtrack(i+1, left[:j]+left[j+1:]):
return True
res[i] = res[r] = -1
backtrack(0, can)
return res
1723. Find Minimum Time to Finish All Jobs
给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。
1 | 输入:jobs = [3,2,3], k = 3 |
方法一:竞赛时搜了一下,以为是NP背包问题被吓住了, 其实给定的范围可以用回溯剪枝的方式来做。总共有三个优化点。其中第二点比较难想。1. 可以按照时长最多的倒序分配给每个工人;2.让某个工人不干活的次数只有一次,比如以[3,2,3],k=3
为例,[32,3,0]
与[32,0,3]
是重复的,后者可以提前终止;如果当前结果过大了,就不用再继续了。2852ms.
1 | def minimumTimeRequired(self, A: List[int], k: int) -> int: |
1 | def minimumTimeRequired(self, A: List[int], k: int) -> int: |