图是一种由节点和链(连接节点,有向或者无向)组成的数据结构。可以用邻接矩阵和邻接表两种数据结构来表示。
P1:邻接矩阵的数据结构由存储节点的顺序表和表示邻接关系的0-1矩阵组成。要实现的功能包括顶点和边的增加和删除、边的权重获取和更新、深度优先和广度优先搜索。
图的接口定义如下:
public interface GGraph<T> {
public static final int MAX_WEIGHT=99999;
int vertexCount();//当前图的顶点数
T get(int i);
int getWeight(int i,int j);
int insertVertex(T x);
void insertEdge(int i,int j,int weight);
void removeVertex(int i);
void removeEdge(int i,int j);
int getNextNeighbor(int i,int j);
void DFSTraverse(int i);//深度优先搜索
void BFSTraverse(int i);//广度优先搜索
}
因为不同数据结构的同一功能接口的具体实现有时候会依赖于其具体数据结构,所以将不依赖的功能和以来的功能接口分开:
用一个抽象图类去封装不依赖数据结构的功能函数,而依赖具体数据结构的功能函数在数据结构内部实现:
public abstract class AbstractGraph<T> implements GGraph<T> {
private static final int MAX_WEIGHT = 99999;
public abstract int vertexCount();
public abstract T get(int i);
public abstract int getNextNeighbor(int i,int j);//返回与与顶点i相连的在顶点j后面的顶点序号
public abstract int getWeight(int i,int j);
public void DFSTraverse(int i)
{...}
public void BFSTraverse(int i)
{...}
//...最短路径、最小生成树算法等
}
定义加权(有向或无向,无向边在图数据结构中存储两次)边的数据结构如下:
public class Edge implements Comparable<Edge> {
public int start,dest,weight;
public Edge(int start,int dest,int weight)
{
this.start=start;
this.dest=dest;
this.weight=weight;
}
public String toString()
{
return "("+this.start+","+this.dest+","+this.weight+")";
}
@Override
public int compareTo(Edge e) {
// TODO Auto-generated method stub
int num=this.start-e.start;
return num==0?(this.dest-e.dest):num;
}
}
邻接矩阵数据结构定义和接口的代码如下:
public class AdjMatixGraph<T>{
//邻接图的存储结构由一个存储节点的顺序表和存储邻接矩阵的二维数组组成
SeqList<T> list;
int[][] adjMatrix;
static final int MAX_WEIGHT=99999;
//构造函数初始化,构造空图,指定顶点线性表容量
public AdjMatixGraph(int size)
{
list=new SeqList<T>(size);
for(int i=0;i<list.length();i++)
for(int j=0;j<list.length();j++)
if(i==j)
adjMatrix[i][i]=0;
else
adjMatrix[i][j]=MAX_WEIGHT;
}
//构造函数用顶点数组、边数组初始化图的线性表和邻接矩阵
public AdjMatixGraph(T[] vertices,Edge[] edges)
{
this(vertices.length);
for(int i=0;i<vertices.length;i++)
this.insertVertex(vertices[i]);
for(int j=0;j<edges.length;j++)
this.insertEdge(edges[j]);
}
public int vertexCount()
{
return this.list.length();
}
public T get(int i)
{
return this.list.get(i);
}
public int getWeight(int i,int j)
{
return this.adjMatrix[i][j];
}
//返回图的顶点集合和邻接矩阵描述字符串
public String toString()
{
String str="顶点集合:"+this.list.toString()+"\n";
int n=this.vertexCount();
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
str+=this.adjMatrix[i][j];
str+="\n";
}
return str;
}
//插入顶点,返回顶点的序号,在list尾部添加节点的同时要在邻接关系数组中添加一行一列
public int insertVertex(T x)
{
this.list.append(x);
int temp[][]=this.adjMatrix;
int i=0,j=0;
int n=this.adjMatrix.length;
if(this.vertexCount()>n)
{
this.adjMatrix=new int[n+1][n+1];
for(;i<temp.length;i++)
{
for(;j<temp.length;j++)
this.adjMatrix[i][j]=temp[i][j];
this.adjMatrix[i][j]=MAX_WEIGHT;
}
this.adjMatrix[i][j]=MAX_WEIGHT;
}
return i;
}
//插入边
public void insertEdge(Edge edge)
{
this.insertEdge(edge.start,edge.dest,edge.weight);
}
//插入边操作重载,插入一条边不需要对节点进行改动,只需要修改邻接矩阵,如果该边已经存在则不需要修改
public void insertEdge(int start,int dest,int weight)
{
int n=this.vertexCount();
if(start>=0 && start<n&& dest>=0 && dest<n && this.adjMatrix[start][dest]==MAX_WEIGHT)
this.adjMatrix[start][dest]=weight;
}
//图的删除顶点操作,在线性表中删除相应序号的顶点,再将邻接矩阵第i列i行删掉,并将后面的数据前移
//序列号i从0开始,for循环计数器k从0到i遍历0到i-1
public void removeVertex(int i)
{
int n=this.vertexCount();
this.list.remove(i);
for(int k=0;k<i;k++)
for(int j=i;j<this.vertexCount();j++)
this.adjMatrix[k][j]=this.adjMatrix[k][j+1];
for(int k=i;k<this.vertexCount();k++)
for(int j=0;j<i;j++)
this.adjMatrix[k][j]=this.adjMatrix[k+1][j];
for(int j=i;j<n;j++)
for(int k=i;k<n;k++)
this.adjMatrix[j][k]=this.adjMatrix[j+1][k+1];
}
测试代码如下:
public static void main(String args[])
{
String[] vertices={"A","B","C","D","E"};
Edge edges[]={new Edge(0,1,5),new Edge(0,3,2),new Edge(1,0,5),
new Edge(1,3,6),new Edge(1,2,7),new Edge(2,1,7),new Edge(2,3,8),
new Edge(2,4,3),new Edge(4,2,3),new Edge(4,3,9),new Edge(3,0,2),new Edge(3,4,9)};
AdjMatixGraph<String> g=new AdjMatixGraph<String>(vertices,edges);
System.out.print(g.toString());
}
P2:图的邻接表是一条存储各顶点的顺序表组成,而顶点的数据结构除了包含顶点数据项,还包含指向“与当前顶点的邻接边”的引用。
public class Vertex<T> {
public T data;
public SortedSinglyLinkedList<Edge> adjlink;
public Vertex(T data)
{
this.data=data;
this.adjlink=new SortedSinglyLinkedList<Edge>();
}
public String toString()
{
return "\n"+this.data.toString()+":"+this.adjlink.toString();
}
}
图的邻接表数据结构如下:
public class AdjListGraph<T> {
static final int MAX_WEIGHT=99999;
protected SeqList<Vertex<T>> vertexlist;
public AdjListGraph(int size)
{
size=10>size?10:size;
this.vertexlist=new SeqList<Vertex<T>>(size);
}
public AdjListGraph(T[] vertices,Edge[] edge)
{
this(vertices.length*2);
for(int i=0;i<vertices.length;i++)
insertVertex(vertices[i]);
for(int i=0;i<edge.length;i++)
insertEdge(edge[i]);
}
public int insertVertex(T x)
{
Vertex<T> v=new Vertex<T>(x);
this.vertexlist.append(v);
return this.VertexCount()-1;
}
//插入一条边,首先找到该顶点的排序单链表,再在排序单链表中逐个比较找到插入位置,如果找到相同边(起点、中点、权重都相同)则不执行插入操作
public void insertEdge(int i,int j,int weight)
{
Edge e=new Edge(i,j,weight);
SortedSinglyLinkedList<Edge> ssll=this.vertexlist.get(i).adjlink;
Node<Edge> front=ssll.head,p=front.next;
while(p!=null&&p.data.compareTo(e)<0)
{
front=p;
p=p.next;
}
if(p!=null && p.data.compareTo(e)==0)
return;
Node<Edge> node=new Node<Edge>(e,p);
front.next=node;
}
public void insertEdge(Edge e)
{
insertEdge(e.start,e.dest,e.weight);
}
public void removeEdge(int i,int j)
{
int n=this.VertexCount();
if(i>=0 && i<n && j>=0 && j<n && i!=j)
{
Node<Edge> front=this.vertexlist.get(i).adjlink.head;
Node<Edge> p=front.next;
while(p!=null && p.data.compareTo(new Edge(i,j,1))!=0)
{
front=p;
p=p.next;
}
front.next=p.next;
front=this.vertexlist.get(j).adjlink.head;
p=front.next;
while(p!=null && p.data.compareTo(new Edge(j,i,1))!=0)
{
front=p;
p=p.next;
}
front.next=p.next;
}
}
public void removeEdge(Edge e)
{
removeEdge(e.start,e.dest);
}
public void removeVertex(int i)
{
int n=this.VertexCount();
if(i>=0 && i<n)
{
for(int j=0;j<n;j++)
{
Node<Edge> front=this.vertexlist.get(j).adjlink.head,p=front.next;
while(p!=null)
{
Edge e=p.data;
if(e.start==j ||e.dest==j)
{
front.next=p.next;
p=front.next;
}
else
{
if(e.start>j)
e.start--;
if(e.dest>j)
e.dest--;
front=p;
p=p.next;
}
}
}
this.vertexlist.remove(i);
}
else
return;
}
public int VertexCount(){
return this.vertexlist.length();
}
public T get(int i){
return this.vertexlist.get(i).data;
}
//得到顶点序号为i,j的两点权重,先在顺序表中搜索到顶点i,再在对应的边链表中找到edge.dest=j那个节点
public int getWeight(int i,int j)
{
int n=this.VertexCount();
if(i>=0 && i<n && j>=0 &&j<n)
{
if(i==j)
return 0;
Vertex<T> vertexTemp=this.vertexlist.get(i);
Node<Edge> p=vertexTemp.adjlink.head.next;//p指向第i条单链表的第一个节点
while(p!=null)
{
if(p.data.dest==j)
return p.data.weight;
p=p.next;
}
return MAX_WEIGHT;
}
throw new IndexOutOfBoundsException("i="+i+",j="+j);
}
public String toString()
{
String str="顶点有:"+this.vertexlist.toString()+"\n";
for(int i=0;i<this.vertexlist.length();i++)
{
str+=this.vertexlist.get(i).adjlink.toString();
str+="\n";
}
return str;
}
测试代码如下:
public static void main(String[] args)
{
String[] vertices={"A","B","C","D","E"};
Edge edges[]={new Edge(0,1,5),new Edge(0,3,2),new Edge(1,0,5),
new Edge(1,3,6),new Edge(3,1,6),new Edge(1,2,7),new Edge(2,1,7),new Edge(2,3,8),
new Edge(2,4,3),new Edge(4,2,3),new Edge(4,3,9),new Edge(3,0,2),new Edge(3,4,9)};
AdjListGraph<String> g=new AdjListGraph<String>(vertices,edges);
System.out.println(g.toString());
g.removeEdge(1, 2);
System.out.println(g.toString());
g.insertEdge(1, 4, 10);
System.out.println(g.toString());
}