Ran's Hacking Zone

Welcome my friend.

Graph的那些事儿

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算法(基于队列)

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/okingniko/article/details/51538779
文章标签: Graph 算法
个人分类: Java LeetCode
上一篇LeetCode No334. Increasing Triplet Subsequence
下一篇LeetCode No332. Reconstruct Itinerary
想对作者说点什么? 我来说一句

CSS那些事儿.pdf 高清下载

2013年04月03日 34.76MB 下载

明朝那些事儿.mobi

2017年05月31日 4.34MB 下载

IT项目管理那些事儿.pdf

2015年01月29日 68.12MB 下载

黑金 FPGA那些事儿 (系列)

2018年03月17日 8.31MB 下载

编程的那些事(包括很多内容)

2009年09月29日 2.12MB 下载

CSS那些事儿.pdf

2018年01月15日 71.44MB 下载

UML那些事儿.pdf

2013年06月17日 29.62MB 下载

没有更多推荐了,返回首页

关闭
关闭