Graph的那些事儿

原创 2016年05月30日 17:09:32

0. 导言

为了让代码更加清晰,有以下一些约定:

  1. Graph的表示使用邻接表,更详细的说明参见 图的表示
  2. 本文代码中使用的图为 有向图,图中的顶点用 int 型来表示
  3. 图的顶点数经初始化后不可变,图的初始化有以下两种形式

    Graph(int verCount);
    Graph(String filename);

    其中文件内容为:

    100  // 顶点数
    1000 //边数
    52 59  // 以下每一行代表一条边
    24 57
    54 63
    ....

    图表示文件的生成参见下文中的GenGraph.java程序。

  4. 若想在Graph类中添加addVertex方法,可考虑使用符号表,即使用string类型作为邻接表的索引。

1. 图的表示

Graph的表示可以使用:

  • 边的列表(list of edges):定义一个Edge类表示每一条边
  • 邻接矩阵(adjacency matrix): 用一个V * V的矩阵来表示图
  • 邻接表(adjacency lists): 使用一个以顶点为索引的列表数组
  • 邻接集(adjacency sets): 使用一个以顶点为索引的set集合

典型Graph实现的性能复杂度:

数据结构 所需空间 添加一条边 检查两个顶点是否相邻 遍历顶点v的相邻顶点
边的列表 E 1 E E
邻接矩阵 V2 1 1 V
邻接表 E + V 1 degree(v) degree(v)
邻接集 E + V logV logV logV + degree(v)
package graph;

import java.io.*;
import java.util.LinkedList;

public class Digraph {
    private final int verCount; // number of vertices;
    private int edgeCount;  // number of edges;
    private LinkedList<Integer>[] adj;  // adjacency lists;

    public Digraph(int verCount) {
        this.verCount = verCount;
        this.edgeCount = 0;
        adj = (LinkedList<Integer>[]) new LinkedList[verCount];
        for (int v = 0; v < verCount; ++v) 
            adj[v]  = new LinkedList<Integer>();
    }

    public Digraph(String filename) throws IOException  {
        BufferedReader in = new BufferedReader(
                new FileReader(new File(filename).getAbsoluteFile()));
        // Get the vertex count
        verCount = Integer.parseInt(in.readLine());
        // Get the edge count
        int edgeNum = Integer.parseInt(in.readLine());
        // Initial the graph
        adj = (LinkedList<Integer>[]) new LinkedList[verCount];
        for (int v = 0; v < verCount; ++v) 
            adj[v]  = new LinkedList<Integer>();

        // Add the edges to graph
        for (int i = 0; i < edgeNum; ++i) {
            String[] currEdge = in.readLine().split(" ");

            if (currEdge.length > 0)
                addEdge(Integer.parseInt(currEdge[0]), 
                        Integer.parseInt(currEdge[1]));
        }

        in.close();
    }

    public int getVerCount() { return verCount; }
    public int getedgeCount() { return edgeCount; }

    public void addEdge(int v, int w) {
        adj[v].add(w);
        edgeCount++;
    }

    public Iterable<Integer> adj(int v) {
        return adj[v];
    }

    public Digraph reverse() {
        Digraph dg = new Digraph(verCount);
        for (int v = 0; v < verCount; ++v) 
            for (int w : adj(v))
                dg.addEdge(v, w);
        return dg;
    }

    public static void main(String[] args) throws IOException {
        Digraph ug = new Digraph("graph.txt");
        System.out.println(ug.reverse().getedgeCount());
        // print the adjacent vertex of vertex 0
        for (int w : ug.adj(0))
            System.out.print(w + " ");
    }
}

GenGraph.java:

// GenGraph.java
package graph;

import java.util.*;
import utils.*;

public class GenGraph {
    private String outfile;
    private Random rand = new Random(System.currentTimeMillis());
    private int verCount;
    private int edgeCount;

    public GenGraph(String outfile, int verCount, int edgeCount) {
        this.outfile = outfile;
        this.verCount = verCount;
        this.edgeCount = edgeCount;
    }

    public void run() {
        StringBuilder sb = new StringBuilder();
        sb.append(verCount + "\n");
        sb.append(edgeCount + "\n");
        for (int i = 0; i < edgeCount; ) {
            int left = rand.nextInt(verCount);
            int right = rand.nextInt(verCount);
            if (left == right)
                continue;
            else if (left < right)
                sb.append(left + " " + right + "\n");
            else
                sb.append(right + " " + left + "\n");
            ++i;    
        }
        TextFile.write(outfile, sb.toString());
    }

    public static void main(String[] args) {
        GenGraph gen = new GenGraph("graph.txt", 100, 1000);
        gen.run();
    }

}


// TextFile.java
package utils;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

public class TextFile extends ArrayList<String> {

    // Read a file as a single string:
    public static String read(String fileName) {
        StringBuilder sb = new StringBuilder();
        try {
            BufferedReader in = new BufferedReader(new FileReader(
                    new File(fileName).getAbsoluteFile()));
            try {
                String s;
                while ((s = in.readLine()) != null) {
                    sb.append(s);
                    sb.append("\n");
                }
            } finally {
                in.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return sb.toString();
    }

    // Write a single file in one method call:
    public static void write(String fileName, String text) {
        try {
            PrintWriter out = new PrintWriter(
                    new File(fileName).getAbsoluteFile());
            try {
                out.print(text);
            } finally {
                out.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // Read a file, split by any regular expression
    public TextFile(String fileName, String splitter) {
        super(Arrays.asList(read(fileName).split(splitter)));
        // Regular expression split() often leaves an
        // empty String at the first position.
        if (get(0).equals(""))  remove(0);
    }

    // Normally read by lines
    public TextFile(String fileName) {
        this(fileName, "\n");
    }

    public void write(String fileName) {
        try {
            PrintWriter out = new PrintWriter(
                    new File(fileName).getAbsoluteFile());
            try {
                for (String item : this)
                    out.println(item);
            } finally {
                out.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

2. 图的应用

2.1 单点和多点的连通性

  • 单点可达性: 给定一幅有向图和一个起点s,回答”是否存在一条从s到达给定顶点v的有向路径?”等类似问题。
  • 多点可达性:给定一幅有向图和顶点的集合,回答”是否存在一条从集合中的任意顶点到达给定顶点v的有向路径?”等类似问题。
package graph;

import java.io.*;
import java.util.*;

public class DirectedDFS {
    private boolean[] marked;

    // find vertices in graph that are reachable from s
    public DirectedDFS(Digraph graph, int s) {
        marked = new boolean[graph.getVerCount()];
        dfs(graph, s);
    }

    // find vertices in graph that are reachable from sources
    public DirectedDFS(Digraph graph, Iterable<Integer> sources) {
        marked = new boolean[graph.getVerCount()];
        for (int s : sources)
            if (!marked[s])  dfs(graph, s);
    }

    private void dfs(Digraph graph, int v) {
        marked[v] = true;
        for (int w : graph.adj(v))
            if (!marked[w]) dfs(graph, w);
    }

    public boolean marked(int v) {
        return marked[v];
    }

    public static void main(String[] args) throws IOException {
        Digraph graph = new Digraph(args[0]);
        List<Integer> sources = new LinkedList<Integer>();
        for (int i = 1; i < args.length; ++i)
            sources.add(Integer.parseInt(args[i]));

        DirectedDFS reachable = new DirectedDFS(graph, sources);

        for (int v = 0; v < graph.getVerCount(); ++v)
            if (reachable.marked(v) == true)
                System.out.print(v + " ");
    }
}
E.g.
    java DirectedDFS graph.txt 1
    java DirectedDFS graph.txt 1 2 6

2.2 单点有向路径

  • 单点有向路径: 给定一副有向图和一个起点s, 回答”从s到给定目的顶点v是否存在一条有向路径?如果有,请找出这条路径。”等类似问题。
package graph;

import java.util.*;
import java.io.*;

public class DepthFirstPaths {
    private boolean[] marked;
    public int[] edgeTo; // last vertex on known path to this vertex
    private final int s;  // source

    // find paths in graph from source s
    public DepthFirstPaths(Digraph graph, int s) {
        marked = new boolean[graph.getVerCount()];
        edgeTo = new int[graph.getVerCount()];
        this.s = s;
        dfs(graph, s);
    }

    private void dfs(Digraph graph, int v) {
        marked[v] = true;
        for (int w : graph.adj(v))
            if (!marked[w]) {
                edgeTo[w] = v;
                dfs(graph, w);
            }
    }

    public boolean hasPathTo(int v) {
        return marked[v];
    }

    public Iterable<Integer> pathTo(int v) {
        if (!hasPathTo(v))  return null;
        Deque<Integer> path = new ArrayDeque<Integer>();
        for (int x = v; x != s; x = edgeTo[x])
            path.push(x);
        path.push(s);
        return path;
    }

    public static void main(String[] args) throws IOException {
        Digraph graph = new Digraph(args[0]);
        int s = Integer.parseInt(args[1]);
        DepthFirstPaths search = new DepthFirstPaths(graph, s);

        for (int x : search.pathTo(66))
            System.out.print(x + " ");

        for (int v = 0; v < graph.getVerCount(); ++v) {
            System.out.println("Path " + s + " ---> " + v + " :");
            if (search.hasPathTo(v)) 
                for (int x : search.pathTo(v)) {
                    if (x == s) 
                        System.out.print(s);
                    else 
                        System.out.print("-" + x);
                }
            System.out.println();
        }
    }
}

2.3 单点最短有向路径

2.4 有向环检测

2.5 深度优先的顶点排序

2.6 优先级限制下的调度问题

2.7 拓扑排序

2.8 强连通性

2.9 顶点对的可达性

3. 最小生成树

4. 最短路径

4.1 Dijkstra算法(即时版本)

4.2 拓扑排序

4.3 Bellman-Ford算法(基于队列)

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

python_graph_dot_1.8.2

The Shortest Path in Nya Graph

The Shortest Path in Nya Graph Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 ...

S7-GRAPH_32bit

  • 2017-02-10 09:14
  • 22.88MB
  • 下载

CodeForces 165D Beard Graph

题目链接:http://codeforces.com/problemset/problem/165/D Beard Graph time limit per test:4 se...

DirectShow - About the Capture Graph Builder

A filter graph that performs video or audio capture is called a capture graph. Capture graphs are of...

Graph Theory

  • 2015-08-04 11:26
  • 3.50MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)