图(一)| BFS与DFS算法 - Python实现

本文介绍了图的基础概念,包括无环无向图、三种图的表示方法:邻接矩阵、关联矩阵和邻接表。重点讲解了Python中实现广度优先搜索(BFS)和深度优先搜索(DFS)的算法,包括相关数据结构如栈和队列的实现,以及使用邻接表描述图。此外,还展示了BFS在求解最短路径问题上的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 基础概念:

图的数学表述为 G = ( V , E ) G=(V,E) G=(V,E),即图是由一组顶点和一组边构成,例如:

在这里插入图片描述
相关概念如下:

  • 相邻节点;
  • 度:相邻节点的个数;
  • 路径:从某个节点到另一个节点的连续序列;
  • 简单路径:无重复节点的路径;
  • 无环的
  • 连通的:每两个节点之间都存在路径;
  • 无向的
  • 有向的;
  • 加权的;
  • 未加权的
  • 强连通的:对于有向图,每两个顶点之间都存在双向的路径;

例如上面的例子中展现的就是一张无环的、无向的、未加权的、连通的图,这样的图里任意两个节点间都存在简单路径。


* 图的三种表示方法:

1. 邻接矩阵:

a r r a y [ i ] [ j ] = { 1 , i 与 j 为 相 邻 顶 点 ; 0 , i 与 j 不 相 邻 ; array[i][j] = \begin{cases} 1, & i与j为相邻顶点; \\ 0, & i与j不相邻; \end{cases} array[i][j]={ 1,0,ijij

容易得到对于邻接矩阵 M M M M T = M M^T = M MT=M

2. 关联矩阵:

a r r a y [ v ] [ e ] = { 1 , v 是 e 的 入 射 点 ; 0 , v 不 是 e 的 入 射 点 ; array[v][e] = \begin{cases} 1, & v是e的入射点; \\ 0, & v不是e的入射点; \end{cases} array[v][e]={ 1,0,veve

对于有向图可稍作修改,例如
在这里插入图片描述

3. 邻接表:

邻接表最简单的描述方式是使用字典,以某顶点为键,以该顶点的相邻顶点为值即可。例如最开始的例子中的图可以表示为一个Python字典:

{
   
	A:[B,C,D],
	B:[A,E,F],
	C:[A,G],
	D:[A,H,I,J],	
	E:[B,K,L],
	F:[B],
	G:[C],
	H:[D,M],
	I:[D],
	J:[D],
	K:[E],
	L:[E],	
	M:[H]
}

后面我们将采用这种方式实现图,进而使用BFS与DFS算法遍历图中的节点。


二. 广度优先搜索BFS:

广度优先搜索是一种对图进行遍历的算法,其遍历思想是“先宽后深”,优先访问同一层的节点;而深度优先搜索的遍历思想则是“先深后宽”,从指定顶点开始,沿着某条路径直到这条路径的最后一个节点,再原路退回,探索下一条路径。

对于这两种算法,我们其实只需要将队列应用到BFS中、将栈应用到DFS中,即可非常相似的实现两种算法。这一点我们后续可以更清楚的看到。那么首先让我们实现简单的栈和队列:

1. 实现栈和队列:

栈和队列的构建都很简单,我们使用Python提供的列表存储数据,然后遵守相应的“先进后出”、“先进先出”原则定义入栈/出栈、入队/出队的方法即可。最后,我们需要一个方法动态获取栈/队列的长度,将size定义为方法而不是属性可以简化代码、避免手动更新size属性。

代码如下:

class MyQueue(object):
	'''
	构建队列
	'''

	def __init__(self):
		self.myQueue = []

	def push(self,item):

		self.myQueue.append(item)
		return self.myQueue

	def pop(self):

		return self.myQueue.pop(0)

	def size(self):
		'''
		将队列的大小动态定义为方法,其它方法中无需对其进行显示更新
		'''
		return len(self.myQueue)

class MyStack(object):
	'''
	构建栈
	'''

	def __init__(self):
		self.myStack = []

	def push(self,item):

		self.myStack.append(item)
		return self.myStack

	def pop(self):

		return self.myStack.pop(len(self.myStack) - 1)
### 关于DFSBFSPython实现 #### 1. 广度优先搜索(BFS) 广度优先搜索是种基于队列的数据结构实现算法。它按照层次顺序访问中的节点,通常从根节点开始,逐层扩展到其邻居节点。以下是其实现的核心逻辑: - 使用个队列来保存待访问的节点。 - 初始时将起始节点加入队列。 - 循环执行以下操作直到队列为空: - 取出队首节点并标记为已访问。 - 将该节点的所有未访问邻居依次加入队列。 下面是基于邻接表存储方式的个简单实现[^1]: ```python from collections import deque def bfs(graph, start): visited = set() queue = deque([start]) result = [] while queue: node = queue.popleft() # FIFO原则 if node not in visited: visited.add(node) result.append(node) neighbors = graph[node] for neighbor in neighbors: if neighbor not in visited: queue.append(neighbor) return result ``` 时间复杂度为 \(O(V + E)\),其中 \(V\) 是顶点数,\(E\) 是边数;空间复杂度为 \(O(V)\)[^3]。 --- #### 2. 深度优先搜索(DFS) 深度优先搜索则依赖栈数据结构或者递归来完成。它的特点是尽可能深地探索某个分支后再回溯处理其他分支。具体来说: - 如果使用显式的栈,则每次从未访问的邻居中选取个压入栈。 - 若采用递归方法,则通过函数调用堆栈自然形成隐式栈行为。 下面分别给出两种形式下的代码样例[^2]: ##### 基于栈的方式: ```python def dfs_stack(graph, start): visited = set() stack = [start] result = [] while stack: node = stack.pop() # LIFO原则 if node not in visited: visited.add(node) result.append(node) neighbors = reversed(graph[node]) # 确保递归版本致的结果顺序 for neighbor in neighbors: if neighbor not in visited: stack.append(neighbor) return result ``` ##### 运用递归的形式: ```python def dfs_recursive(graph, start, visited=None): if visited is None: visited = [] if start not in visited: visited.append(start) for neighbor in graph[start]: if neighbor not in visited: dfs_recursive(graph, neighbor, visited) return visited ``` 同样,在邻接表表示下运行效率较高,达到线性的性能指标即 \(T=O(V+E)\); 而对于密集型网络如由邻接矩阵描述的情形会退化至平方级别运算量 \(T=O(|V|^2)\), 同时额外消耗约等于结点总数的空间资源用于记录状态变化轨迹. --- ### 性能分析对比总结 | 特性 | BFS | DFS | |--------------|------------------------------|-----------------------------| | **核心结构** | Queue | Stack / Recursion | | **适用场景** | 寻找最短路径等问题 | 探索连通性和拓扑排序 | | **时间复杂度** | \(O(V + E)\) | \(O(V + E)\) | | **空间复杂度** | \(O(V)\) | \(O(V)\) | 以上表格展示了两者的主要差异以及各自的优劣之处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值