(十五)图的广度优先遍历BFS及最短路径
类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点。
具体算法表述如下:
- 访问初始结点v并标记结点v为已访问。
- 结点v入队列
- 当队列非空时,继续执行,否则算法结束。
- 出队列,取得队头结点u。
- 查找结点u的第一个邻接结点w。
- 若结点u的邻接结点w不存在,则转到步骤3;否则循环执行以下三个步骤:
1). 若结点w尚未被访问,则访问结点w并标记为已访问。
2). 结点w入队列
3). 查找结点u的继w邻接结点后的下一个邻接结点w,转到步骤6。
如下图,其广度优先算法的遍历顺序为:1->2->3->4->5->6->7->8
public class a {
private Map<String, List<String>> graph = new HashMap<String, List<String>>();
/**
* 初始化图数据:使用邻居表来表示图数据。
*/
public void initGraphData() {
// 图结构如下
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
// \ | / \ /
// 8 9
graph.put("1", Arrays.asList("2", "3")); //public static <T> List<T> asList(T... a) 例:List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
graph.put("2", Arrays.asList("1", "4", "5"));
graph.put("3", Arrays.asList("1", "6", "7"));
graph.put("4", Arrays.asList("2", "8"));
graph.put("5", Arrays.asList("2", "8"));
graph.put("6", Arrays.asList("3", "8", "9"));
graph.put("7", Arrays.asList("3", "9"));
graph.put("8", Arrays.asList("4", "5", "6"));
graph.put("9", Arrays.asList("6", "7"));
}
/**
* 宽度优先搜索(BFS, Breadth First Search)
* BFS使用队列(queue)来实施算法过程
*/
/*
Queue是个接口 LinkedList:是实现类 是一个双向链表
链表的优势:
①随机访问效率低:对数据的索引需要从链表头开始遍历()
②插入元素:无需对数据移动
linkedList:特有的方法 getFirst()、addFirst;getLast()、addLast();removeFirst()、removeLast()
*/
private Queue<String> queue = new LinkedList<String>();
//因为序列是不带重复元素的,所以可以用map来标记是否访问过
private Map<String, Boolean> status = new HashMap<String, Boolean>();
/**
* 开始点
*
* @param startPoint
*/
public void BFSSearch(String startPoint) {
//1.把起始点放入queue;
queue.add(startPoint);
status.put(startPoint, false);
bfsLoop();
}
private void bfsLoop() {
// 1) 从queue中取出队列头的点;更新状态为已经遍历。
String currentQueueHeader = queue.poll(); //出队 poll:获取并移除
status.put(currentQueueHeader, true);
System.out.println(currentQueueHeader);
// 2) 找出与此点邻接的且尚未遍历的点,进行标记,然后全部放入queue中。
List<String> neighborPoints = graph.get(currentQueueHeader);
for (String poinit : neighborPoints) {
//getOrDefault(Object key, V defaultValue) Returns the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key.
if (!status.getOrDefault(poinit, false)) { //未被遍历
if (queue.contains(poinit)) continue;
queue.add(poinit);
status.put(poinit, false);
}
}
if (!queue.isEmpty()) { //如果队列不为空继续遍历
bfsLoop();
}
}
public static void main(String [] args)
{
a test = new a();
test.initGraphData();
// test.BFSSearch("1");
test.BFSSearch("2");
}
广度优先遍历找最短路径
只要将bfsloop简单修改即可
每个节点node的distance都是node距离起始点start的最短距离.
private void bfsLoop() {
LinkedList<Integer> distq = new LinkedList<Integer>();
distq.push(0);
while(!queue.isEmpty())
{
String currentQueueHeader = queue.poll(); //出队 poll:获取并移除
status.put(currentQueueHeader, true);
System.out.println("当前节点:"+currentQueueHeader);
List<String> neighborPoints = graph.get(currentQueueHeader);
int d=distq.poll();
for (String poinit : neighborPoints) {
distq.push(d+1);//说明要访问邻接的顶点
if (!status.getOrDefault(poinit, false)) {
System.out.println("1--"+poinit+"的距离为"+distq.peek());
if (queue.contains(poinit)) continue;
queue.add(poinit);
status.put(poinit, false);
}
}
}
}