测试类
public class TestGraphAdjMatrix { //测试类
public static <E> void main(String[] args){
Scanner read=new Scanner(System.in);
GraphAdjMatrix g=new GraphAdjMatrix();
System.out.println("------------------------");
System.out.println("操作选项菜单:");
System.out.println("1.输出图");
System.out.println("2.添加边");
System.out.println("3.删除边");
System.out.println("4.添加顶点");
System.out.println("5.删除顶点");
System.out.println("6.图的遍历");
System.out.println("7.最短路径");
System.out.println("8.最小生成树");
System.out.println("0.退出");
System.out.println("------------------------");
int f=0,C=0,e,A,B;
char s;
do{
System.out.println("请输入操作选项:");
e=read.nextInt();
switch(e){
case 1:{
g.pntGraph();
break;
}
case 2:{
g.insertEdge();
break;
}
case 3:{
g.deleteEdge();
break;
}
case 4:{
g.insertVex();
break;
}
case 5:{
g.deleteVex();
break;
}
case 6:{
System.out.println("输入遍历方式:1 广度优先遍历 2 深度优先遍历");
C=read.nextInt();
if(C==1)
g.DFSTraverse();
else
g.BFSTraverse();
break;
}
case 7:{
g.dijkstra();
break;
}
case 8:{
g.prim();
break;
}
}
}while(e!=0);
read.close();//测试类中不能关闭输入,否者程序将终止
}
}
主方法
public class GraphAdjMatrix<E>{
Vex[] vexs;//顶点表
int [][] edges;//邻接矩阵 存储边的关系
int vexnum=0;//实际最大点数
int vex=0;//最大的点数 比实际值大5
int edgenum=0;//需要添加的边数
int edge=0;//最大边数 定点数的平方
int isdirection=0;//是否为有向图
final int MAXVALUE=10000;//模拟无穷大值
boolean[] visited;//标记点是否被遍历
Scanner st=new Scanner(System.in);
public GraphAdjMatrix(){
System.out.println("请输入储存的结点数和边的条数以及图的性质:例如 x y z(1无向图,2有向图)");
vexnum=st.nextInt();
edgenum=st.nextInt();
isdirection=st.nextInt();
vex=vexnum+5;//创建数组存储点和边时预留5的空间方便后续点的添加
vexs=new Vex[vex];
edge=(vexnum+5)*(vexnum+5);
edges=new int[edge][edge];//其中每个元素为0;
for(int i=0;i<vexnum;i++)//初始化边
for(int j=0;j<vexnum;j++){
if(i==j)edges[i][j]=0;
else edges[i][j]=this.MAXVALUE;
}
create_Graph(vexs,edges);//构造图
}
private void create_Graph(Vex[] vexs,int[][] edges) {//创建图
int i,j,k,vi,vj;
char ch;
Scanner in=new Scanner(System.in);
System.out.println("请使用一行连续输入图的"+this.vexnum+"顶点名称字符如AB...:");
char[] vname=in.nextLine().toCharArray();//读取字符串并将其按字符分开
//初始化顶点表
for( i=0;i<vexnum;i++)vexs[i]=new Vex(vname[i]);
//初始化邻接矩阵
System.out.println("请输入"+edgenum+"边信息,使用顶点的序号输入,如(i,j)为第i顶点到第j顶点的边的权值,输入i j weight,第一个顶点序号为0:");
int w=0;
for(i=0;i<edgenum;i++){
vi=in.nextInt();
vj=in.nextInt();
w=in.nextInt();
edges[vi][vj]=w;
if(isdirection==1) edges[vj][vi]=w;//如果是无向图在对应vj-vi也添加数据
}
}
public void pntGraph(){//输出图的信息
System.out.println("图的顶点表为:");
for(int i=0;i<vexnum;i++)System.out.print(vexs[i].vname+" ");
System.out.println();
System.out.println("图的邻接矩阵为:");
for(int i=0;i<vexnum;i++){
for(int j=0;j<vexnum;j++){//因为不同位数输出宽度不一样 现在值考虑最多两位是矩阵对齐
System.out.print(edges[i][j]==this.MAXVALUE?"∞ ":edges[i][j]/10==0?edges[i][j]+" ":edges[i][j]+" ");
}
System.out.println();
}
}
public boolean insertVex() {//插入点
System.out.println("请输入需要添加的点:");
char v=st.next().charAt(0);
if(vexnum>vex) //边界判断
return false;
vexs[vexnum++]=new Vex(v);
for(int i=0;i<vexnum;i++)//初始化与新插入点的边
edges[i][vexnum-1]=this.MAXVALUE;
for(int j=0;j<vexnum;j++)
edges[vexnum-1][j]=this.MAXVALUE;
edges[vexnum][vexnum]=0;
return true;
}
public boolean deleteVex() {//删除点
System.out.println("请输入需要删除的点:");
char v=st.next().charAt(0);
for(int i=0;i<vexnum;i++){
char ss=vexs[i].vname;
if(ss==v){
for(int j=i;j<vexnum-1;j++){
vexs[j]=vexs[j+1];//删除点
}
vexs[vexnum-1]=null;//删除与点相关的边
for(int col=i;col<vexnum-1;col++){//删除行
for(int row=0;row<vexnum;row++)
edges[col][row]=edges[col+1][row];
}
for(int row=i;row<vexnum-1;row++){//删除列
for(int col=0;col<vexnum;col++)
edges[col][row]=edges[col][row+1];
}
vexnum--;
return true;
}
}
return false;
}
public boolean insertEdge() {//插入边
System.out.println("请输入需要插入的边:");
int v1=st.nextInt();
int v2=st.nextInt();
int weight=st.nextInt();
if(v1<0||v2<0||v1>=vexnum||v2>=vexnum)//边界判断
throw new ArrayIndexOutOfBoundsException();
edges[v1][v2]=weight;
if(isdirection==1)
edges[v2][v1]=weight;
return false;
}
public boolean deleteEdge() {//删除边
System.out.println("请输入需要删除的边:");
int v1=st.nextInt();
int v2=st.nextInt();
if(v1<0||v2<0||v1>=vexnum||v2>=vexnum)//边界判断
throw new ArrayIndexOutOfBoundsException();
edges[v1][v2]=MAXVALUE;//把权值恢复初始值
if(isdirection==1)//如果是无向图v2-v1也需要恢复
edges[v2][v1]=MAXVALUE;
return false;
}
public void DFSTraverse() {//深度优先遍历
System.out.println("请输入遍历的源点名称:");
char v=st.next().charAt(0);
int i=indexOfVex(v);
if(i<0||i>vexnum)
throw new ArrayIndexOutOfBoundsException();
visited = new boolean[vexnum];//初始化用来标记的数组
StringBuilder sb=new StringBuilder();//创建字符串用来储存遍历的点 特点是创建后大小还可以变动 String则不行
Stack<Integer> stack =new Stack<Integer>();//创建栈用来存储图的点
stack.push(i);//将源点压栈
visited[i]=true;//标记该点已经被访问
while(!stack.isEmpty()){//取出栈顶元素并把该点关联的未被访问的点继续压入栈中直到全部访问完毕
i=stack.pop();
sb.append(vexs[i].vname+",");//将遍历的点存在字符串中
for(int j=vexnum-1;j>=0;j--){
if((edges[i][j]!=0&&edges[i][j]!=MAXVALUE)&&!visited[j]){//点未被访问并且不为无穷大有具体的数值就是可达
stack.push(j);
visited[j]=true;//标记该点
}
}
}
System.out.println("深度优先搜索结果:");
String s=sb.length()>0?sb.substring(0,sb.length()-1):null;//将串的值赋给String对象输出
System.out.println(s);
}
public void BFSTraverse() {// 广度优先遍历
System.out.println("请输入遍历的源点名称:");
char v=st.next().charAt(0);
int i=indexOfVex(v);
if(i<0||i>vexnum)
throw new ArrayIndexOutOfBoundsException();
visited = new boolean[vexnum];
StringBuilder sb1=new StringBuilder();
Queue<Integer> queue =new LinkedList<Integer>();//创建队列
queue.offer(i);//源点入队
visited[i]=true;//标记已被访问
while(!queue.isEmpty()){
i=queue.poll();//出队 并把该点相关的点入队
sb1.append(vexs[i].vname+",");
for(int p=vexnum-1;p>=0;p--){
if((edges[i][p]!=0&&edges[i][p]!=MAXVALUE)&&!visited[p]){
queue.offer(p);
visited[p]=true;
}
}
}
System.out.println("广度优先搜索结果:");
String s=sb1.length()>0?sb1.substring(0,sb1.length()-1):null;
System.out.println(s);
}
public void dijkstra() {//dijkstra算法实现最短路径
System.out.println("请输入遍历的源点名称:");
char v=st.next().charAt(0);
int t=indexOfVex(v);
if(t<0||t>vexnum)
throw new ArrayIndexOutOfBoundsException();
int[] flag=new int[vexnum];//标记点是否被访问
int[] D=new int[vexnum];//存储该点到 源点 的最短距离
int[] P=new int[vexnum];//记录到达该点最短路径上的上一个节点
//选择vi为源点,进行初始化 数组P 、D和flag数组
D[t]=0;flag[t]=1;P[t]=-1;//初始化源点的相关数组数值
for(int k=0;k<vexnum;k++){//将源点这一行的矩阵数据存进D数组 并且将所有的点的上一个节点标记为源点
if(k!=t){
D[k]=edges[t][k];
P[k]=t;
}
}
for(int n=1;n<vexnum;n++){//除源点还有n-1个节点未访问,所以需要在循环上一步对源点操作的步骤n-1次
int min=MAXVALUE;
int j=0;
for(int k=0;k<vexnum;k++){//假设选择顶点k
if(flag[k]==0&&D[k]<min){//未被访问的节点,并且是数值D数组里最小的一个
min=D[k];
j=k;
}
}
flag[j]=1; //标记顶点j已经被访问
//其余未被确定为最小距离的点到达改j点的距离加上j到源点的距离小于该点直接到达源点的距离就修改D的数据 同时把P改为j
for(int k=0;k<vexnum;k++){
if(flag[k]==0&&(min+edges[j][k])<D[k]){
D[k]=min+edges[j][k];
P[k]=j;
}
}
if(j==0){//当所有的点遍历完了还是找不到不为无穷大最近的点时视该点孤立 与其他点路径不可达
System.out.println("路径不可达!");
return;
}
}
System.out.println("请输入遍历的终点:");
char ss=st.next().charAt(0);
int r=indexOfVex(ss);//获取该节点在数组的标号 例如A点的标号为0
pntShortPath(t,r,D,P);//调用最短路径输出函数
}
public void pntShortPath(int v1,int v2,int[] d,int[] p){//输出最短路径
Stack<Integer> st=new Stack<Integer>();//创建栈 因为访问的时候是从重点的标记反过来查找的 利用栈先进后出的特点把错误顺序反过来正序输出
int k=v2;
while(p[k]!=-1){//将数组数据压站
st.push(p[k]);
k=p[k];
}
System.out.println("顶点"+valueOffCex(v1)+"到顶点"+valueOffCex(v2)+"最短路径长度为:"+d[v2]);
System.out.print("最短路径为:");
int w;
while(!st.isEmpty()){
w=st.pop();
System.out.print(valueOffCex(w)+"-->");
}
System.out.println(valueOffCex(v2));
}
public void prim(){//prim算法实现最小生成树
System.out.println("请输入遍历的源点名称:");
char v=st.next().charAt(0);
int p=indexOfVex(v);
if(p<0||p>vexnum)
throw new ArrayIndexOutOfBoundsException();
int[][] gm=edges;
int[] cost=new int[vexnum];//存储该点到达最近节点的距离
int[] tree=new int[vexnum];//记录最近的哪个点的标号
int vnum=vexnum;
boolean[] flag=new boolean[vnum];//标记该点是否被访问 被访问的我们视为已经找到了距离该点最近的点
int i,j,k=0,mincost;
for(i=0;i<vnum;i++){//将源点到达各个点的距离赋给数组
cost[i]= gm[p][i];
tree[i]=p;//顶点p到达的顶点为i
}
tree[p]=-1;//防止进入死循环造成越界异常 将源点的最近的节点的标号设为1
flag[p]=true;//以p为第一个树根节点
for(i=0;i<vnum-1;i++){
mincost=MAXVALUE;
for(j=0;j<vnum;j++){
if(flag[j]==false && cost[j]<mincost){
mincost=cost[j];
k=j;//k表示到邻接顶点k到边权小
}
}
flag[k]=true;//表示第k顶点已经加入U集合
for(j=0;j<vnum;j++){
if(flag[j]==false&&gm[k][j]<cost[j]){
cost[j]=gm[k][j];
tree[j]=k;
}
}
}
pntPrimTree(tree,cost,p);
System.out.println("^");
}
public void pntPrimTree(int[] tree,int[] cost,int begin){//输出最小生成树
int N=tree.length;
for(int i=0;i<N;i++){
if(tree[i]==begin){
System.out.print("("+valueOffCex(begin)+","+valueOffCex(i)+")["+cost[i]+"] --> ");
pntPrimTree(tree,cost,i);
}
}
}
public int getNumOfVertex() {//返回节点数
return vexnum;
}
public int indexOfVex(char v) {//定位点的位置
for(int i=0;i<vexnum;i++){
char sss=vexs[i].vname;
if(sss==v){
return i;
}
}
return -1;
}
public char valueOffCex(int v){//定位指定位置的顶点
if(v<0||v>vexnum)
throw new ArrayIndexOutOfBoundsException();
return vexs[v].vname;
}
public int getEdge(int v1, int v2) {//获取边
if(v1<0 || v2<0 || v1>=vexnum || v2>vexnum)
throw new ArrayIndexOutOfBoundsException();
return edges[v1][v2];
}
}
节点数据类public class Vex {//节点的数据类型
char vname; //节点名称
public Vex(char ch){
this.vname = ch;
}
}