搜索(todo)

leetcode题解: 搜索

BFS

3. 最短单词路径

  1. 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. 矩阵中的连通分量

  1. 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

在矩阵中寻找字符串

  1. 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. 全排列

  1. 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. 含有相同元素全排列

  1. 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. 组合

  1. 组合
    题目描述:给定两个整数 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.组合求和

39. 组合总和

给你一个 无重复元素 的整数数组 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.含有相同元素的组合求和

40. 组合总和 II

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)

posted @ 2021-12-20 10:59  _无支祁  阅读(64)  评论(0编辑  收藏  举报