之前写过图的邻接矩阵表示及其常用操作https://blog.csdn.net/qiuxinfa123/article/details/83719789,这篇博客主要介绍邻接表的相关操作,包括图的建立、深度优先搜索、广度优先搜索、单源最短路径、多源最短路径、最小生成树的Prim和Kruskal算法。
先看下节点类型以及边的类型。
//作为某个点的邻接点的顶点信息
class Node{
int index; //顶点的序号
int weight; //以该顶点为终点的边的权值
Node nextNode; //指向下一个顶点
}
//输入的所有顶点的类型,是任意一条链表的起点
class Vertex{
char data; //顶点值
Node firstEdge; //指向第一条边
}
//边的类型
static class Edge{
char start; //起点
char end; //终点
int weight; //边的权值
public Edge(char start,char end,int weight) {
this.start=start;
this.end=end;
this.weight=weight;
}
}
然后看图的初始化。
//有参构造器
public LinkGraph(char[] vert,Edge[] edge) {
//读入顶点,并初始化
vertex=new Vertex[vert.length];
parent=new int[vert.length];
for(int i=0;i
获取某个顶点的序号以及将某个顶点插入到指定顶点的后面。
//获取某个顶点对应的序号
public int getPosition(char v) {
for(int i=0;i
打印图的信息。
//打印图
public void print() {
System.out.println("邻接表存储的图:");
for(int i=0;i");
//如果存在邻接点
Node tmp=vertex[i].firstEdge;
while(tmp.nextNode!=null) {
System.out.print(vertex[tmp.index].data+"-->");
tmp=tmp.nextNode;
}
System.out.print(vertex[tmp.index].data);
System.out.println();
}
}
图的遍历,包括DFS和BFS。
//深度优先搜索,从第一个顶点开始遍历
public void DFS() {
boolean[] visited=new boolean[vertex.length]; //默认为false;
int[] path=new int[vertex.length]; //记录遍历的顶点序号
int index=0; //path[]的索引
MyStack stack=new MyStack(vertex.length);
visited[0]=true;
stack.push(0);
path[index++]=0;
while(!stack.isEmpty()) {
int v=getUnVisitedAdjVertex(stack.peek(),visited);
//如果不存在没有访问的邻接点,就出栈,原路返回
if(v==-1) {
stack.pop();
}
//否则,存在还没有访问过的邻接点,入栈,并标注已访问
else {
path[index++]=v; //访问邻接点
visited[v]=true; //标志已访问
stack.push(v); //入栈
}
}
//打印DFS路径
System.out.println("DFS路径:");
for(int i=0;i
单源最短路径问题的Dijkstra算法。
//单源最短路径问题:Dijkstra算法,s为起点,比上一种方法比较容易计算最短距离
public void dijkstra(int s) {
int[] path=new int[vertex.length]; //记录到起点经过的顶点路径
int[] distance=new int[vertex.length]; //记录到起点的距离
boolean[] visited=new boolean[vertex.length]; //标记是否访问过
//初始化到起点的距离
for(int i=0;itmp) {
distance[j]=tmp;
path[j]=k; //这里的意思是,顶点j到达起点,必定经过顶点k
}
}
}
//打印Dijkstra算法的最短路径,这里的路径是逆序输出的,可以使用栈将其恢复正常
System.out.printf("Dijkstra(%c)\n",vertex[s].data);
for(int i=0;i的权值;若start和end不是连通的,则返回无穷大。
private int getWeight(int start, int end) {
if (start==end)
return 0;
Node node = vertex[start].firstEdge;
while (node!=null) {
if (end==node.index)
return node.weight;
node = node.nextNode;
}
return INF;
}
多源最短路径问题的Floyd算法。
//Floyd算法求解任意两个顶点的最短距离问题,也就是多源最短路径问题
public void floyd() {
//存储最短路径
int[][] dist=new int[vertex.length][vertex.length];
//记录最短路径经过的顶点
int[][] prev=new int[vertex.length][vertex.length];
//初始化
for(int i=0;i tmp) {
// "i到j最短路径"对应的值,设为更小的一个(即经过k)
dist[i][j] = tmp;
// "i到j最短路径"对应的路径,经过k
prev[i][j] = prev[i][k];
}
}
}
}
// 打印floyd最短路径的结果
System.out.printf("floyd: \n");
for (int i = 0; i < vertex.length; i++) {
for (int j = 0; j < vertex.length; j++)
System.out.printf("%2d ", dist[i][j]);
System.out.printf("\n");
}
}
最小生成树的Prim算法。
//最小生成树:Prim算法
public void prim(int s) {
int[] distance=new int[vertex.length]; //记录到起点的距离
//初始化到起点的距离
for(int i=0;itmp) {
distance[j]=tmp;
}
}
}
//打印最小生成树
System.out.printf("prim(%c)\n",vertex[s].data);
for(int i=0;i");
}
System.out.print(vertex[prims[vertex.length-1]].data);
int sum=0; //最小权值和
for(int i=1;i
最小生成树的Krustral算法。
//最小生成树:Kruskal算法
public void kruskal() {
ArrayList list=new ArrayList<>();
//初始化边
for(int i=0;i() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight-o2.weight; //权值小的在前
}
});
//初始化并查集,parent[i]=-1;表示这棵树只有它自己,一开始是n棵树
for(int i=0;i=0;root=parent[root]);
//路径压缩
while(root!=v) {
int tmp=parent[v];
parent[v]=root;
v=tmp;
}
return root;
}
//将两个不同集合的元素进行合并,使两个集合中任两个元素都连通
void union( int u, int v)
{
int r1 = findRoot(u), r2 = findRoot(v); //r1 为 u 的根结点,r2 为 v 的根结点
int tmp = parent[r1] + parent[r2]; //两个集合结点个数之和(负数)
//如果 R2 所在树结点个数 > R1 所在树结点个数(注意 parent[r1]是负数)
if( parent[r1] > parent[r2] ) //优化方案――加权法则
{
parent[r1] = r2;
parent[r2] = tmp;
}
else
{
parent[r2] = r1;
parent[r1] = tmp;
}
}
顺便贴上测试数据。
public static void main(String[] args) {
char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; //顶点
Edge[] edges = { //边
// 起点 终点 权
new Edge('A', 'B', 12),
new Edge('A', 'F', 16),
new Edge('A', 'G', 14),
new Edge('B', 'C', 10),
new Edge('B', 'F', 7),
new Edge('C', 'D', 3),
new Edge('C', 'E', 5),
new Edge('C', 'F', 6),
new Edge('D', 'E', 4),
new Edge('E', 'F', 2),
new Edge('E', 'G', 8),
new Edge('F', 'G', 9),
};
LinkGraph graph=new LinkGraph(vexs,edges);
//打印图的邻接表
graph.print();
//深度优先搜索
graph.DFS();
System.out.println();
//广度优先搜索
graph.BFS();
System.out.println();
//单源最短路径:Dijkstra算法
System.out.println("单源最短路径:Dijkstra算法");
graph.dijkstra(3);
System.out.println();
//多源最短路径问题:Floyd算法
graph.floyd();
System.out.println();
//最小生成树:Prim算法
graph.prim(0);
//最小生成树:Kruskal算法
graph.kruskal();
}