一、什么是宽度优先搜索?
宽度优先搜索是图的一种遍历策略,它的思想是从一个点开始,辐射状的,一层层的,由近到远遍历 。
二、宽度优先搜索模版
Queue<Node> queue = new LinkedList();
HashMap<Node, Integer> distance = new HashMap<>();
//放入根节点
queue.offer(node);
distance.put(node, 0);//如果不需要记录距离,可以用HashSet
while(!queue.isEmpty()) {
Node node = queue.poll();
for (Node neighbor : node.neighbors) {
if (distance.containsKey(node)) {
continue;
}
distance.put(neighbor, distance.get(neighbor) + 1);
queue.offer(neighbor);
}
}
三、三种宽度优先搜索题型
1. 连通块
通过一个点,找到所有图中与之连通的点。
连通块问题都可以用宽度有限搜索模版解决,
但是对于矩阵中的连通块问题,需要用到坐标变换数组,有一个更细化的模版:
Queue<Point> queue = new LinkedList<Point>();
int[] dx = {x1, x2, ..., xn}; // (x + dx[i], y + dy[i]) 为下一个连通点
int[] dy = {y1, y2, ..., yn};
queue.offer(new Point(i, j));
while (!queue.isEmpty()) {
Point head = queue.poll();
for (int k = 0; k < n; k++) { // 访问下一个连通点
int newX = head.x + x[k];
int newY = head.y + y[k];
if(!inBound(grid, newX, newY)) { //该连通点是不是超出矩阵边界
continue;
}
if (grid[newX][newY] == true) { //是否已经访问过该点
grid[newX][newY] = false;
queue.offer(new Point(newX, newY));
}
}
}
2. 分层遍历
在连通块问题里,我们并不在意宽度有限搜索的层级。但是遇到需要得到宽度优先搜索的层数之类的与层级有关的问题,需要使用分层遍历。
分层遍历的思路是:在宽度优先搜索模版的每个while循环里加多一个循环,把当前层的node都处理掉。把下一层的node都加入queue。这样每次while都处理一层的node。
Queue<Node> queue = new LinkedList();
HashSet<Node> set = new HashSet<>();
//放入根节点
queue.offer(node);
set.add(node);
while(!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) { //多加一个for循环处理所有当前层的node
Node node = queue.poll();
for (Node neighbor : node.neighbors) {
if (set.contains(node)) {
continue;
}
set.add(neighbor);
queue.offer(neighbor);
}
//for 循环结束后,queue里装的是是所有下一层的node
//这里可以做跟层级相关的操作,比如层数+1,push这层node到array里...
...
}
3. 拓扑排序
拓扑排序得到一个图中节点的拓扑顺序。如果题目需要用到节点的拓扑顺序,可以考虑使用这个算法。
拓扑排序算法分为三步:
- 得到所有node的in-degree
- 初始化queue, 放入in-degree为0的点
- 宽度优先搜索
模版:
ArrayList<Node> topSort = new ArrayList();
//第一步,得到所有node的in-degree,储存在hashmap里
HashMap<Node, Integer> map = new HashMap();
for (Node node : graph) {
for (Node neighbor : node.neighbors) {
if (map.containsKey(neighbor)) {
map.put(neighbor, map.get(neighbor) + 1);
}
else {
map.put(neighbor, 1);
}
}
}
//第二步,初始化,把in-degree为0的点放入queue和答案集中
Queue<Node> queue = new LinkedList();
for (Node node : graph) {
if (!map.containsKey(node)) {
queue.offer(node);
topSort.add(node);
}
}
//第三步,宽度优先搜索
while (!queue.isEmpty()) {
Node head = queue.poll();
for (Node neighbor : head.neighbors) {
//每遍历到一个node,这个node的in-egree减1
map.put(neighbor, map.get(neighbor) - 1);
if (map.get(neighbor) == 0) { //in-degree为0的点放入答案集和queue中
topSort.add(neighbor);
queue.offer(neighbor);
}
}
}
四、题目
https://blog.csdn.net/Kejonho/article/details/109309787