著名的七桥问题,为我们带来了“图”的概念。
目录
一、搜索
目标:给定起点,遍历图中所有顶点。
方法:BFS、DFS
典型应用:web crawling
伪码示例:
BFS(G, s)
// B=所有边界点构成的集合
B.EnQueue(s);
WHILE B非空
d=B.DeQueue();
将d标记为“已探索”;
FOR d的所有邻居t
IF t的标记为“未探索”
B.EnQueue(t);
ENDFOR
ENDWHILE
DFS(G, s)
// B=所有边界点构成的集合
B.Push(s);
WHILE B非空
d=B.Pop();
将d标记为“已探索”;
FOR d的所有邻居t
IF t的标记为“未探索”
B.Push(t);
ENDFOR
ENDWHILE
应用:
求有向图的强连通分量(Strongly Connected Component, SCC)
#TWO-PASS算法(Kosaraju算法)
TWO-PASS(G)
G中有向边方向取反→G^rev^
Loop-DFS(G^rev^),记录每个顶点的完成时间f(v).
按f(v)的降序,在G中运行Loop-DFS,记录每个顶点的Leader l(v).
具有相同leader的顶点属于同一个SCC
Loop-DFS( G )
global t=0
global s=NULL
FOR i=1 to n
IF i的标记为“未探索”
s = i;
DFS(G, i);
ENDFOR
求给定两点间所有路径
python实现
f = open('test-6.txt', 'r')
Graph = {
}
edge_num = int(f.readline())
node_num = int(f.readline())
#文件读取
# edge_num = int(input("图的边数:"))
# node_num = int(input("图的点数:"))
# print("请输入%d行链路:" % edge_num)
#手动输入
while edge_num > 0:
edge_num = edge_num - 1
s = f.readline()
node1, node2 = s.split()
# node1, node2 = input().split()
if (node1 not in Graph):
Graph[node1] = []#读入点
Graph[node1].append(node2)#读入有向边
if (node2 not in Graph):
Graph[node2] = []
Graph[node2].append(node1)
#用读入的数据生成图
Path = []#DFS路径点
num = 0#total paths num
def DFS(Graph, start, end):
Path.append(start)
while (start in Path):
for i in Graph[start]:
if i not in Path:
if i == end:
Path.append(i)
global num
num = num + 1
Path.pop()
else:
DFS(Graph, i, end)
Path.pop()
if (node_num == len(Graph)):
start = f.readline()
start = start.replace(start[-1], '')
end = f.readline()
print("从" + start + "到" + end + "的所有路径为:")
DFS(Graph, start, end)
print("total paths num:", num)
else:
print("图的点数与输入路径不符!")
引申:
求解思路:BFS-分支定界;DFS-回溯
它们是求解问题的两种基本范型。
看累了来吸会儿猫(
二、贪心
思想:求局部最佳解,“希望”最终得到的是全局最佳解。
最小生成树
目标:以最小代价连接所有点。
方法:Prim、Kruskal
#基于堆的Prim实现
from heapq import heappush, heapify, heappop
class WeightedGraph(object):
def __init__(self):
self.edges = []
self.adj = {
} # 邻接边集
self.edge_num = 0
self.node_num = 0
def generate_graph(self, node1, node2, w):
self.adj.setdefault(node1, [(w, node1, node2)])
self.adj.setdefault(node2, [(w, node2, node1)])
if (w, node1, node2) not in self.adj[node1]:
self.adj[node1].append((w, node1, node2))
if (w, node2, node1) not in self.adj[node2]:
self.adj[node2].append((w, node2, node1))
self.edges.append((w, node1, node2))
class MST(object):
def __init__(self):
self.shape = []
self.weight = 0
def initialize_graph(): # 根据输入数据初始化图
graph = WeightedGraph()
f = open('test-3.txt', 'r')
graph.edge_num = int(f.readline())
graph.node_num = int(f.readline())
for line in f.readlines():
node1, node2, w = line.split()
graph.generate_graph(node1, node2, int(w))
return graph
def prim(Graph):
T = MST() # 初始化最小树
start = Graph.edges[0][1] # 起始点
S = list(start) # 树上顶点集合
cut_edges = Graph.adj[start] # 边界边集合
heapify(cut_edges) # 边界边建堆
while len(S) < Graph.node_num: # 直到生成树包含所有点
min_edge = heappop(cut_edges)
w, node1, node2 = min_edge
if node2 not in S:
T.shape.append(min_edge)
T.weight = T.weight + w
S.append(node2)
for adj_edge in Graph.adj[node2]: # 将与node2相邻的边界边加入堆
if adj_edge[2] not in S:
heappush(cut_edges, adj_edge)
return T
def main():
graph = initialize_graph()
T = prim(graph)
for edge in T.shape:
print('(' + edge[1] + ',' + edge[2] + ')')
print("Weight Sum:", T.weight)
if __name__ == "__main__":
main()
#基于并查集的Kruskal实现
class WeightedGraph(object):
def __init__(self):
self.edges = []
self.leader_set = {
}
self.leader = {
}
self.edge_num = 0
self.node_num = 0
def generate_graph(self, node1, node2, w):
self.edges.append((w, node