查找最短路径
广度优先搜索可回答两类问题:
第一类问题:从节点A出发,有前往节点B的路径吗?
第二类问题:从节点A出发,前往节点B的哪条路径最短?
数据结构
- 队列:队列是一种先进先出(First In First Out,FIFO)的数据结构,如果你将两个元素加入队列,先加入的元素将在后加入的元素之前出队。
- 图:图(有向图)由节点和边组成。一个节点可能与众多节点直接相连,这些节点被称为邻居。我们可以用一种结构表示这种关系,它就是散列表。
人际关系图
代码实现
Map<String, List<String>> graph = new HashMap<>();
graph.put("我", Arrays.asList("孙", "吴", "李"));
graph.put("孙", Arrays.asList("郑", "周"));
graph.put("吴", Arrays.asList("周"));
graph.put("李", Arrays.asList("钱", "赵"));
graph.put("郑", Arrays.asList());
graph.put("周", Arrays.asList());
graph.put("钱", Arrays.asList());
graph.put("赵", Arrays.asList());
Queue<String> searchQueue = new LinkedList<>(); //创建一个队列
List<String> searched = new ArrayList<>(); //这个列表用于记录检查过的人
searchQueue.addAll(graph.get("我")); //将我的朋友都加入到搜索队列中
boolean flag = false;
//寻找周
while (!searchQueue.isEmpty()) { //只要队列不为空
String person = searchQueue.poll(); //就取出其中的第一个人
if (!searched.contains(person) && "周".equals(person)) { //仅当这个人没检查过时才检查这个人是否是周
flag = true; //是周
break;
}
searchQueue.addAll(graph.get(person)); //不是周。将这个人的朋友都加入搜索队列
searched.add(person); //将这个人标记为检查过
}
if (flag) {
System.out.println("我找到周啦!");
} else {
System.out.println("没有找到周!");
}
步骤分析
步骤 | 描述 | 结果 | searchQueue | searched |
---|---|---|---|---|
1 | 将我的朋友都加入到搜索队列中 | “孙”, “吴”, “李” | ||
2 | “孙”出队并检查”孙” | “孙”不是”周”,将”孙”设为已检查,并将”孙”的朋友加入搜索队列 | “吴”, “李”, “郑”, “周” | “孙” |
3 | “吴”出队并检查”吴” | “吴”不是”周”,将”吴”设为已检查,并将”吴”的朋友加入搜索队列 | “李”, “郑”, “周”, “周” | “孙”,”吴” |
4 | “李”出队并检查”李” | “李”不是”周”,将”李”设为已检查,并将”李”的朋友加入搜索队列 | “郑”, “周”, “周”,”钱”, “赵” | “孙”,”吴”,”李” |
5 | “郑”出队并检查”郑” | “郑”不是”周”,将”郑”设为已检查,并将”郑”的朋友加入搜索队列 | “周”, “周”,”钱”, “赵” | “孙”,”吴”,”李”,”郑” |
6 | “周”出队并检查”周” | “周”是”周”,跳出循环,查询结束 | “周”,”钱”, “赵” | “孙”,”吴”,”李”,”郑” |
运行时间
如果你在你的整个人际关系网中搜索”周”,就意味着你将沿每条边前行(记住,边是从一个人到另一个人的箭头或连接),因此运行时间至少为O(边数)。你还使用了一个队列,其中包含要检查的每个人。将一个人添加到队列需要的时间是固定的,即为O(1),因此对每个人都这样做需要的总时间为O(人数)。所以,广度优先搜索的运行时间为O(人数 + 边数),这通常写作O(V + E),其中V为顶点(vertice)数,E为边数。