【数据结构与算法】无向图的结构与遍历 BFS&DFS
1 表示无向图的数据类型
1.1 邻接矩阵
可以使用一个V*V的二维布尔矩阵,当定点v和定点w相连的时候,定义第v行第w列的值为true,否则为false。邻接矩阵不适合定点较多的情况,含有百万的顶点数的图是很常见的,V^2的空间很难得满足。
1.2 邻接表
邻接表使用一个记录当前顶点相邻顶点的链表即边表
,和一个快速访问给定顶点的顶点列表即顶点表
。数据类型如下:
public class Graph { private final int V; private int E; private Bag<Integer>[] adj; Graph(int V) { this.V = V; adj = (Bag<Integer>[]) new Bag[V]; for (int i = 0; i < V; i++) adj[i] = new Bag<Integer>(); } public void addEdge(int u, int v) { adj[u].add(v); } public int V() { return this.V; } public int E() { return this.E; } public Iterable<Integer> adj(int v) { return adj[v]; } public String toString() { String s = V + " vertices, " + E + " edges.\n"; for (int i = 0; i < V; i++) { s += i + " : "; for(int w : this.adj(i)) s += w + " "; s += "\n"; } return s; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int V = sc.nextInt(); int E = sc.nextInt(); Graph g = new Graph(V); for (int i = 0; i < E; i++) { int u = sc.nextInt(); int v = sc.nextInt(); g.addEdge(u, v); g.addEdge(v, u); } System.out.println(g); } }
测试输入:
13 13 0 5 4 3 0 1 9 12 6 4 5 4 0 2 11 12 9 10 0 6 7 8 9 11 5 3
输出结果
13 vertices, 0 edges. 0 : 6 2 1 5 1 : 0 2 : 0 3 : 5 4 4 : 5 6 3 5 : 3 4 0 6 : 0 4 7 : 8 8 : 7 9 : 11 10 12 10 : 9 11 : 9 12 12 : 11 9
2 深度优先搜索
DepthFirstSearch.class
2.1 DFS的递归写法
public class DepthFirstSearch { boolean[] marked; private int count; DepthFirstSearch(Graph g, int s) { marked = new boolean[g.V()]; dfs(g, s); } void visit(int v) { System.out.print(v + " "); } void dfs(Graph g, int v) { marked[v] = true; visit(v); for(int w : g.adj(v)) { if(!marked[w]) { dfs(g, w); } } } }
测试结果
0 6 4 5 3 2 1
2.2 ★DFS的非递归写法
非递归写法主要借助了栈,思想与二叉树的先序遍历类似:每次将栈顶出栈再将栈顶的右节点和左节点依次入栈,只不过这里需要每次入栈一个相邻未访问节点,等到全被访问才将栈顶出栈。
①首先将第一个顶点入栈,并标记为已访问。
②遍历栈顶顶点第一个未被访问过的相邻节点将其入栈,如果栈顶顶点没有未被访问过的相邻顶点则将栈顶节点出栈。
③重复执行②直至栈空。
void dfs2(Graph g, int v) { Stack<Integer> s = new Stack<>(); s.push(v); visit(v); marked[v] = true; while(!s.empty()) { int x = s.peek(); boolean flag = false; // 每次访问第一个未被访问的相邻节点 for(int w : g.adj(x)) { if(!marked[w]) { visit(w); s.push(w); marked[w] = true; flag = true; break; } } // 如果不存在相邻顶点则将栈顶顶点出栈 if(!flag) s.pop(); } }
2.3 寻找路径
2.4 DFS寻找所有路径
3 广度优先搜索
广度优先遍历一般用于求单源最短路径。
public class BreadthFirstPaths { private boolean[] marked; private int[] edgeTo; private int s; BreadthFirstPaths(Graph g, int s) { marked = new boolean[g.V()]; edgeTo = new int[g.V()]; this.s = s; bfs(g, s); } public void bfs(Graph g, int v) { Queue<Integer> q = new LinkedBlockingQueue<>(); q.add(v); marked[v] = true; while (!q.isEmpty()) { int x = q.poll(); for (int w : g.adj(x)) { if (!marked[w]) { edgeTo[w] = x; q.add(w); marked[w] = true; } } } } public Stack<Integer> pathTo(int v) { Stack<Integer> s = new Stack<>(); while (v != this.s) { s.push(v); v = edgeTo[v]; } s.push(v); return s; } }
分类:
数据结构与算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步