搜索(todo)
BFS
3. 最短单词路径
- Word Ladder (Medium)
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
题目描述:找出一条从 beginWord 到 endWord 的最短路径,每次移动规定为改变一个字符,并且改变之后的字符串必须在 wordList 中。
基础解法(词表长度太长的时候会超时)
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
wordList.append(beginWord)
n = len(wordList)
start = n-1
end = 0
while end < n and wordList[end] != endWord:
end += 1
if end == n:
return 0
graphic = self.buildGraphic(wordList)
return self.getShortestPath(graphic, start, end)
def buildGraphic(self, wordList):
n = len(wordList)
graphic = [[]]*n
for i in range(n):
cur = []
for j in range(n):
if self.isConnect(wordList[i], wordList[j]):
cur.append(j)
graphic[i] = cur
return graphic
def isConnect(self, s1, s2):
diffCnt = 0
for i in range(len(s1)):
if diffCnt > 1:
return False
if s1[i] != s2[i]:
diffCnt += 1
return diffCnt == 1
def getShortestPath(self, graphic, start, end):
queue = collections.deque()
queue.append(start)
marked = [False]*len(graphic)
path = 1
while len(queue) > 0:
size = len(queue)
path+=1
for j in range(size-1, -1, -1):
cur = queue.popleft()
for idx in graphic[cur]:
if idx == end:
return path
if marked[idx]:
continue
marked[idx] = True
queue.append(idx)
return 0
优化解法
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
def addWord(word: str):
if word not in word2id:
nonlocal nodeNum
word2id[word] = nodeNum
nodeNum += 1
def addEdge(word: str):
addWord(word)
id1 = word2id[word]
chars = list(word)
for i in range(len(chars)):
tmp = chars[i]
chars[i] = "*"
newWord = "".join(chars)
addWord(newWord)
id2 = word2id[newWord]
edges[id2].append(id1)
edges[id1].append(id2)
chars[i] = tmp
word2id = {}
edges = collections.defaultdict(list)
nodeNum = 0
for word in wordList:
addEdge(word)
addEdge(beginWord)
if endWord not in wordList:
return 0
beginId, endId = word2id[beginWord], word2id[endWord]
queue = collections.deque()
queue.append(beginId)
dis = [float('inf')]*nodeNum
dis[beginId] = 0
while queue:
x = queue.popleft()
print(x)
if x == endId:
return dis[x]//2+1
for i in edges[x]:
if dis[i] == float('inf'):
dis[i] = dis[x] +1
queue.append(i)
return 0
DFS
广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。
而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。
从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 可达性 问题。
在程序实现 DFS 时需要考虑以下问题:
- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。
1. 最大连通面积
695. Max Area of Island (Medium)
class Solution:
def dfs(self, grid, cur_i, cur_j) -> int:
if cur_i < 0 or cur_j < 0 or cur_i == len(grid) or cur_j == len(grid[0]) or grid[cur_i][cur_j] != 1:
return 0
grid[cur_i][cur_j] = 0
ans = 1
for di, dj in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
next_i, next_j = cur_i + di, cur_j + dj
ans += self.dfs(grid, next_i, next_j)
return ans
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
ans = 0
for i, l in enumerate(grid):
for j, n in enumerate(l):
ans = max(self.dfs(grid, i, j), ans)
return ans
2. 矩阵中的连通分量
- Number of Islands (Medium)
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
res = 0
for i in range(0, len(grid)):
for j in range(0, len(grid[0])):
if grid[i][j] == '1':
res +=1
self.infect(grid, i, j)
return res
def infect(self, grid, i, j):
if i< 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] != '1':
return
grid[i][j] = '2'
self.infect(grid, i-1, j)
self.infect(grid, i+1, j)
self.infect(grid, i, j-1)
self.infect(grid, i, j+1)
Backtracking
在矩阵中寻找字符串
- Word Search (Medium)
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
def check(i: int, j: int, k: int, word_len: int) -> bool:
if board[i][j] != word[k]:
return False
if k == word_len:
return True
visited.add((i, j))
result = False
for di, dj in directions:
newi, newj = i + di, j + dj
if 0 <= newi < h and 0 <= newj <w:
if (newi, newj) not in visited:
if check(newi, newj, k + 1, word_len):
result = True
break
visited.remove((i, j))
return result
h, w = len(board), len(board[0])
n = len(word)-1
visited = set()
for i in range(h):
for j in range(w):
if check(i, j, 0, n):
return True
return False
5. 全排列
- Permutations (Medium)
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
res = []
path = []
self.dfs(nums, path, res)
return res
def dfs(self, nums, path, res):
if len(path) == len(nums):
res.append(path[:])
for num in nums:
if num in path:
continue
path.append(num)
self.dfs(nums, path, res)
path.remove(num)
6. 含有相同元素全排列
- Permutations II (Medium)
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
visited = [0 for i in nums]
nums.sort()
res = []
path = []
self.dfs(nums, path, res, visited)
return res
def dfs(self, nums, path, res, visited):
if len(path) == len(nums):
res.append(path)
for i in range(len(nums)):
if visited[i] == 1:
continue
if i > 0 and nums[i - 1] == nums[i] and visited[i - 1] == 0:
continue
# path.append(nums[i])
visited[i] = 1
self.dfs(nums, path+[nums[i]], res, visited)
visited[i] = 0
# path.pop()
# def permuteUnique(self, nums: List[int]) -> List[List[int]]:
# res = []
# path = []
# self.dfs(nums, path, res, 0, len(nums))
# return res
# def dfs(self, nums, path, res, start, k):
# if len(path) == k:
# if path not in res:
# res.append(path[:])
# for i in range(start, k):
# # if nums[i] == nums[start]:
# # continue
# path.append(nums[i])
# nums[i], nums[start] = nums[start], nums[i]
# self.dfs(nums, path, res, start + 1, k)
# nums[i], nums[start] = nums[start], nums[i]
# path.pop()
7. 组合
- 组合
题目描述:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
if k<=0 or k > n:
return []
res = []
path = []
self.dfs(n, k, 1, path, res)
return res
def dfs(self, n, k, start, path, res):
if len(path) == k:
res.append(path[:])
return
for i in range(start, n+1):
path.append(i)
self.dfs(n, k, i+1, path, res)
path.pop()
8.组合求和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
path = []
candidates.sort()
self.dfs(candidates, path, res, target)
return res
def dfs(self, candidates, path, res, target):
if target < 0:
return
if target == 0:
# res.append(path[:])
cur = path[:]
cur.sort()
if cur not in res:
res.append(cur)
for i in range(len(candidates)):
cur = candidates[i]
if cur > target:
return
if i > 0 and cur == candidates[i-1]:
continue
path.append(cur)
self.dfs(candidates, path, res, target-cur)
path.pop()
9.含有相同元素的组合求和
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
path = []
candidates.sort()
self.dfs(target, path, res, candidates, 0)
if sum(candidates) < target:
return []
return res
def dfs(self, target, path, res, candidates, start):
if target < 0:
return
if target == 0:
cur = path[:]
cur.sort()
if cur not in res:
res.append(cur)
for i in range(start, len(candidates)):
if candidates[i] > target:
return
if i > start and candidates[i] == candidates[i-1]:
continue
path.append(candidates[i])
self.dfs(target-candidates[i], path, res, candidates, i+1)
path.pop()
最短路径(todo)
MAX= float('inf')
matrix = [
[0,10,MAX,4,MAX,MAX],
[10,0,8,2,6,MAX],
[MAX,8,10,15,1,5],
[4,2,15,0,6,MAX],
[MAX,6,1,6,0,12],
[MAX,MAX,5,MAX,12,0]
]
def dijkstra(matrix, start_node):
#矩阵一维数组的长度,即节点的个数
matrix_length = len(matrix)
#访问过的节点数组
used_node = [False] * matrix_length
#最短路径距离数组
distance = [MAX] * matrix_length
#初始化,将起始节点的最短路径修改成0
distance[start_node] = 0
#将访问节点中未访问的个数作为循环值,其实也可以用个点长度代替。
while used_node.count(False):
min_value = float('inf')
min_value_index = 999
#在最短路径节点中找到最小值,已经访问过的不在参与循环。
#得到最小值下标,每循环一次肯定有一个最小值
for index in range(matrix_length):
if not used_node[index] and distance[index] < min_value:
min_value = distance[index]
min_value_index = index
#将访问节点数组对应的值修改成True,标志其已经访问过了
used_node[min_value_index] = True
#更新distance数组。
#以B点为例:distance[x] 起始点达到B点的距离,
#distance[min_value_index] + matrix[min_value_index][index] 是起始点经过某点达到B点的距离,比较两个值,取较小的那个。
for index in range(matrix_length):
distance[index] = min(distance[index], distance[min_value_index] + matrix[min_value_index][index])
return distance
start_node = int(input('请输入起始节点:'))
result = dijkstra(matrix,start_node)
print('起始节点到其他点距离:%s' % result)