本文讲解无向图, 算法笔记14:有向图讲解有向图。
无向图的表示和实现
如下图Graph所示,有一些定点和边组成图,边连接的两个定点相邻,边是对称的,0-1表示可以从0到1,也可以从1到0。从图中可以看到,对于任意一个顶点,都可以有任意多的相邻顶点,我们使用List数组(因为相邻顶点的个数不一致)保存对应索引顶点的相邻顶点,称之为邻接表。下图Graph的邻接表如下:
顶点 | 相邻顶点 |
---|---|
0 | 1 |
1 | 0 2 4 |
2 | 1 3 |
3 | 2 |
4 | 1 |
根据邻接表实现Graph
import java.io.*;
import java.util.*;
/**
* 图
* @author XY
*
*/
public class Graph {
private int V;//顶点个数
private int E;//边的数量
private ArrayList<Integer>[] adj;//邻接表
public Graph(int v) {
//使用顶点个数
this.V = v;
adj = (ArrayList<Integer>[]) new ArrayList[this.V];
for (int i = 0; i < adj.length; i++) {
adj[i] = new ArrayList<Integer>();
}
}
public Graph(Scanner scanner) throws Exception {
//根据输入流
this(scanner.nextInt());
int numedag=scanner.nextInt();
for(int i=0;i<numedag;i++){
int v=scanner.nextInt();
int w=scanner.nextInt();
addEdage(v, w);
}
}
public void addEdage(int v, int w) {
//图是对称的,所以一条边要改变两个顶点的list.
if(v!=w && !adj[v].contains(w)){
//排除自环和平行边
adj[v].add(w);
adj[w].add(v);
E++;
}
}
public Iterable<Integer> adj(int v) {
//返回顶点的相邻顶点
return adj[v];
}
public int V() {
return this.V;
}
public int E() {
return this.E;
}
@Override
public String toString() {
StringBuffer sb=new StringBuffer();
sb.append("V:"+this.V+" E:"+this.E);
for (int i = 0; i < adj.length; i++) {
sb.append("\n"+i+":");
for(int n:adj[i]){
sb.append(" "+n);
}
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
Graph graph=new Graph(new Scanner(new File("E:"+File.separator+"graph.txt")));
System.out.println(graph);
}
}
上图中Graph的输入流构造函数的输入流和toString()的结果如下所示:
符号图
我们在实际使用中不会直接使用数字,而是使用名称,如第一张图中的航线图,边表示城市之间可以直达,为了使用名称完成图,实现符号图;
import java.io.*;
import java.util.*;
/**
* 符号图:将符号和索引一一对应,图的具体实现使用索引
* @author XY
*
*/
public class SymbolGraph {
private HashMap<String, Integer> map;//根据符号找对应索引
private String[] index;//根据索引找符号
private int count=0;
private Graph graph;
public SymbolGraph(File file,String delim) throws Exception{
map=new HashMap<String, Integer>();
// Scanner scan=new Scanner(new BufferedInputStream(new FileInputStream(file)));
//注意BufferedInputStream和InputStream相比缓存对IO次数的影响
Scanner scan=new Scanner(new FileInputStream(file));
while (scan.hasNextLine()){
String[] temp=scan.nextLine().split(delim);
for (int i = 0; i < temp.length; i++) {
if(!map.containsKey(temp[i]))
map.put(temp[i],count++);
}
}
index=new String[count];
graph=new Graph(count);
scan=new Scanner(file);
while(scan.hasNextLine()){
String[] temp=scan.nextLine().split(delim);
int v=map.get(temp[0]);
index[v]=temp[