十五、图的算法之无向图
2016-04-17 18:10
411 查看
无向图
定义:图是由一组顶点和一组能够将两个顶点相连的边组成的。特殊的图:
有自环:与自己相连 有平行边:也称为多重图
相关术语:
大多数我们会省略“简单”二字
图的密度:已连接顶点对占未连接的比例
无向图数据类型
常见的存储方式:(邻接集是采用Set存储,以方便去重以及删除顶点)代码(内部使用了背包,也就是链表)
public class Graph { private static final String NEWLINE = System.getProperty("line.separator"); private final int V; private int E; private Bag<Integer>[] adj; public Graph(int V) { if (V < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative"); this.V = V; this.E = 0; adj = (Bag<Integer>[]) new Bag[V]; for (int v = 0; v < V; v++) { adj[v] = new Bag<Integer>(); } } public Graph(In in) { this(in.readInt()); int E = in.readInt(); if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative"); for (int i = 0; i < E; i++) { int v = in.readInt(); int w = in.readInt(); addEdge(v, w); } } public Graph(Graph G) { this(G.V()); this.E = G.E(); for (int v = 0; v < G.V(); v++) { // reverse so that adjacency list is in same order as original Stack<Integer> reverse = new Stack<Integer>(); for (int w : G.adj[v]) { reverse.push(w); } for (int w : reverse) { adj[v].add(w); } } } public int V() { return V; } public int E() { return E; } // throw an IndexOutOfBoundsException unless 0 <= v < V private void validateVertex(int v) { if (v < 0 || v >= V) throw new IndexOutOfBoundsException("vertex " + v + " is not between 0 and " + (V-1)); } public void addEdge(int v, int w) { validateVertex(v); validateVertex(w); E++; adj[v].add(w); adj[w].add(v); } public Iterable<Integer> adj(int v) { validateVertex(v); return adj[v]; } public int degree(int v) { validateVertex(v); return adj[v].size(); } public String toString() { StringBuilder s = new StringBuilder(); s.append(V + " vertices, " + E + " edges " + NEWLINE); for (int v = 0; v < V; v++) { s.append(v + ": "); for (int w : adj[v]) { s.append(w + " "); } s.append(NEWLINE); } return s.toString(); } }
深度优先搜索
可以解决判断是否连通以及找出两点的一条路经问题(不一定是最短的)public class DepthFirstPaths { private boolean[] marked; // marked[v] = is there an s-v path? private int[] edgeTo; // edgeTo[v] = last edge on s-v path private final int s; // source vertex public DepthFirstPaths(Graph G, int s) { this.s = s; edgeTo = new int[G.V()]; marked = new boolean[G.V()]; dfs(G, s); } // depth first search from v private void dfs(Graph G, int v) { marked[v] = true; for (int w : G.adj(v)) { if (!marked[w]) { edgeTo[w] = v; dfs(G, w); } } } public boolean hasPathTo(int v) { return marked[v]; } public Iterable<Integer> pathTo(int v) { if (!hasPathTo(v)) return null; Stack<Integer> path = new Stack<Integer>(); for (int x = v; x != s; x = edgeTo[x]) path.push(x); path.push(s); return path; } }
广度优先搜索
可以解决单源最短路径问题(此处无权重)深度和广度的区别:
public class BreadthFirstPaths { private static final int INFINITY = Integer.MAX_VALUE; private boolean[] marked; // marked[v] = is there an s-v path private int[] edgeTo; // edgeTo[v] = previous edge on shortest s-v path private int[] distTo; // distTo[v] = number of edges shortest s-v path public BreadthFirstPaths(Graph G, int s) { marked = new boolean[G.V()]; distTo = new int[G.V()]; edgeTo = new int[G.V()]; bfs(G, s); assert check(G, s); } /** * Computes the shortest path between any one of the source vertices in <tt>sources</tt> * and every other vertex in graph <tt>G</tt>. * @param G the graph * @param sources the source vertices */ public BreadthFirstPaths(Graph G, Iterable<Integer> sources) { marked = new boolean[G.V()]; distTo = new int[G.V()]; edgeTo = new int[G.V()]; for (int v = 0; v < G.V(); v++) distTo[v] = INFINITY; bfs(G, sources); } // breadth-first search from a single source private void bfs(Graph G, int s) { Queue<Integer> q = new Queue<Integer>(); for (int v = 0; v < G.V(); v++) distTo[v] = INFINITY; distTo[s] = 0; marked[s] = true; q.enqueue(s); while (!q.isEmpty()) { int v = q.dequeue(); for (int w : G.adj(v)) { if (!marked[w]) { edgeTo[w] = v; distTo[w] = distTo[v] + 1; marked[w] = true; q.enqueue(w); } } } } // breadth-first search from multiple sources private void bfs(Graph G, Iterable<Integer> sources) { Queue<Integer> q = new Queue<Integer>(); for (int s : sources) { marked[s] = true; distTo[s] = 0; q.enqueue(s); } while (!q.isEmpty()) { int v = q.dequeue(); for (int w : G.adj(v)) { if (!marked[w]) { edgeTo[w] = v; distTo[w] = distTo[v] + 1; marked[w] = true; q.enqueue(w); } } } } public boolean hasPathTo(int v) { return marked[v]; } public int distTo(int v) { return distTo[v]; } public Iterable<Integer> pathTo(int v) { if (!hasPathTo(v)) return null; Stack<Integer> path = new Stack<Integer>(); int x; for (x = v; distTo[x] != 0; x = edgeTo[x]) path.push(x); path.push(x); return path; } // check optimality conditions for single source private boolean check(Graph G, int s) { // check that the distance of s = 0 if (distTo[s] != 0) { StdOut.println("distance of source " + s + " to itself = " + distTo[s]); return false; } // check that for each edge v-w dist[w] <= dist[v] + 1 // provided v is reachable from s for (int v = 0; v < G.V(); v++) { for (int w : G.adj(v)) { if (hasPathTo(v) != hasPathTo(w)) { StdOut.println("edge " + v + "-" + w); StdOut.println("hasPathTo(" + v + ") = " + hasPathTo(v)); StdOut.println("hasPathTo(" + w + ") = " + hasPathTo(w)); return false; } if (hasPathTo(v) && (distTo[w] > distTo[v] + 1)) { StdOut.println("edge " + v + "-" + w); StdOut.println("distTo[" + v + "] = " + distTo[v]); StdOut.println("distTo[" + w + "] = " + distTo[w]); return false; } } } // check that v = edgeTo[w] satisfies distTo[w] + distTo[v] + 1 // provided v is reachable from s for (int w = 0; w < G.V(); w++) { if (!hasPathTo(w) || w == s) continue; int v = edgeTo[w]; if (distTo[w] != distTo[v] + 1) { StdOut.println("shortest path edge " + v + "-" + w); StdOut.println("distTo[" + v + "] = " + distTo[v]); StdOut.println("distTo[" + w + "] = " + distTo[w]); return false; } } return true; } }
连通分量(查找连通性)
也就是找两点是否连通(是否是等价关系,和union-find算法一样)代码:借用了深度搜素id标志类别数,count记录每个类的个数
public class CC { private boolean[] marked; // marked[v] = has vertex v been marked? private int[] id; // id[v] = id of connected component containing v private int[] size; // size[id] = number of vertices in given component private int count; // number of connected components public CC(Graph G) { marked = new boolean[G.V()]; id = new int[G.V()]; size = new int[G.V()]; for (int v = 0; v < G.V(); v++) { if (!marked[v]) { dfs(G, v); count++; } } } // depth-first search private void dfs(Graph G, int v) { marked[v] = true; id[v] = count; size[count]++; for (int w : G.adj(v)) { if (!marked[w]) { dfs(G, w); } } } public int id(int v) { return id[v]; } public int size(int v) { return size[id[v]]; } public int count() { return count; } public boolean connected(int v, int w) { return id(v) == id(w); } public boolean areConnected(int v, int w) { return id(v) == id(w); } }
和之前的union-find算法对比:
实际应用中 union-find更快,因为不需要图的数据结构且是动态算法(常数时间内检查连通性,甚至在添加一条边后)
检测环
主要关注hasSelfLoop和haveParallelEdges的写法public class Cycle { private boolean[] marked; private int[] edgeTo; private Stack<Integer> cycle; public Cycle(Graph G) { if (hasSelfLoop(G)) return; if (hasParallelEdges(G)) return; marked = new boolean[G.V()]; edgeTo = new int[G.V()]; for (int v = 0; v < G.V(); v++) if (!marked[v]) dfs(G, -1, v); } // does this graph have a self loop? // side effect: initialize cycle to be self loop private boolean hasSelfLoop(Graph G) { for (int v = 0; v < G.V(); v++) { for (int w : G.adj(v)) { if (v == w) { cycle = new Stack<Integer>(); cycle.push(v); cycle.push(v); return true; } } } return false; } // does this graph have two parallel edges? // side effect: initialize cycle to be two parallel edges private boolean hasParallelEdges(Graph G) { marked = new boolean[G.V()]; for (int v = 0; v < G.V(); v++) { // check for parallel edges incident to v for (int w : G.adj(v)) { if (marked[w]) { cycle = new Stack<Integer>(); cycle.push(v); cycle.push(w); cycle.push(v); return true; } marked[w] = true; } // reset so marked[v] = false for all v for (int w : G.adj(v)) { marked[w] = false; } } return false; } public boolean hasCycle() { return cycle != null; } public Iterable<Integer> cycle() { return cycle; } private void dfs(Graph G, int u, int v) { marked[v] = true; for (int w : G.adj(v)) { // short circuit if cycle already found if (cycle != null) return; if (!marked[w]) { edgeTo[w] = v; dfs(G, v, w); } // check for cycle (but disregard reverse of edge leading to v) else if (w != u) { cycle = new Stack<Integer>(); for (int x = v; x != w; x = edgeTo[x]) { cycle.push(x); } cycle.push(w); cycle.push(v); } } } }
双色问题
能不能够用两种颜色将图的所有顶点着色(图的二分性)public class TwoColor { private boolean[] marked; private boolean[] color; private boolean isTwoColorable = true; public TwoColor(Graph G){ marked = new boolean[G.V()]; color = new boolean[G.V()]; for (int s = 0; s < G.V(); s++) { if(!marked[s]){ dfs(G,s); } } } private void dfs(Graph G, int v) { // TODO Auto-generated method stub marked[v] = true; for(int w:G.adj(v)){ if(!marked[w]){ color[w] = !color[v]; dfs(G,w); } else if (color[w]=color[v]) isTwoColorable = false; } } public boolean isBipartite(){ return isTwoColorable; } }
符号图
顶点名是字符串(不是用数字表示了)public class SymbolGraph { private ST<String, Integer> st; // string -> index private String[] keys; // index -> string private Graph G; public SymbolGraph(String filename, String delimiter) { st = new ST<String, Integer>(); // First pass builds the index by reading strings to associate // distinct strings with an index In in = new In(filename); // while (in.hasNextLine()) { while (!in.isEmpty()) { String[] a = in.readLine().split(delimiter); for (int i = 0; i < a.length; i++) { if (!st.contains(a[i])) st.put(a[i], st.size()); } } StdOut.println("Done reading " + filename); // inverted index to get string keys in an aray keys = new String[st.size()]; for (String name : st.keys()) { keys[st.get(name)] = name; } // second pass builds the graph by connecting first vertex on each // line to all others G = new Graph(st.size()); in = new In(filename); while (in.hasNextLine()) { String[] a = in.readLine().split(delimiter); int v = st.get(a[0]); for (int i = 1; i < a.length; i++) { int w = st.get(a[i]); G.addEdge(v, w); } } } public boolean contains(String s) { return st.contains(s); } public int index(String s) { return st.get(s); } public String name(int v) { return keys[v]; } public Graph G() { return G; } }
相关文章推荐
- Activity生命周期
- linux 软件安装提示错误
- 立项说明书之可行性分析
- Matlab隐函数求导作图
- 遍历list集合删除指定元素方法
- 南京理工大学第八届程序设计大赛(校外镜像)
- jQuery选择器探究:语法汇总
- Java中的多态
- 20145110《Java程序设计》第七周学习总结
- mongo入门--java连接mongo数据库
- 2016-4-11
- python随机数计算并输出
- 导入项目,项目出现红叉原因
- Netbeans Theme
- 理解进程调度时机跟踪分析进程调度与进程切换的过程
- 计数、译码、显示电路Multisim仿真
- ngrok-qydev.com
- Android应用中使用百度地图API定位自己的位置(二)
- 【Swift学习】Swift编程之旅---集合类型之Sets(七)
- 学习进度(第七周)