62 不同路径
标准的动态规划,每一步数都可以从上一步的状态转移过来。
class Solution(object):
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
dp = [[0 for i in range(n)] for i in range(m)]
dp[0][0] = 1
for i in range(m):
for j in range(n):
if i > 0 :
dp[i][j] = dp[i][j]+dp[i-1][j]
if j > 0:
dp[i][j] = dp[i][j]+dp[i][j-1]
return dp[m-1][n-1]
对于动态规划还是注意初始条件,和转移方程。
以及对于二维dp,还是看清楚m
和n
的具体,分清楚行和列。
64 最小路径和
依然是一道dp问题,这类图表dp已经找到一些敲门,可以对比这个代码和上一题的代码。有很强的相似性。
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
n = len(grid[0])
dp = [[float('inf') for i in range(n)] for i in range(m)]
dp[0][0] = grid[0][0]
for i in range(m):
for j in range(n):
if i > 0:
dp[i][j] = min(dp[i][j], dp[i-1][j]+grid[i][j])
if j > 0:
dp[i][j] = min(dp[i][j], dp[i][j-1]+grid[i][j])
return dp[m-1][n-1]
代码精妙之处:
- 需要注意的就是,看到dp表初始为0还是inf比较合适
- 如果希望节省空间,可以改为
grid[i][j] = min(dp[i][j], dp[i-1][j]+grid[i][j])
直接在原坐标上修改
75 颜色分类
又是看到题目就傻了,没有找到思路,似然想到这种想要减少复杂度的题目应该用指针或许可以解决。但是我没找到突破口。
这个三指针方法有点绕。用三个指针p0
,p2
和cur
来分别表示第一个1,2的最左边界(不含2)和当前考虑的元素。
本解法的思路是沿着数组移动 curr
指针,
- 若
nums[cur] = 0
,则将其与nums[p1]
互换并且移动p1
,cur
; - 若
nums[cur] = 2
,则与nums[p2]
互换,并且移动p2
; - 若
nums[cur]=1
,则继续移动cur
。
class Solution(object):
def sortColors(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
cur = 0
p1 = 0
p2 = len(nums)-1
while cur<=p2:
if nums[cur] == 0:
nums[p1], nums[cur] = nums[cur], nums[p1]
cur += 1
p1 += 1
elif nums[cur] == 2:
nums[p2], nums[cur] = nums[cur], nums[p2]
p2 -= 1
elif nums[cur] == 1:
cur += 1
一道精妙的算法题,难点在于每次判断结果以后,指针如何进行迭代。
78 子集
还是一道回溯(dfs)题目,套用前面的模板就可以ac
class Solution(object):
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = []
nums.sort()
def backtrack(now, index):
res.append(now[:])
for i in range(index, len(nums)):
now.append(nums[i])
backtrack(now, i+1)
now.pop()
backtrack([], 0)
return res
对于这类题目,最优方法对了在优化上可以没必要过分苛求。
79 单词搜索
一道很不错的DFS+回溯算法的题目,值得二刷复习的。
看到题目,思路大概限定在了BFS,DFS, 和dp上。如果是dp,子问题就是‘ABCCE’是否存在,感觉还是不解决根本问题。
在BFS和DFS选择的话,bfs会遇到一个问题就是多源搜索的话,不好保存每次已经走过的路程。
什么问题适合用bfs和dfs?我分享下我对这个题目的思考过程。 这个题目如果使用BFS有两种思路,一个是多源起点开始bfs,也就是从每个’A’都同步开始执行广度搜索,但是这样有一个问题每次不好标记那些是已经加入队列的不能再次使用,因为是同时多支搜索的。如果是单源bfs,就和weiwei回答一样,bfs占用的空间比较大。 加上,这个题目要求只要找到一个通路就可以返回,更适合一条路走到黑的dfs。 如果问有几种不同的方式组成‘ABCDE’,那其实bfs和dfs的复杂就是一样的了。
废话不多说了,直接上代码。
class Solution(object):
def exist(self, board, word):
"""
:type board: List[List[str]]
:type word: str
:rtype: bool
"""
def dfs(r, l, n,mark):
if n == len(word):
return True
# 向上搜索
if r>0 and board[r-1][l]==word[n] and mark[r-1][l]==0:
mark[r-1][l] = 1
#!!!注意标记,这个位置已经被使用了
if dfs(r-1, l, n+1,mark):
return True
# !!结束一次失败的搜索以后,需要及时释放
mark[r-1][l] = 0
if r<h-1 and board[r+1][l] == word[n] and mark[r+1][l]==0:
mark[r+1][l] = 1
if dfs(r+1, l, n+1,mark):
return True
mark[r+1][l] = 0
if l>0 and board[r][l-1]==word[n] and mark[r][l-1]==0:
mark[r][l-1] = 1
if dfs(r, l-1, n+1,mark):
return True
mark[r][l-1] = 0
if l<w-1 and board[r][l+1] == word[n] and mark[r][l+1]==0:
mark[r][l+1] = 1
if dfs(r, l+1, n+1, mark):
return True
mark[r][l+1] = 0
return False
h = len(board)
w = len(board[0])
mark = [[0 for i in range(w)]for j in range(h)]
for i in range(h):
for j in range(w):
if board[i][j] == word[0]:
mark[i][j] = 1
if dfs(i,j, 1,mark):
return True
mark[i][j] =0
return False
代码亮点:
- 还是注意这个题目需要标记已经使用的元素
- 如何实现剪枝,全在于每次只要
True
就返回