图搜索BFS算法及存储优化
1 实验目的
- 理解图的存储方式,包括邻接矩阵和邻接表,并能够选择适当的存储方式。
- 学习并实现图的广度优先搜索算法(
BFS)。 - 通过实验测试,比较不同数据规模下的
BFS算法的性能差异。
2 实验要求
- 根据给定的数据选择合适的存储方式(邻接矩阵和邻接表中的一种)进行图的存储。
- 对图进行广度优先遍历(
BFS)。 - 对数据集1进行实验:
-
- 使用给定的数据集
data.txt,将其看作无向图。 - 根据图的特征(节点数较少而边比较密集),选择合适的存储方式。
- 以节点A为起始顶点进行广度优先遍历,输出遍历过程。
- 使用给定的数据集
- 对数据集2进行实验:
-
- 使用给定的数据集twitter_small。
- 选择一种合适的存储方式存储数据。
- 输出
BFS的遍历时间。
- 编写实验报告,包括实验内容、实验目的、算法设计思路、源码及注释、算法测试结果、实验过程中的困难和收获。
- 注意:对于数据集2的实验,可以根据实际情况选择邻接矩阵或邻接表作为存储方式,并记录
BFS的遍历时间。
3 实验内容
3.1 宽度优先搜索(BFS)
宽度优先搜索BFS算法的具体步骤如下:
- 初始化:
-
- 创建一个队列,用于存储待访问的节点。
- 创建一个标记数组,用于标记节点是否已经访问过。
- 将起始节点加入队列,并将起始节点标记为已访问。
- 进入循环,直到队列为空:
-
- 从队列中取出队首节点。
- 输出或处理该节点。
- 遍历当前节点的邻居节点:
-
- 对于邻接矩阵中的行或列,检查与当前节点相连的节点。
- 如果某个节点与当前节点相连且未被访问过,则将该节点加入队列,并将其标记为已访问。
- 重复步骤2和步骤3,直到队列为空。
以下是伪代码表示的邻接矩阵的BFS算法过程
BFS(graph, start_node):
queue = new Queue()
visited = new array of size graph.size initialized with false
queue.enqueue(start_node)
visited[start_node] = true
while queue is not empty:
current_node = queue.dequeue()
print(current_node)
for each neighbor in graph[current_node]:
if neighbor is not visited:
queue.enqueue(neighbor)
visited[neighbor] = true
3.2 邻接矩阵存储
对于数据集1,该数据集构成的图的边比较稠密,所以我们采用邻接矩阵来存储该图结构,存储及BFS过程如下:
# 使用邻接矩阵表示图
class GraphMatrix:
def __init__(self, size):
# 初始化邻接矩阵,所有元素设为0
self.adjacencyMatrix = [[0] * size for _ in range(size)]
self.nodes = []
# 添加双向边
def add_edge_matrix(self, from_, to):
self.adjacencyMatrix[from_][to] = 1
self.adjacencyMatrix[to][from_] = 1
# 添加节点
def add_node(self, node):
self.nodes.append(node)
# 广度优先搜索
def bfs_matrix(self, start_node):
# 初始化访问标记数组
visited = [False] * len(self.adjacencyMatrix)
# BFS队列
queue = []
# 标记起始节点为已访问并入队
visited[start_node] = True
queue.append(start_node)
while queue:
current_node = queue.pop(0)
print(self.nodes[current_node], end=' ')
# 遍历邻接矩阵中的每个节点
for i in range(len(self.adjacencyMatrix)):
if self.adjacencyMatrix[current_node][i] == 1 and not visited[i]:
visited[i] = True
queue.append(i)
3.3 邻接表存储
对于数据集2,图的节点数较多而边的数量相对较少,邻接表的存储方式更节省空间。而且对于大数据集来说,邻接表在存储稀疏图时比邻接矩阵更高效,在进行图的遍历操作时具有较高的效率,插入和删除节点或边的操作上具有更好的动态性能。
建立邻接表并存储图结构及BFS过程如下::
# 使用邻接表表示图
class GraphAdjacency:
def __init__(self):
self.adjacency_list = {}
# 添加边到邻接表
def add_edge_adjacency(self, from_, to):
if from_ not in self.adjacency_list:
self.adjacency_list[from_] = set()
self.adjacency_list[from_].add(to)
# BFS搜索并返回访问的节点数
def BFS_adjacency(self, start_node):
visited = set()
queue = []
queue.append(start_node)
visited.add(start_node)
cnt = 0
while queue:
current_node = queue.pop(0)
cnt += 1
if current_node in self.adjacency_list:
for neighbor in self.adjacency_list[current_node]:
if neighbor not in visited:
queue.append(neighbor)
visited.add(neighbor)
return cnt
在实验过程中我们发现,任意选取一个点可能无法遍历到有向图的所有节点,因为有的图可能只有入度没有出度,我们经过对比实验发现并找到一个结点,验证了有向图是连通的,因为我们找到了一个点,从这个点开始广度优先遍历可以一次性遍历到全部的81306个结点。这个点的编号是54226675。
4 实验结果
- 运行数据集1的函数
# 数据集1 使用邻接矩阵存储 节点少边多 稠密图
def graph_matrix_test():
graph = GraphMatrix(9) # 初始化邻接矩阵
with open('data.txt', 'r') as file:
for line in file:
from_, to = line[0], line[2]
if from_ not in graph.nodes:
graph.add_node(from_)
if to not in graph.nodes:
graph.add_node(to)
graph.add_edge_matrix(graph.nodes.index(from_), graph.nodes.index(to))
start_node = graph.nodes.index('A')
print("GraphMatrix BFS Traversal:")
graph.bfs_matrix(start_node)
print()
- 运行数据集2的函数
# 数据集2 使用邻接表存储 节点多 边少 稀疏图
def graph_adjacency_test():
from_, to = 0, 0
max_node_num = 81306
graph = GraphAdjacency()
start_time = time.time()
with open('twitter_small.txt', 'r') as file:
for line in file:
from_, to = map(int, line.split())
graph.add_edge_adjacency(from_, to)
duration_file = (time.time() - start_time) * 1000
print(f"读取文件执行时间:{duration_file} 毫秒")
start_node = 54226675
start_time = time.time()
cnt = graph.BFS_adjacency(start_node)
print(f"The number of nodes visited by BFS: {cnt}")
duration_bfs = (time.time() - start_time) * 1000
print(f"宽度优先搜索执行时间:{duration_bfs} 毫秒")
- 实验结果

5 实验总结
通过本次实验,我获得了以下收获:
- 图的存储方式选择:学会根据图的特征和需求选择适当的存储方式。对于节点较少而边比较密集的图,邻接矩阵可能更合适;对于稀疏图,邻接表则更适合。
- 广度优先搜索算法(
BFS):掌握了BFS算法的实现和应用。该算法可以按照广度优先的顺序遍历图中的节点,对于寻找最短路径等问题很有用。 - 数据集处理能力:通过处理不同规模的数据集,我提升了对大规模数据的处理能力。了解了如何选择合适的存储方式,并记录了算法执行时间以评估性能。
- 在遍历数据集2的过程中,我学到了应该通过大量实验去寻找遍历的起始节点,尽可能完全遍历到图的所有节点,有向图的遍历相对与无向图的处理稍微复杂些
合。 - 广度优先搜索算法(
BFS):掌握了BFS算法的实现和应用。该算法可以按照广度优先的顺序遍历图中的节点,对于寻找最短路径等问题很有用。 - 数据集处理能力:通过处理不同规模的数据集,我提升了对大规模数据的处理能力。了解了如何选择合适的存储方式,并记录了算法执行时间以评估性能。
- 在遍历数据集2的过程中,我学到了应该通过大量实验去寻找遍历的起始节点,尽可能完全遍历到图的所有节点,有向图的遍历相对与无向图的处理稍微复杂些
1091

被折叠的 条评论
为什么被折叠?



