JAVA数据结构基础–图的两种创建方式
图的邻接矩阵表示
如图示一个有向图转为矩阵表示的例子(矩阵中空格表示无穷大,即无路径到达)。矩阵的行表示起始点,列表示终止点。对角线元素表示自己到自己,全为0。
图左侧的权值(道路长度)为1的路径,由1出发,指向0。在矩阵中表示为Edge[1][0]=1;表示1到0路径长度为1
以下我分析的是无向图的矩阵表示代码,若改成有向图,改动插入边的函数即可。
完整代码
import java.util.LinkedList;
import java.util.Queue;
public class Graph {
private String[] Vertex;// 存储顶点名字
private int[][] Edge;// 存储边信息
private int numOfVertex;// 顶点个数
private int maxOfVertex;// 最大顶点个数
private int MAX = Integer.MAX_VALUE / 2;//无穷大表示值
public Graph(int maxOfVertex) {//构造函数
numOfVertex = 0;
this.maxOfVertex = maxOfVertex;
Vertex = new String[maxOfVertex];
Edge = new int[maxOfVertex][maxOfVertex];
inti(maxOfVertex);
}
public void inti(int maxOfVertex) {//初始化
maxOfVertex = this.maxOfVertex;
for (int i = 0; i < maxOfVertex; i++) {
for (int j = 0; j < maxOfVertex; j++) {
if (i != j) {
Edge[i][j] = MAX;
} else {
Edge[i][j] = 0;
}
}
}
}
public int getNumOfVertex() {// 获取顶点个数
return numOfVertex;
}
public boolean insertVertex(String name) {// 插入顶点
if (numOfVertex >= maxOfVertex) {
return false;
}
Vertex[numOfVertex++] = name;
return true;
}
public boolean insertEdge(String v1, String v2, int weight) {// 插入边
int v_1 = findByName(v1);
int v_2 = findByName(v2);
if (v_1 < 0 || v_2 < 0) {
return false;
}
Edge[v_1][v_2] = weight;
Edge[v_2][v_1] = weight;
return true;
}
public int getWeight(int s, int e) {//获得权重,即道路长度
return Edge[s][e];
}
public int findByName(String name) {// 通过名字查找下标
for (int i = 0; i < numOfVertex; i++) {
if (Vertex[i].equals(name)) {
return i;
}
}
return -1;
}
public boolean deleteVertex(String name) {// 删除顶点
int v = findByName(name);
if (v < 0) {
return false;
}
Vertex[v] = Vertex[numOfVertex - 1];
for (int i = 0; i < numOfVertex; i++) {
Edge[v][i] = Edge[numOfVertex - 1][i];
Edge[i][v] = Edge[i][numOfVertex - 1];
Edge[numOfVertex - 1][i] = MAX;
Edge[i][numOfVertex - 1] = MAX;
}
Vertex[--numOfVertex] = null;
return true;
}
public boolean deleteEdge(String v1, String v2) {// 删除边
int v_1 = findByName(v1);
int v_2 = findByName(v2);
if (v_1 < 0 || v_2 < 0) {
return false;
}
Edge[v_1][v_2] = MAX;
Edge[v_2][v_1] = MAX;
return true;
}
public int getEdge(String v1, String v2) {//获取权重
int v_1 = findByName(v1);
int v_2 = findByName(v2);
if (v_1 < 0 ||v_2 < 0) {
return -1;
}
return Edge[v_1][v_2];
}
}
函数解析
一、图类结构
public class Graph {
private String[] Vertex;// 存储顶点名字
private int[][] Edge;// 存储边信息
private int numOfVertex;// 顶点个数
private int maxOfVertex;// 最大顶点个数
private int MAX = Integer.MAX_VALUE / 2;//无穷大表示值
}
一、Vertex数组:存储图顶点的名字,下标与名字一 一对应;
二、Edge二维数组:图的表示矩阵,即上面图片中矩阵的实例;
三、maxOfVertex:添加顶点总和的最大值;
四、numOfVertex:当前已顶点的个数;
五、MAX:表示无穷大,代表两顶点不通。(这里取整数最大值的一半);
二、初始化:
public void inti(int maxOfVertex) {//初始化
maxOfVertex = this.maxOfVertex;
for (int i = 0; i < maxOfVertex; i++) {
for (int j = 0; j < maxOfVertex; j++) {
if (i != j) {
Edge[i][j] = MAX;
} else {
Edge[i][j] = 0;
}
}
}
}
当i!=j时,Edge[i][j]=MAX;(在未添加边时,所有顶点都不通)
当i==j时,Edge[i][j]=0;(自己到自己距离为0)
三、插入顶点:
public boolean insertVertex(String name) {// 插入顶点
if (numOfVertex >= maxOfVertex) {
return false;
}
Vertex[numOfVertex++] = name;
return true;
}
一、 if (numOfVertex >= maxOfVertex):判断当前已添加顶点是否达到最大值。
二、Vertex[numOfVertex++] = name:Vertex数组的第numOfVertex个存入该顶点名字。(该顶点名字与顶点下标存在关联)
四、通过顶点名字找下标:
public int findByName(String name) {// 通过名字查找下标
for (int i = 0; i < numOfVertex; i++) {
if (Vertex[i].equals(name)) {
return i;
}
}
return -1;
}
循环次数为已添加顶点个数,即每个顶点依次遍历寻找。
若Vertex数组中存储的顶点信息匹配,返回下标值。
五、插入边
public boolean insertEdge(String v1, String v2, int weight) {// 插入边
int v_1 = findByName(v1);
int v_2 = findByName(v2);
if (v_1 < 0 || v_2 < 0) {
return false;
}
Edge[v_1][v_2] = weight;
Edge[v_2][v_1] = weight;
return true;
}
一、 先找到俩顶点的下标。
二、if (v_1 < 0 || v_2 < 0):若不存在两个顶点,返回false;
三、改变表示矩阵中的值。(这里是表示无向图,改变两处。有向图改变一处)
Edge[v_1][v_2] = weight;
Edge[v_2][v_1] = weight;
六、删除顶点:
public boolean deleteVertex(String name) {// 删除顶点
int v = findByName(name);
if (v < 0) {
return false;
}
Vertex[v] = Vertex[numOfVertex - 1];
for (int i = 0; i < numOfVertex; i++) {
Edge[v][i] = Edge[numOfVertex - 1][i];
Edge[i][v] = Edge[i][numOfVertex - 1];
Edge[numOfVertex - 1][i] = MAX;
Edge[i][numOfVertex - 1] = MAX;
}
Vertex[--numOfVertex] = null;
return true;
}
图例示范:
七、删除边
public boolean deleteEdge(String v1, String v2) {// 删除边
int v_1 = findByName(v1);
int v_2 = findByName(v2);
if (v_1 < 0 || v_2 < 0) {
return false;
}
Edge[v_1][v_2] = MAX;
Edge[v_2][v_1] = MAX;
return true;
}
一、先判断顶点是否存在
二、将表示矩阵中【v1到v2】和【v2到v1】的地方改为MAX。
图的邻接表表示:
链表存储可以只存出存在的边,节约了空间。
完整代码:
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class Graph {
class Vertex {// 顶点
protected String name;//顶点名
Vertex(String N) {
name = N;
}
}
class Edge {// 边
protected Vertex Start;// 起始边
protected Vertex End;// 结束边
private int weight;// 权重
Edge(Vertex S, Vertex E, int W) {
Start = S;
End = E;
weight = W;
}
protected int getWeight() {
return weight;
}
protected Vertex getStart() {
return Start;
}
protected Vertex getEnd() {
return End;
}
}
int maxSize;//最大顶点数
int numVertex;//当前顶点个数
int numEdge;//边个数
Vertex vertex[]; //顶点信息集
public LinkedList<Edge>[] Edges;//存放顶点相邻边链表的数组
Graph(int maxSize) {//构造函数
this.maxSize = maxSize;
numVertex = 0;
numEdge = 0;
Edges = new LinkedList[maxSize+1];
vertex = new Vertex[maxSize+1];
for(int i=0;i<maxSize+1;i++){
Edges[i]=new LinkedList();
}
}
public boolean addEdge(String start, String end, int W) {//添加边
if(findNum(start)<0||findNum(end)<0) {
return false;
}
Vertex S = vertex[findNum(start)];
Vertex E = vertex[findNum(end)];
numEdge++;
Edge edge = new Edge(S, E, W);
Edges[findNum(S.name)].add(edge);
return true;
}
public void addvertex(String name) {
Vertex vertexs = new Vertex(name);
vertex[numVertex] = vertexs;
numVertex++;
}
public int findWeight(String v1, String v2) {
int v_1 = findNum(v1);
int v_2 = findNum(v2);
if(v_1<0||v_2<0) {
return -1;
}
for (int i = 0; i < Edges[v_1].size(); i++) {
if (findNum(Edges[v_1].get(i).End.name) == v_2) {
return Edges[v_1].get(i).getWeight();
}
}
return Integer.MAX_VALUE/2;
}
private int findNum(String name) {
for(int i=0;i<numVertex;i++) {
if(name.equals(vertex[i].name)) {
return i;
}
}
return -1;
}
public boolean deleteVertex(String name) {
if(numVertex >= maxSize) {
return false;
}
int v = findNum(name);
vertex[v] = vertex[--numVertex];
vertex[numVertex] = vertex[maxSize];
Edges[v] = Edges[numVertex];
Edges[numVertex] = Edges[maxSize];
return true;
}
public boolean deleteEdge(String v1,String v2) {
int v_1 = findNum(v1);
int v_2 = findNum(v2);
int i=0;
while(findNum(Edges[v_1].get(i).getEnd().name)!=v_2){
i++;
}
Edges[v_1].remove(i);
numEdge--;
return true;
}
}
代码解析
三个类,图类,边类,定点类。
class Vertex {// 顶点类
protected String name;//存储顶点名,
//与矩阵表示不同,改为顶点类与顶点数组下标对应
Vertex(String N) {//构造函数,赋值顶点名字
name = N;
}
}
class Edge {// 边类
protected Vertex Start;// 起始边
protected Vertex End;// 结束边
private int weight;// 权重
Edge(Vertex S, Vertex E, int W) {
Start = S;//起始顶点
End = E;//结束顶点
weight = W;//权重
}
}
public class Graph {
int maxSize;//最大顶点数
int numVertex;//当前顶点个数
int numEdge;//边个数
Vertex vertex[]; //顶点信息集
public LinkedList<Edge>[] Edges;//存放顶点相邻边链表的数组
}
LinkedList[] Edges:
首先它是一个数组,存储链表。而每个链表存储的都是边。(详细看边的添加)
构造函数:
Graph(int maxSize) {//构造函数
this.maxSize = maxSize;
numVertex = 0;
numEdge = 0;
Edges = new LinkedList[maxSize+1];
vertex = new Vertex[maxSize+1];
for(int i=0;i<maxSize+1;i++){//省略这步会出现空指针异常
Edges[i]=new LinkedList();
}
}
初始化为maxSize+1的原因是,在删除边和顶点的时候,将删除顶点赋值为最后一个空的数组即可。避免了直接复制null产生空指针的问题。
通过名字寻找顶点下标
private int findNum(String name) {
for(int i=0;i<numVertex;i++) {
if(name.equals(vertex[i].name)) {//与顶点数组中name属性配对返回下标
return i;
}
}
return -1;
}
添加顶点
public void addvertex(String name) {
Vertex vertexs = new Vertex(name);//new一个顶点对象
vertex[numVertex] = vertexs;//将顶点集的最后一个未赋值的赋值为新建的对象
numVertex++;//当前顶点个数加一
}
添加边
public boolean addEdge(String start, String end, int W) {//添加边
if(findNum(start)<0||findNum(end)<0) {//判断顶点是否存在
return false;
}
Vertex S = vertex[findNum(start)];//获取顶点信息
Vertex E = vertex[findNum(end)];
numEdge++;//边数+1
Edge edge = new Edge(S, E, W);//new一个边对象
Edges[findNum(S.name)].add(edge);
return true;
}
Edges[findNum(S.name)].add(edge):
举个例子,0到1有条边,则为Edges[0].add(边对象);
即Edge下标对应起始顶点。Edge[1]存放所有以1为起始顶点的边。
Edge[]的是链表的数组,用add()函数添加边对象。
删除边
public boolean deleteVertex(String name) {
if(numVertex >= maxSize) {
return false;
}
int v = findNum(name);
vertex[v] = vertex[--numVertex];//将最后一个存在的顶点移动至删除顶点位置
vertex[numVertex] = vertex[maxSize];//将移动结点赋值为空
Edges[v] = Edges[numVertex];//顶点与顶点数组下标对应,边集也是如此
Edges[numVertex] = Edges[maxSize];
return true;
}
删除边
public boolean deleteEdge(String v1,String v2) {
int v_1 = findNum(v1);
int v_2 = findNum(v2);
int i=0;
while(findNum(Edges[v_1].get(i).getEnd().name)!=v_2){
i++;
}
Edges[v_1].remove(i);//找到此边,删除
numEdge--;//边条数减一
return true;
}
一、findNum(Edges[v_1].get(i).getEnd().name:
得到以v1为起始点的边,并获取此边结束顶点的顶点名字。
二、若头匹配,尾巴不匹配,i++,继续匹配下一条以v1为起始点的边。
直到匹配成功,退出while循环,得到下标边对应索引后删除此边。