实验内容
针对图,根据给定的数据选择合适的存储方式(邻接矩阵和邻接表中的一种)进行存储(存储方式选择也是实验的检查内容之一),并进行图的广度优先遍历的过程。
数据集 1:使用 data.txt 中的数据,看做无向图,选择合适的方式进行存储(提示:其特征为节点数较少而边比较密集),并以 A 为起始顶点输出遍历过程。
数据集 2:twitter_small: Nodes 81306, Edges 1768149, 有向图;
twitter_large: Nodes 11316811, Edges 85331846, 有向图。
对 twitter_small,选择一种合适的存储方式存储数据,并输出 BFS 的遍历时间。twitter_large 不做要求。
提示:图可能不是连通图,且可能有重复边。
算法设计思路
数据集1
由于实验要求中已经说明,data中的图节点数较少且边比较密集所以采用邻接矩阵的存储方式,由于数据集规模较小,所以直接初始化一个矩阵对数据进行读取,若两个点之间存在边,将对应矩阵位置的值设为1即可得到邻接矩阵,然后实现BFS函数对图进行遍历即可。
数据集2
- 首先,定义一个
BFS
类,其中包含构造函数__init__
和BFS遍历方法bfs
。 - 在构造函数
__init__
中,初始化一个空的visited
集合,用于记录已访问的节点。 - 在BFS遍历方法
bfs
中,接受起始节点start_node
作为输入参数。 - 初始化一个队列
queue
,将起始节点start_node
放入队列中,并将其添加到visited
集合中。 - 当队列
queue
不为空时,执行以下循环:- 弹出队列中的第一个节点
node
。 - 遍历节点
node
的邻居节点neighbor
:- 如果
neighbor
不在visited
集合中,则将其添加到队列queue
中,并将其添加到visited
集合中。
- 如果
- 弹出队列中的第一个节点
- 在
do
方法中,接受邻接表adj_list
和所有节点集合all_nodes
作为输入参数。 - 将输入的邻接表和所有节点集合赋值给
BFS
对象的属性adj_list
和all_nodes
。 - 初始化一个空的
traversal_times
列表,用于存储每次遍历的时间。 - 对所有节点进行遍历:
- 如果节点不在
visited
集合中,则执行以下操作:- 记录遍历前的
visited
集合长度l1
。 - 记录遍历开始的时间
start_time
。 - 调用
BFS
对象的bfs
方法进行BFS遍历。 - 记录遍历结束的时间
end_time
。 - 计算本次遍历的时间
traversal_time
(即end_time - start_time
)。 - 记录遍历后的
visited
集合长度l2
。 - 将本次遍历的时间
traversal_time
添加到traversal_times
列表中。
- 记录遍历前的
- 如果节点不在
- 输出总节点数(
visited
集合的长度)和总遍历时间(traversal_times
列表中所有时间的总和)。
源码及注释
BFS_1
with open('data.txt', 'r') as file:
lines = file.readlines()
nodes = lines[0].strip().split(',')
node_index = {node: index for index, node in enumerate(nodes)}
num_nodes = len(nodes)
adj_matrix = [[0] * num_nodes for _ in range(num_nodes)]
# 构建邻接矩阵
for line in lines[1:]:
node1, node2 = line.strip().split('-')
index1 = node_index[node1]
index2 = node_index[node2]
adj_matrix[index1][index2] = 1
adj_matrix[index2][index1] = 1
# 输出邻接矩阵
for row in adj_matrix:
print(row)
from collections import deque
def bfs(graph, start_node):
visited = [False] * len(graph)
queue = deque()
# 将起始节点放入队列并标记为已访问
queue.append(start_node)
visited[start_node] = True
while queue:
node = queue.popleft()
print(nodes[node], end=' ')
# 遍历与当前节点相邻的未访问节点
for neighbor in range(len(graph[node])):
if graph[node][neighbor] == 1 and not visited[neighbor]:
queue.append(neighbor)
visited[neighbor] = True
# 以A为起始顶点进行BFS遍历
start_node = node_index['A']
bfs(adj_matrix, start_node)
BFS_2
import time
from collections import defaultdict
class BFS():
def __init__(self) -> None:
self = self
visited = set()
self.visited = visited
def bfs(self,start_node):
queue = [start_node]
self.visited.add(start_node)
while queue:
node = queue.pop(0)
# 处理当前节点
#print(node)
for neighbor in self.adj_list[node]:
if neighbor not in self.visited:
queue.append(neighbor)
self.visited.add(neighbor)
def do(self,adj_list,all_nodes):
self.adj_list = adj_list
self.all_nodes = all_nodes
# 遍历所有节点
traversal_times = []
for node in all_nodes:
if node not in self.visited:
l1 = len(self.visited)
start_time = time.time()
BFS.bfs(self,start_node=node)
end_time = time.time()
l2 = len(self.visited)
traversal_time = end_time - start_time
print("本次BFS遍历时间:", traversal_time, "秒")
print("本次遍历点数:",l2-l1)
traversal_times.append(traversal_time)
print("总节点数:", len(self.visited))
print("总遍历时间:", sum(traversal_times), "秒")
# 读取数据集文件
with open('twitter_small.txt', 'r') as file:
lines = file.readlines()
# 构建邻接表
adj_list = defaultdict(list)
all_nodes = set()
for line in lines:
node1, node2 = map(int, line.strip().split())
adj_list[node1].append(node2)
all_nodes.add(node1)
all_nodes.add(node2)
B = BFS()
B.do(adj_list,all_nodes)