既然图算法的核心是图的BFS搜索。所以必须提前学习BFS搜索。
首先为什么BFS搜索是最短路径?
假设起点为S,终点为T。我们可以把图想象为一个个同心圆。直接与S相连的点,组成了一个以S为圆心,半径为1的圆。称之为集合
C
1
C_1
C1,以
C
1
C_1
C1直接相连的,组成了一个以S为圆心,半径为2的圆。称之为集合
C
2
C_2
C2,因为宽度搜索是按层进行的,那么顺序肯定是
S
→
C
1
→
C
2
→
…
…
C
N
S \rarr C_1 \rarr C_2 \rarr ……C_N
S→C1→C2→……CN,T就在
C
N
C_N
CN中。
图的BFS搜索与树类似。因为图会存在环,为了避免无限循环,需要用一个hash set来&存储已访问过的数据。而找到之后,要回溯路径,需要记录上一个节点,这个时候就需要一个hashmap key为节点,value为parent节点。以下是图的BFS搜索代码:
public List<E> bfs(int first, int finalState) {
// 宽度搜索。直接一个Deque
HashMap<Integer,Integer> parentMap = new HashMap<>();
HashSet<Integer> visited = new HashSet<>();
Deque<Integer> deque = new LinkedList<>();
deque.offer(first);
Integer e = null;
while (!Objects.equals(e, finalState) && !deque.isEmpty()) {
e = deque.pollFirst();
visited.add(e);
List<? extends Edge> edgeList = this.edges[e];
for (Edge edge : edgeList) {
// 这里决定parent
if (!visited.contains(edge.getTo())) {
parentMap.put(edge.getTo(), edge.getFrom());
deque.offerLast(edge.getTo());
}
}
}
List<E> path = new ArrayList<>();
if (Objects.equals(e, finalState)) {
for (Integer i = e; i != null; i = parentMap.get(i)) {
Vertex<E> vertex = vertices[i];
path.add(vertex.getValue());
}
}
Collections.reverse(path);
return path;
}
python代码:
def bfs(self, start, end, visitor):
parent_map = {}
# 因为python 没有hashset,所以以字典代替
visited = {}
queue = []
queue.append(start)
e = start
while e != end and len(queue) != 0:
e = queue.pop(0)
visitor(self.__vertices[e])
visited[e] = 1
# 把点连接的点全部放入队列中
for child in self.__edges[e]:
if child not in visited:
parent_map[child] = e
queue.append(child)
# 没有找到
if e != end:
return []
# 找到之后组装路径
path = [end]
while end != start:
# 不停地找parent
end = parent_map[end]
path.append(end)
path.reverse()
return path