图论与算法(5)图的广度优先遍历应用

news/2024/10/24 11:15:23/

1. 广度优先遍历

1.1 树的广度优先遍历

树的广度优先遍历(Breadth-First Traversal),也称为层次遍历,是一种按层次顺序逐级访问树节点的遍历方式。在广度优先遍历中,先访问树的根节点,然后按照从上到下、从左到右的顺序逐层访问树的节点。

首先将树的根节点入队列,然后循环执行以下操作:出队列一个节点,对该节点进行处理,然后将该节点的所有子节点按顺序入队列。通过不断出队列和入队列的操作,可以按照层次顺序逐级遍历树的节点,直到队列为空。

广度优先遍历保证了在访问某一层节点之前,先访问上一层的所有节点。这种遍历方式通常适用于需要按层次分析树结构的情况,比如求解最短路径、最小生成树等问题。

值得注意的是,广度优先遍历仅适用于无向树或有向无环图。对于有向有环图,由于存在环路,可能导致遍历陷入死循环。

1.2 图的广度优先遍历

图的广度优先遍历(Breadth-First Traversal),也称为宽度优先搜索(BFS),是一种遍历图的算法。在广度优先遍历中,从图中的某个起始节点开始,逐层遍历图的节点,先访问当前节点的所有邻接节点,然后再按顺序访问邻接节点的邻接节点,以此类推,直到遍历完图中所有可达节点。

下面是图的广度优先遍历的代码:

package BFS;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;/*** 图的广度优先遍历(BFS)* @author wushaopei* @create 2023-06-05 10:04*/
public class GraphBFS {private Graph G;                      // 图对象private boolean[] visited;            // 记录顶点是否访问过private List<Integer> order;          // 记录顶点的遍历顺序public GraphBFS(Graph G) {this.G = G;visited = new boolean[G.V()];order = new ArrayList<>(G.V());for (int v = 0; v < G.V(); v++) {if (!visited[v]) {bfs(v);                   // 对未访问过的顶点进行BFS}}}private void bfs(int s) {Queue<Integer> queue = new LinkedList<>();   // 创建队列用于存储待访问顶点queue.add(s);                                // 将起始顶点入队visited[s] = true;                           // 标记起始顶点为已访问while (!queue.isEmpty()) {                   // 当队列不为空时,继续遍历int v = queue.remove();                     // 出队一个顶点vorder.add(v);                             // 将v添加到遍历顺序中for (int w : G.adj(v)) {                   // 遍历v的邻接顶点wif (!visited[w]) {                     // 如果w未被访问过visited[w] = true;                 // 标记w为已访问order.add(w);                      // 将w入队}}}}public Iterable<Integer> order() {return order;                                // 返回顶点的遍历顺序}public static void main(String[] args) {Graph graph = new Graph("cc.txt");GraphBFS graphBFS = new GraphBFS(graph);System.out.println(graphBFS.order());}
}

在上述代码中,使用了一个队列来辅助实现广度优先遍历。首先将起始节点入队列,然后循环执行以下操作:出队列一个节点,对该节点进行处理,并将其标记为已访问,然后将该节点的未访问过的邻接节点按顺序入队列。通过不断出队列和入队列的操作,可以按照广度优先的顺序遍历图中的节点。

广度优先遍历的特点是从起始节点开始逐层向外扩展,先访问离起始节点最近的节点,再访问稍远的节点,直到遍历到图中所有可达节点。这种遍历方式可以用于求解最短路径、连通性问题等。

值得注意的是,在处理图的广度优先遍历时,需要使用额外的数据结构来记录已访问的节点,以防止重复访问和陷入循环。常用的数据结构有集合(Set)或标记数组等。

2. 路径问题

路径问题

如果两个顶点在同一个联通分量中,那么它们之间一定存在路径。联通分量是指图中的一组顶点,这些顶点之间可以相互连通,通过边进行路径的传递。

单源路径问题是指在给定的图中,找到从单个源顶点到其他所有顶点的路径。下面是使用广度优先搜索(DFS)来解决单源路径问题的步骤:

package BFS;/*** @author wushaopei* @create 2023-06-05 10:35*/
import java.util.*;/*** 图的广度优先遍历(BFS)* @author wushaopei* @create 2023-06-05 10:04*/
/*** 图的单源路径问题,使用广度优先遍历(BFS)求解*/
public class SingleSourcePath {private Graph G;                 // 图对象private boolean[] visited;       // 记录顶点是否访问过private int s;                   // 源顶点private int[] pre;               // 记录顶点在路径中的前一个顶点public SingleSourcePath(Graph G, int s) {this.G = G;this.s = s;visited = new boolean[G.V()];           // 初始化visited数组,默认所有顶点未访问pre = new int[G.V()];                   // 初始化pre数组,默认所有顶点前一个顶点为-1for (int v = 0; v < G.V(); v++) {pre[v] = -1;}bfs(s);                                // 从源顶点s开始进行BFS遍历}private void bfs(int s) {Queue<Integer> queue = new LinkedList<>();    // 创建队列用于存储待访问顶点queue.add(s);                                 // 将源顶点s入队visited[s] = true;                            // 标记源顶点s为已访问while (!queue.isEmpty()) {                     // 当队列不为空时,继续遍历int v = queue.poll();                       // 出队一个顶点vfor (int w : G.adj(v)) {                     // 遍历顶点v的邻接顶点wif (!visited[w]) {                       // 如果w未被访问过queue.add(w);                        // 将w入队visited[w] = true;                   // 标记w为已访问pre[w] = v;                          // 设置w在路径中的前一个顶点为v}}}}public boolean isConnectedTo(int t) {G.validateVertex(t);                      // 验证目标顶点t是否合法return visited[t];                        // 返回目标顶点t是否与源顶点s相连}public Iterable<Integer> path(int t) {List<Integer> res = new ArrayList<>();if (!isConnectedTo(t)) return res;         // 若目标顶点t与源顶点s不相连,则返回空路径int cur = t;while (cur != s) {res.add(cur);                          // 将当前顶点加入路径中cur = pre[cur];                        // 更新当前顶点为其前一个顶点}res.add(s);                                // 将源顶点s加入路径中Collections.reverse(res);                   // 反转路径列表,得到从源顶点s到目标顶点t的路径return res;                                 // 返回路径列表}public static void main(String[] args) {Graph graph = new Graph("cc.txt");SingleSourcePath singleSourcePath = new SingleSourcePath(graph, 0);System.out.println(singleSourcePath.path(6));  // 打印从源顶点0到顶点6的路径}
}

通过广度优先遍历算法,从源顶点s开始逐层遍历图中的顶点,通过队列的先进先出特性,保证了路径的最短性。在遍历过程中,记录每个顶点的前驱顶点,最终可以通过回溯路径找到从源顶点到目标顶点的路径。

3. 深度优先遍历与广度优先遍历对比

深度优先遍历:

  • 从顶点0开始,递归地深度优先遍历子顶点。
  • 遍历路径为0->1->3->2->6。
  • 当到达顶点1时,发现顶点3已经被访问过,因此回溯到顶点1的父节点0。
  • 继续遍历顶点2,然后到达顶点6。
  • 最终,遍历了顶点0、1、3、2、6。

广度优先遍历:

  • 从顶点0开始,通过队列进行广度优先遍历。
  • 遍历路径为0->1->2->3->4->6。
  • 首先遍历顶点0,然后遍历子顶点1和2。
  • 在遍历顶点1时,遍历其子顶点3和4。
  • 在遍历顶点2时,由于顶点3已经被访问过,因此跳过它。
  • 最后,遍历顶点4和6。
  • 最终,遍历了顶点0、1、2、3、4、6。

路径比较:

  • 深度优先遍历的路径为0->1->3->2->6,包含了顶点0、1、3、2、6,共计5个顶点。
  • 广度优先遍历的路径为0->2->6,包含了顶点0、2、6,共计3个顶点。

综上所述,广度优先遍历具有以下优势:

  • 广度优先遍历是一种最短路径算法,可以找到从起始顶点到目标顶点的最短路径。
  • 在示例中,广度优先遍历找到了从顶点0到顶点6的最短路径,而深度优先遍历的路径长度更长。
  • 广度优先遍历通过逐层遍历子顶点,可以保证找到的路径是最短的。

4. 最短路径长度

package BFS;/*** @author wushaopei* @create 2023-06-05 10:35*/
import java.util.*;/*** 图的广度优先遍历(BFS)* @author wushaopei* @create 2023-06-05 10:04*//*** 图的单源路径问题,使用广度优先遍历(BFS)求解*/
public class USSSPath {private Graph G;                 // 图对象private boolean[] visited;       // 记录顶点是否访问过private int s;                   // 源顶点private int[] pre;               // 记录顶点在路径中的前一个顶点private int[] dis;public USSSPath(Graph G, int s) {this.G = G;this.s = s;visited = new boolean[G.V()];           // 初始化visited数组,默认所有顶点未访问pre = new int[G.V()];                   // 初始化pre数组,默认所有顶点前一个顶点为-1dis = new int[G.V()];for (int v = 0; v < G.V(); v++) {pre[v] = -1;}bfs(s,dis);                                // 从源顶点s开始进行BFS遍历}private void bfs(int s, int[] dis) {Queue<Integer> queue = new LinkedList<>();    // 创建队列用于存储待访问顶点queue.add(s);                                 // 将源顶点s入队visited[s] = true;                            // 标记源顶点s为已访问dis[s] = 0;while (!queue.isEmpty()) {                     // 当队列不为空时,继续遍历int v = queue.poll();                       // 出队一个顶点vfor (int w : G.adj(v)) {                     // 遍历顶点v的邻接顶点wif (!visited[w]) {                       // 如果w未被访问过queue.add(w);                        // 将w入队visited[w] = true;                   // 标记w为已访问pre[w] = v;                          // 设置w在路径中的前一个顶点为vdis[w] = dis[v] + 1;}}}}public boolean isConnectedTo(int t) {G.validateVertex(t);                      // 验证目标顶点t是否合法return visited[t];                        // 返回目标顶点t是否与源顶点s相连}public Iterable<Integer> path(int t) {List<Integer> res = new ArrayList<>();if (!isConnectedTo(t)) return res;         // 若目标顶点t与源顶点s不相连,则返回空路径int cur = t;while (cur != s) {res.add(cur);                          // 将当前顶点加入路径中cur = pre[cur];                        // 更新当前顶点为其前一个顶点}res.add(s);                                // 将源顶点s加入路径中Collections.reverse(res);                   // 反转路径列表,得到从源顶点s到目标顶点t的路径return res;                                 // 返回路径列表}public int dis(int s){G.validateVertex(s);return dis[s];}public static void main(String[] args) {Graph graph = new Graph("g.txt");USSSPath usssPath = new USSSPath(graph, 0);System.out.println(usssPath.path(6));  // 打印从源顶点0到顶点6的路径System.out.println(usssPath.dis(3)); // 打印源到顶点的路径长度}
}

该代码实现了使用广度优先遍历求解图的单源路径问题,并提供了打印路径和计算最短距离的功能。在主方法中,通过创建图对象和USSSPath对象,可以对图进行处理并输出结果。


http://www.ppmy.cn/news/237726.html

相关文章

【Linux系统进阶详解】Linux文件属性10列详解原理详解和每个命令使用场景以及实例

Linux文件属性包括10列,分别是: inode号:每个文件在Linux系统中都有一个唯一的inode号码,用于标识这个文件。inode号码是文件系统中的一个重要概念,可以用来查找和管理文件。文件类型:表示文件的类型,包括普通文件、目录、链接文件、字符设备文件、块设备文件、管道文件…

笔记本支架有必要考虑购买吗

任何健康问题都是大问题。不想颈肩酸痛&#xff0c;还要面对电脑。大家该怎么办&#xff1f; 笔记本电脑支架就是在这种困境中应运而生。 它基于人体工程学和人体工程学设计理念的参与&#xff0c;由显示器支架和笔记本支架两部分组成&#xff0c;使用户的视线与显示器平行&am…

笔记本外接显示器,合上笔记本盖子以后在显示屏上显示

只需设置合上笔记本盖子的动作为“不采取任何操作”即可。 步骤如下&#xff1a; 1&#xff09;打开控制面板选择查看方式为“大图标”选择电源选项 2&#xff09;点击选定的电源方案右边的“更改计划设置”再选择“更改高级电源设置” 3&#xff09;找到“电源按钮和盖子”项选…

乐歌DLB502支架怎么安装在显示器上

判断您的显示屏是否可以安装这款DLB502电脑支架&#xff0c;需要知道您显示屏后面四个孔的孔距&#xff0c;常规都是75*75mm&#xff0c;或者100*100mm的&#xff0c;这两种规格都可以安装使用~/:087显示器重量要求在2-9kg以内哦。 超过的话需要选择升级款~ 另外我们的支架头部…

笔记本电脑配备支架有什么作用吗

随着中国笔记本电脑市场价格的不断发展下降&#xff0c;笔记本电脑的渗透率也在不断努力提高。笔记本电脑的便捷性使我们的工作效率大大提高&#xff0c;在哪个场所都能办公&#xff0c;但同时也给我们的身体带来了很多的潜在威胁。 国内对使用笔记本电脑健康的担忧也在升温&am…

显示器支架气弹与机械哪个比较好

目前我们常见的显示器支架中&#xff0c;基本上是由机械弹簧与气弹簧两种技术原理构成。早些年&#xff0c;气动弹簧的成本普遍高于机械弹簧&#xff0c;所以在大多数情况下&#xff0c;使用气动弹簧的同品牌显示器支架要贵一些。当然&#xff0c;现在不是绝对的&#xff0c;还…

台式计算机屏幕扩展,台式机屏幕如何扩展

台式机想要实现开两个屏幕!用什么方法好呢?下面由小编给你做出详细的台式机屏幕扩展方法介绍!希望对你有帮助! 台式机屏幕扩展方法如下&#xff1a; 台式扩展显示屏其实就是电脑连接两个显示器&#xff0c;但是电脑要支持双输出才行&#xff0c;也就是有一个VGA接口和一个HDMI…

计算机显示器外壳怎么防水,电脑显示器怎么拆开外壳

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。 电脑显示器拆开外壳的方法如下&#xff1a; 1、准备好这些工具一字螺丝刀、撬片刀、十字螺丝刀、六角螺丝刀、 毛刷。以及装螺丝的盒子&#xff0c;然后把桌面整理干净。防止刮伤屏…