当前位置:   article > 正文

深度优先(DFS)与广度优先(BFS)附Python代码与具体应用_深度优先搜索的时间复杂度

深度优先搜索的时间复杂度

深度优先搜索(DFS)和广度优先搜索(BFS)是两种常用的图搜索算法,用于在图或树等数据结构中查找特定节点或遍历整个结构。它们在解决许多问题时都非常有用,包括路径查找、连通性检测和图形遍历等。

深度优先搜索(DFS):

  • 工作原理: 在深度优先搜索中,我们从起始节点开始沿着一条路径尽可能深地探索,直到到达最远的节点,然后返回并探索其他的分支。换句话说,DFS 会尽可能深地搜索当前路径,直到没有更多的未探索节点为止。

  • 算法实现: 通常使用递归或栈来实现 DFS。递归的方法将在堆栈中维护当前路径,而非递归方法则需要使用显式的栈数据结构来跟踪节点。

  • 特点: DFS 通常需要较少的内存空间,因为它只需要存储当前路径上的节点。但它可能会陷入无限循环,特别是在图中存在环路时。

  • 应用: DFS 适用于寻找一条路径、深度优先遍历树或图,以及解决具有递归结构的问题。

  • 时间复杂度: 在最坏情况下,DFS 需要遍历整个搜索空间,其时间复杂度取决于节点数量和边的数量。对于树来说,时间复杂度通常为 O(b^d) ,其中 b是每一层平均节点的数量,d 是树的层数。

  • 空间复杂度: 在 DFS 中,需要存储当前路径上的节点,因此空间复杂度取决于树的深度。在最坏情况下,当搜索到树的最大深度时,空间复杂度为 O(bd)迭代或者O(d)递归,b是平均节点数目,其中 d 是树的深度((b-1)*d)->bd)。对于图,由于可能存在环路,空间复杂度可能更高,取决于搜索过程中所访问的节点数量。

  • 以下是递归实现的实例代码:

    1. def dfs(node, visited):
    2. if node not in visited:
    3. visited.add(node)
    4. # 访问节点的邻居
    5. for neighbor in node.neighbors:
    6. dfs(neighbor, visited)
    7. # 定义节点类
    8. class Node:
    9. def __init__(self, val):
    10. self.val = val
    11. self.neighbors = []
    12. # 创建树的节点
    13. node1 = Node(1)
    14. node2 = Node(2)
    15. node3 = Node(3)
    16. # 构建树的连接关系
    17. node1.neighbors.append(node2)
    18. node1.neighbors.append(node3)
    19. node2.neighbors.append(node1)
    20. node3.neighbors.append(node1)
    21. # 初始化已访问节点集合
    22. visited = set()
    23. # 从根节点开始进行深度优先搜索
    24. dfs(node1, visited)
    25. print("深度优先搜索结果:", [node.val for node in visited])

迭代实现:

  1. def dfs(root):
  2. visited = set()
  3. stack = [root]
  4. while stack:
  5. node = stack.pop()
  6. if node not in visited:
  7. visited.add(node)
  8. # 访问节点的邻居
  9. for neighbor in node.neighbors:
  10. if neighbor not in visited:
  11. stack.append(neighbor)
  12. # 定义节点类
  13. class Node:
  14. def __init__(self, val):
  15. self.val = val
  16. self.neighbors = []
  17. # 创建树的节点
  18. node1 = Node(1)
  19. node2 = Node(2)
  20. node3 = Node(3)
  21. # 构建树的连接关系
  22. node1.neighbors.append(node2)
  23. node1.neighbors.append(node3)
  24. node2.neighbors.append(node1)
  25. node3.neighbors.append(node1)
  26. # 从根节点开始进行深度优先搜索
  27. dfs(node1)
  28. print("深度优先搜索结果:", [node.val for node in visited])

广度优先搜索(BFS):

  • 工作原理: 在广度优先搜索中,我们从起始节点开始,逐层地探索图中的节点,先探索距离起始节点最近的节点,然后是稍远一层的节点,依次类推。换句话说,BFS 会首先探索所有与起始节点相邻的节点,然后再探索这些节点的邻居,以此类推。

  • 算法实现: BFS 通常使用队列来实现。它将从起始节点开始,将节点入队,并逐个将节点的邻居出队并入队,直到队列为空。

  • 特点: BFS 保证可以找到最短路径,因为它会先探索距离起始节点更近的节点。但它可能会占用较多的内存空间,特别是在图结构较大的情况下。

  • 应用: BFS 适用于寻找最短路径、查找最短路径、层次遍历树或图,以及解决具有固定距离或层次结构的问题。

  • 时间复杂度: 在最坏情况下,BFS 需要遍历整个搜索空间,因此其时间复杂度与节点数量和边的数量成正比。对于树或图来说, O(b^d) ,其中 b是每一层平均节点的数量,d 是树的层数。

  • 空间复杂度: 在 BFS 中,需要存储每一层的节点,并且需要将每个节点的子节点添加到队列中。在最坏情况下,如果树是满的,则每一层将包含平均 b 个节点,其中 b 是每个节点的子节点数量。因此,BFS 的空间复杂度为 O(b^d)(当前层的长度),其中 d 是树的深度。对于图,可能需要额外的空间来记录已访问的节点,空间复杂度可能更高。

  • 以下是递归实现的实例代码:

    1. from collections import deque
    2. def bfs(root):
    3. visited = set()
    4. queue = deque([root])
    5. visited.add(root)
    6. while queue:
    7. node = queue.popleft()
    8. # 访问节点的邻居
    9. for neighbor in node.neighbors:
    10. if neighbor not in visited:
    11. visited.add(neighbor)
    12. queue.append(neighbor)
    13. # 定义节点类
    14. class Node:
    15. def __init__(self, val):
    16. self.val = val
    17. self.neighbors = []
    18. # 创建树的节点
    19. node1 = Node(1)
    20. node2 = Node(2)
    21. node3 = Node(3)
    22. # 构建树的连接关系
    23. node1.neighbors.append(node2)
    24. node1.neighbors.append(node3)
    25. node2.neighbors.append(node1)
    26. node3.neighbors.append(node1)
    27. # 从根节点开始进行广度优先搜索
    28. bfs(node1)
    29. print("广度优先搜索结果:", [node.val for node in visited])

    迭代实现:

    1. from collections import deque
    2. def bfs(root):
    3. visited = set()
    4. queue = deque([root])
    5. visited.add(root)
    6. while queue:
    7. node = queue.popleft()
    8. # 访问节点的邻居
    9. for neighbor in node.neighbors:
    10. if neighbor not in visited:
    11. visited.add(neighbor)
    12. queue.append(neighbor)
    13. # 定义节点类
    14. class Node:
    15. def __init__(self, val):
    16. self.val = val
    17. self.neighbors = []
    18. # 创建树的节点
    19. node1 = Node(1)
    20. node2 = Node(2)
    21. node3 = Node(3)
    22. # 构建树的连接关系
    23. node1.neighbors.append(node2)
    24. node1.neighbors.append(node3)
    25. node2.neighbors.append(node1)
    26. node3.neighbors.append(node1)
    27. # 从根节点开始进行广度优先搜索
    28. bfs(node1)
    29. print("广度优先搜索结果:", [node.val for node in visited])

当我们谈论树搜索算法时,常常会涉及到两个重要的概念:完全性(Completeness)和最优性(Optimality)。

完全性(Completeness):

  • 完全性指的是算法是否能够保证在有限时间内找到解决方案。一个完全的搜索算法能够在有限时间内找到解(如果解存在的话)。在树搜索中,完全性意味着算法能够遍历整个搜索空间并找到解决方案。

  • 深度优先搜索(DFS)在完全性上的表现: DFS 并不保证完全性。尽管 DFS 可能会找到解决方案,但它可能会陷入无限循环,尤其是当搜索空间有环路时。因此,DFS 不是一个完全的搜索算法。

  • 广度优先搜索(BFS)在完全性上的表现: BFS 是一种完全的搜索算法。由于 BFS 会逐层地遍历整个搜索空间,因此如果解决方案存在,则 BFS 能够找到该解决方案。

最优性(Optimality):

  • 最优性指的是算法是否能够找到最佳的解决方案。一个最优的搜索算法能够找到满足特定标准的最佳解决方案。在树搜索中,通常是指找到成本最小的解决方案。

  • 深度优先搜索(DFS)在最优性上的表现: DFS 不能保证找到最优解,因为它可能会首先遍历到解决方案,但这个解决方案可能不是最优的。因为 DFS 会尽可能地深入搜索路径,而不考虑路径的成本。

  • 广度优先搜索(BFS)在最优性上的表现: BFS 是一种最优的搜索算法,因为它会首先找到最短路径,也就是成本最低的解决方案。由于 BFS 会先探索距离起始节点最近的节点,因此如果成本一样,它会优先找到最短路径。

实际案例:

在一个倒水问题中,你被给定一组水壶,每个水壶都有一个容量(以升为单位)和当前的水位(以升为单位)。目标是测量出一定量的水,它可以出现在任何一个水壶中。假设我们有三个水壶,一个容量为 5 升,另一个容量为 3 升,另一个容量为 9 升。我们的目标是测量出 7 升水。一个状态由当前水位的元组表示,可用的操作包括:

(填满,i):将第 i 个水壶全部装满(来自一个无限水源)。 (倒水,i,j):将水从第 i 个水壶倒入第 j 个水壶,直到水壶 i 为空,或者水壶 j 满了,以先到者为准。

  1. initial_state = (0,0,0)
  2. capacity = (3,5,9)
  3. goal = 7
  4. def isgoal(state):
  5. return goal in state
  6. #action:1)把瓶子装满 2)把一个瓶子中的水倒到另一个瓶子中,直到一个瓶子为空或满
  7. def get_successor(state):
  8. #用一个list来存所有successor的状态
  9. ret =[]
  10. arrlen = range(len(state))
  11. #1)三种可能性
  12. for i in arrlen :
  13. cur =[0,0,0]
  14. cur[i] = capacity[i]
  15. ret.append(tuple(cur))
  16. #2)双重循环遍历
  17. for i in arrlen:
  18. for j in arrlen :
  19. #在所有source存在的,并且target不为满,避免死循环
  20. if i != j and state[i] != 0 :
  21. #判断target的剩余空间,与source所拥有的空间
  22. num = min(state[i],(capacity[j]-state[j]))
  23. #创建list
  24. cur = list(state)
  25. for x in arrlen :
  26. if x == i:
  27. cur[x] = state[i]-num
  28. elif x==j:
  29. cur[x] = state[j]+num
  30. else:
  31. cur[x] = state[x]
  32. ret.append(tuple(cur))
  33. return ret
  34. class node:
  35. def __init__(self,state,parent=None):
  36. #state是个tuple
  37. self.state = state
  38. self.parent= parent
  39. from collections import deque
  40. #用深度优先无法完成任务
  41. def BFS(state):
  42. #用一个队列来存节点,规定一下,从右边进,从左边出
  43. stack = deque()
  44. stack.append(node(state))#DFS
  45. while stack:
  46. #取出栈顶元素
  47. curnode = stack.popleft()
  48. if isgoal(curnode.state) :
  49. return curnode
  50. for successor in get_successor(curnode.state):
  51. stack.append(node(successor,curnode))
  52. return None
  53. def getPath(node):
  54. ret = []
  55. #存状态
  56. while node:
  57. ret.append(node.state)
  58. node = node.parent
  59. ret.reverse()
  60. return ret
  61. node = BFS(initial_state)
  62. target = getPath(node)
  63. print(target)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/509385
推荐阅读
相关标签
  

闽ICP备14008679号