以下题目多次复习
- 200 岛屿数量
- 未看解答自己编写的青春版
- 重点
- 本题的解题思路,也是之前没有接触过的,四字总结:学会感染!
- 题解的代码
- 日后复习重新编写
- 32 最长有效括号
- 未看解答自己编写的青春版
- 重点
- 这道题,动态规划的思路真的很巧妙,没有想明白的地方就在于,如果s[i-1]是右括号的情况,这时应该去寻找与之匹配的左括号,如果存在,index = i-1-dp[i-1],这点一定要搞懂!
- 题解的代码
- 日后复习重新编写
- 994 腐烂的橘子
- 未看解答自己编写的青春版
- 重点
- 这种题目用DFS很绕,作为初学者来说,还是先学习BFS。
- 题解的代码
- 日后复习重新编写
- 207 课程表
- 未看解答自己编写的青春版
- 重点
- 太牛逼了,这道题我叹为观止,着重复习。
- 题解的代码
- 日后复习重新编写
- 一段用于复制的标题
- 未看解答自己编写的青春版
- 重点
- 题解的代码
- 日后复习重新编写
200 岛屿数量
未看解答自己编写的青春版
没思路,目前没有系统学过DFS和BFS,不过大致思路应该了解,不知道怎么用在本题上。
重点
没有想到用感染函数!什么DFS,BFS,就是递归就可以了!没有想到可以改变 grid 值啊!
本题的解题思路,也是之前没有接触过的,四字总结:学会感染!
题解的代码
class Solution {
public int numIslands(char[][] grid) {
int islandNum = 0;
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(grid[i][j] == '1'){
infect(grid, i, j);
islandNum++;
}
}
}
return islandNum;
}
//感染函数
public void infect(char[][] grid, int i, int j){
if(i < 0 || i >= grid.length ||
j < 0 || j >= grid[0].length || grid[i][j] != '1'){
return;
}
grid[i][j] = '2';
infect(grid, i + 1, j);
infect(grid, i - 1, j);
infect(grid, i, j + 1);
infect(grid, i, j - 1);
}
}
日后复习重新编写
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
def infect(grid,i,j,m,n):
if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] != '1':
return
grid[i][j] = '2'
infect(grid,i+1,j,m,n)
infect(grid,i,j+1,m,n)
infect(grid,i-1,j,m,n)
infect(grid,i,j-1,m,n)
m = len(grid)
n = len(grid[0])
res = 0
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
res += 1
infect(grid,i,j,m,n)
return res
32 最长有效括号
未看解答自己编写的青春版
没写出来。能意识到有两种思路:栈和动态规划。动态规划想不清楚递推逻辑,栈的话,编写思路还是走的“有效括号匹配”,让字符入栈了,这是错误的!因为字符串在遍历的时候,有下标信息,下标之差就是最大长度!栈里应该存放下标信息!
错误的代码:
class Solution:
def longestValidParentheses(self, s: str) -> int:
if s == '':
return 0
res = []
stack = []
for i in s :
if i == '(':
res.append(-1)
stack.append(i)
else :
if stack == [] :
res.append(-1)
stack.append(i)
continue
temp = stack.pop()
if temp == '(':
number = res.pop()
if number < 0 :
res.append(2)
else :
res.pop()
res.append(2+number)
else :
res.append(-1)
stack = []
maxi = 0
count = 0
for i in res :
if i < 0 :
count = 0
else :
count += i
maxi = max(maxi,count)
return maxi
重点
这道题,动态规划的思路真的很巧妙,没有想明白的地方就在于,如果s[i-1]是右括号的情况,这时应该去寻找与之匹配的左括号,如果存在,index = i-1-dp[i-1],这点一定要搞懂!
题解的代码
class Solution:
def longestValidParentheses(self, s: str) -> int:
res = 0
stack = [-1]
for i, c in enumerate(s):
if c == "(":
stack.append(i)
else:
stack.pop()
if not stack:
stack.append(i)
else:
res = max(res, i - stack[-1])
return res
class Solution:
def longestValidParentheses(self, s: str) -> int:
dp = [0] * len(s)
for i in range(1, len(s)):
if s[i] == ")":
if s[i - 1] == "(":
dp[i] = dp[i - 2] + 2
else:
j = i - 1 - dp[i - 1]
if j >= 0 and s[j] == "(":
dp[i] = dp[j - 1] + dp[i - 1] + 2
return max(dp + [0]) # 避免max([])的报错
日后复习重新编写
994 腐烂的橘子
未看解答自己编写的青春版
做出来了,是用的和上一题相类似的想法。只不过我觉得这题不能用递归来解。
class Solution:
def orangesRotting(self, grid: List[List[int]]) -> int:
def is_right(grid,m,n):
for i in range(m):
for j in range(n):
if grid[i][j]==1:
if i!=0 and grid[i-1][j] > 1 :
return True
if j!=0 and grid[i][j-1] > 1 :
return True
if i!=m-1 and grid[i+1][j] > 1:
return True
if j!=n-1 and grid[i][j+1] > 1:
return True
return False
res = 2
m = len(grid)
n = len(grid[0])
while is_right(grid,m,n):
for i in range(m):
for j in range(n):
if grid[i][j]==res:
if i!=0 and grid[i-1][j] == 1:
grid[i-1][j] = res+1
if j!=0 and grid[i][j-1] == 1:
grid[i][j-1] = res+1
if i!=m-1 and grid[i+1][j] == 1:
grid[i+1][j] = res+1
if j!=n-1 and grid[i][j+1] == 1:
grid[i][j+1] = res+1
res += 1
for i in range(m):
for j in range(n):
if grid[i][j]==1:
return -1
#print(grid)
return res-2
重点
这种需要一圈一圈往外传播的一般用BFS解, 先找到起始所有腐烂的橘子,然后循环处理,把新腐烂的橘子加入下一次循环的队列中, 当下一次循环的队列为空时,说明不能继续腐烂了, 判断一下还有没有新鲜的橘子,如果有,就返回-1,否则返回分钟数。
class Solution(object):
def orangesRotting(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]
rotlist = list()
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 2:
rotlist.append([i, j])
minute = 0
while(rotlist): #BFS循环
newrotlist = list()
for rotnode in rotlist:
x0 = rotnode[0]
y0 = rotnode[1]
for k in range(4):
x = x0 + dx[k]
y = y0 + dy[k]
if 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 1:
grid[x][y] = 2
newrotlist.append([x,y])
if not newrotlist:
break
rotlist = newrotlist[:]
minute += 1
for row in grid:
for i in row:
if i == 1:#还有新鲜的
return -1
return minute
这种题目用DFS很绕,作为初学者来说,还是先学习BFS。
题解的代码
日后复习重新编写
207 课程表
未看解答自己编写的青春版
没思路,感觉用栈模拟是可以的,但是不知道怎么开启循环,也就是说,不知道怎样遍历所有情况。
重点
看了看评论,完全是我没接触过的点。
class Solution(object):
# 思想:该方法的每一步总是输出当前无前趋(即入度为零)的顶点
def canFinish(self, numCourses, prerequisites):
"""
:type numCourses: int 课程门数
:type prerequisites: List[List[int]] 课程与课程之间的关系
:rtype: bool
"""
# 课程的长度
clen = len(prerequisites)
if clen == 0:
# 没有课程,当然可以完成课程的学习
return True
# 入度数组,一开始全部为 0
in_degrees = [0 for _ in range(numCourses)]
# 邻接表
adj = [set() for _ in range(numCourses)]
# 想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
# [0,1] 表示 1 在先,0 在后
# 注意:邻接表存放的是后继 successor 结点的集合
for second, first in prerequisites:
in_degrees[second] += 1
adj[first].add(second)
# print("in_degrees", in_degrees)
# 首先遍历一遍,把所有入度为 0 的结点加入队列
res = []
queue = []
for i in range(numCourses):
if in_degrees[i] == 0:
queue.append(i)
counter = 0
while queue:
top = queue.pop(0)
counter += 1
for successor in adj[top]:
in_degrees[successor] -= 1
if in_degrees[successor] == 0:
queue.append(successor)
return counter == numCourses
class Solution(object):
# 这里使用逆邻接表
def canFinish(self, numCourses, prerequisites):
"""
:type numCourses: int 课程门数
:type prerequisites: List[List[int]] 课程与课程之间的关系
:rtype: bool
"""
# 课程的长度
clen = len(prerequisites)
if clen == 0:
# 没有课程,当然可以完成课程的学习
return True
# 深度优先遍历,判断结点是否访问过
# 这里要设置 3 个状态
# 0 就对应 False ,表示结点没有访问过
# 1 就对应 True ,表示结点已经访问过,在深度优先遍历结束以后才置为 1
# 2 表示当前正在遍历的结点,如果在深度优先遍历的过程中,
# 有遇到状态为 2 的结点,就表示这个图中存在环
visited = [0 for _ in range(numCourses)]
# 逆邻接表,存的是每个结点的前驱结点的集合
# 想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
# 1 在前,0 在后
inverse_adj = [set() for _ in range(numCourses)]
for second, first in prerequisites:
inverse_adj[second].add(first)
for i in range(numCourses):
# 在遍历的过程中,如果发现有环,就退出
if self.__dfs(i, inverse_adj, visited):
return False
return True
def __dfs(self, vertex, inverse_adj, visited):
"""
注意:这个递归方法的返回值是返回是否有环
:param vertex: 结点的索引
:param inverse_adj: 逆邻接表,记录的是当前结点的前驱结点的集合
:param visited: 记录了结点是否被访问过,2 表示当前正在 DFS 这个结点
:return: 是否有环
"""
# 2 表示这个结点正在访问
if visited[vertex] == 2:
# 表示遇到环
return True
if visited[vertex] == 1:
return False
visited[vertex] = 2
for precursor in inverse_adj[vertex]:
# 如果有环,就返回 True 表示有环
if self.__dfs(precursor, inverse_adj, visited):
return True
# 1 表示访问结束
visited[vertex] = 1
return False
总体来说的思路:
统计每个课被指向次数,初始被指向次数为0的肯定是安全的(不在环上)。
每被安全课程指向一次,被指次数减一,
如果被指次数减到0,说明该课程全部指向都来自安全课程,则它也是安全的。
依此进行队列循环。
太牛逼了,这道题我叹为观止,着重复习。
题解的代码
日后复习重新编写
一段用于复制的标题
未看解答自己编写的青春版
搞不懂这道题在干嘛,我也不知道我在写什么,因为根本不了解前缀树是个什么玩意,但是AC了。
class Trie:
def __init__(self):
self.tire = []
def insert(self, word: str) -> None:
self.tire.append(word)
def search(self, word: str) -> bool:
if word in self.tire:
return True
return False
def startsWith(self, prefix: str) -> bool:
n = len(prefix)
for i in self.tire :
if len(i)>= n :
if i[:n]==prefix:
return True
return False
重点
先放一篇博客吧,讲前缀树的,讲的很好。
前缀树博客讲解
大致的思路如下:
class Trie:
def __init__(self):
# For empty string `""`
self.trie = {'flag': True}
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
node = self.trie
for char in word:
if char in node:
node = node[char]
else:
node[char] = {}
node = node[char]
# Indentify whether path is a word
node['flag'] = True
def search(self, word: str) -> bool:
"""
Returns if the word is in the trie.
"""
node = self.trie
for char in word:
if char in node: node = node[char]
else: return False
return node.get('flag', False)
def startsWith(self, prefix: str) -> bool:
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
node = self.trie
for char in prefix:
if char in node: node = node[char]
else: return False
return True