这里给出图类Graph.java(暂时只支持无向无权图)的实现,这里较之于此前所实现的邻接表实现的图的最大区别在于,邻接表的表项不再使用数组,而是采用java内部封装好的红黑树接口TreeSet这样做的目的是为保证每一次输出一个图的顺序性,以及使得对顶点或者是边的操作控制在对数的时间复杂度以内,因此,今后的几乎所有算法都是基于这样一种基本恩的数据结构展开的。
import java.io.File;
import java.io.IOException;
import java.util.TreeSet;//是用红黑树来替代链表存邻接节点,保证了邻接节点关键字大小是有序的
import java.util.Scanner;
//本部分代码原本是作为TreeSet来实现的数据结构,但由于Treeset的性质由于其他
//所以其他部分的算法都是基于由TreeSet来实现的图结构,故将类名更改为Graph.java
//暂时只支持无向图
public class Graph
{
private int V;//顶点
private int E;//边
private TreeSet<Integer> [] adj;//邻接表 使用红黑树
public Graph(String filename)
{//从文件中读取图的信息,并构建一个无向图
File file=new File(filename);
try(Scanner scanner=new Scanner(file))
{//这样写不用考虑回收资源
V=scanner.nextInt();//读取图的顶点数
if(V<0)
{//由于一个图最少有一个顶点,于是要考虑到方方面面的情况
throw new IllegalArgumentException("V must be non-negative");
}
adj=new TreeSet[V];//使用读取进来的第一行的数字创建一个邻接表
//由于java语法的特点邻接表的创建只能使用这种形式
//先进行链表的创建,在进行对链表中每一个节点进行空间申请的操作
for(int i=0;i<V;i++)
{//对链表中的每一个节点进行申请空间
//adj[i]指的是与第i个顶点直接相连的顶点所构成的节点链表
adj[i]=new TreeSet<Integer>();//这里使用类型推断
}
E=scanner.nextInt();//再度入图的边数
if(E<0)
{//由于一个图最少有一个顶点,于是要考虑到方方面面的情况
throw new IllegalArgumentException("E must be non-negative");
}
for(int i=0;i<E;i++)
{
//读入各个顶点的信息,顺带判断合法性
int a=scanner.nextInt();
validateVertex(a);
int b=scanner.nextInt();
validateVertex(b);
//判断是否为自环边
if(a==b)//如果某一条边的头尾节点都指向了同一个顶点,那么久认为这是一条自环边
{
throw new IllegalArgumentException("Self Loop is Detected!");
}
if(adj[a].contains(b))//如果已经有了这样的一条边,就认为他是一条平行边
{
throw new IllegalArgumentException("Parallel edges are detected!");
}
//根据各个顶点的信息,对邻接表进行追加元素操作
adj[a].add(b);
adj[b].add(a);
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
public void validateVertex(int v)
{//判断边的信息是否正确,比如:有没有某一个边的序号小于0,或者大于V
if(v<0||v>V)
{
throw new IllegalArgumentException("Vertex "+v+" is invalid");
}
}
public int V()//为用户提供接口来访问节点数以及边数
{
return V;
}
public int E()
{
return E;
}
public boolean hasEdge(int v,int w)//判断两个顶点之间是否存在一条边
{
validateVertex(v);
validateVertex(w);
return adj[v].contains(w);
}
public Iterable<Integer> adj(int v)//返回与顶点v相邻的边???????
{
validateVertex(v);
return adj[v];
}
public int degree(int v)//返回某一节点的度
{
validateVertex(v);
return adj[v].size();
}
@Override
public String toString()//重写toString()方法来打印图
{
StringBuilder sb=new StringBuilder();//创建StringBuilder的对象用来操作字符串
sb.append(String.format("V = %d,E = %d\n", V,E));//首先输出有关图的信息
for(int v=0;v<V;v++)//v表示各个顶点的下标
{
sb.append(String.format("%d :", v));//控制输出的每一行的最左侧格式
for(int w:adj[v])
{
sb.append(String.format("%d ", w));//使用增强for循环来对邻接表中的每一链表中的元素进行遍历
}
sb.append("\n");
}
return sb.toString();
}
public static void main(String []args)
{
Graph adjSet=new Graph("g.txt");//传入图文件,并创建一个图
System.out.println(adjSet);
}
}
这里使用到的图文件如下
7 7
0 1
0 2
1 3
1 4
2 3
2 6
5 6