题目要求:
以邻接矩阵和邻接表作为图的存储结构,分别实现增加、删除边,增加、删除顶点,求各顶点的度(有向图包括入度和出度),存储结构之间的转换,判断图的连通性和输出连通分量的个数,给出顶点u和v,判断u到v是否存在路径,若存在,则输出u到v的一条简单路径。
邻接矩阵:
package keshe;
import java.util.Scanner;
//图的邻接矩阵的存储结构
public class MGraph implements IGraph{
private GraphKind kind; // 图的种类标志
private int vexNum, arcNum; // 图的当前顶点数和边数
private Object[] vexs; // 顶点集合
private int[][] arcs; // 邻接矩阵
//无参构造
public MGraph(){
this(null, 0, 0, null, null);
}
//含参构造
public MGraph(GraphKind kind, int vexNum, int arcNum, Object[] vexs, int[][] arcs) {
this.kind = kind;
this.vexNum = vexNum;
this.arcNum = arcNum;
this.vexs = vexs;
this.arcs = arcs;
}
//创建图
public void createGraph() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入图的类型:(UDG、DG)");
kind=GraphKind.valueOf(sc.next());//从键盘键入
switch (kind) {//选择开关
case UDG:
createUDG(); //构造无向图
return;
case DG:
createDG(); //构造有向图
return;
}
}
//创建有向图 设权值为1
public void createDG(){
Scanner sc=new Scanner(System.in);
System.out.println("请分别输入图的顶点数、图的边数");
vexNum=sc.nextInt();//从键盘键入顶点的数目
arcNum=sc.nextInt();//从键盘键入边的总数
vexs=new Object[vexNum];//创建一个vexNum大小的数组,用来存放vex
System.out.println("请输入图的各个顶点;");
for(int i=0;i<vexNum;i++){//循环向数组中输入顶点
vexs[i]=sc.next();
}
arcs=new int[vexNum][vexNum];//创建邻接矩阵
for(int i=0;i<vexNum;i++){//矩阵初始化,权值都赋值为0
for(int j=0;j<vexNum;j++){
arcs[i][j]=0;
}
}
System.out.println("请输入各个边的两个顶点");
for(int k=0;k<arcNum;k++){//输入矩阵的信息,都将权值赋值为1
int i=locateVex(sc.next());//若有着两个节点,存储矩阵的值为1
int j=locateVex(sc.next());
arcs[i][j]=1;//有向图ij和ji不同
}
}
//创建无向图 设权值为1
public void createUDG(){
Scanner sc=new Scanner(System.in);
System.out.println("请分别输入图的顶点数、图的边数");
vexNum=sc.nextInt();//从键盘键入顶点的数目
arcNum=sc.nextInt();//从键盘键入边的总数
vexs=new Object[vexNum];//创建一个vexNum大小的数组,用来存放vex
System.out.println("请输入图的各个顶点;");
for(int i=0;i<vexNum;i++){//循环向数组中输入顶点
vexs[i]=sc.next();
}
arcs=new int[vexNum][vexNum];//创建邻接矩阵
for(int i=0;i<vexNum;i++){//矩阵初始化,权值都赋值为0
for(int j=0;j<vexNum;j++){
arcs[i][j]=0;
}
}
System.out.println("请输入各个边的两个顶点");
for(int k=0;k<arcNum;k++){//输入矩阵的信息,都将权值赋值为1
int i=locateVex(sc.next());
int j=locateVex(sc.next());
arcs[i][j]=arcs[j][i]=1;//相同
}
}
//返回顶点数
public int getVexNum() {
return vexNum;
}
public void setVexNum(int n){//置节点总数
vexNum=n;
}
//返回边数
public int getArcNum() {
return arcNum;
}
public void setArcNum(int n){//置边总数
arcNum=n;
}
//返回图的种类标志
public GraphKind getKind() {
return kind;
}
public void setKind(GraphKind k){//置图的种类
kind=k;
}
public Object[] getVexs(){
return vexs;//得到节点的数组
}
public void setVexs(Object a[]){
vexs=a;//置结点
}
public int[][] getArcs(){//得到边的邻接矩阵
return arcs;
}
public void setArcs(int a[][]){
arcs=a;//置边
}
//返回v表示结点的值,0<=v<vexNum
public Object getVex(int v){//得到节点的值
if(v<0||v>=vexNum){
System.out.println("第"+v+"个顶点不存在");
}
return vexs[v];//返回数组的第v的节点的值
}
//给定顶点值vex,返回其在图中的位置,如果图中不包含此顶点,则返回-1
public int locateVex(Object vex) { //查找vex节点的位置
for(int v = 0; v < vexNum; v++){
if(vexs[v].equals(vex))//两个值进行比对 找到了返回数组的下标
return v;
}
return -1;//找不到返回-1
}
//返回v的第一个邻接点的位置,若v没有邻接点则返回-1,0=<v<vexNum
public int firstAdjVex(int v){
if(v<0||v>=vexNum){//如果输入的v值小于0或者大于顶点数目:输入不合法,返回第v个顶点不存在
System.out.println("第"+v+"个顶点不存在");
}
for(int i=0;i<vexNum;i++){
if(arcs[v][i]==1){//从v到i有边
return i;
}
}
return -1;
}
//返回v相对于w的下一个邻接点,若w是v的最后一个邻接点,则返回-1,其中0=<v,w<vexNum
public int nextAdjVex(int v,int w){
if(v<0||v>=vexNum){//输入不合法
System.out.println("第"+v+"个顶点不存在");
}
for(int i=w+1;i<vexNum;i++){//寻找w以后的邻接点
if(arcs[v][i]==1){//如果存在这条边则返回这个顶点的位置 i是w的后一个。
return i;
}
}
return -1;
}
//增加边
public void addArc(){
Scanner sc=new Scanner(System.in);
System.out.println("请输入所加边的两个顶点");
int i=locateVex(sc.next());//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给i
int j=locateVex(sc.next());//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给j
switch(kind){
case DG:
if(arcs[i][j]==0){
arcs[i][j]=1;//置1 此时有边相连,即加边成功
arcNum++;
}
return;
case UDG:
if(arcs[i][j]==0){
arcs[i][j]=arcs[j][i]=1;//无向图ij和ji一样
arcNum++;
}
return;
}
}
//删除边
public void deleteArc(){
Scanner sc=new Scanner(System.in);
System.out.println("请输入所删除边的两个顶点");
int i=locateVex(sc.next());//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给i
int j=locateVex(sc.next());//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给j
switch(kind){
case DG:
if(arcs[i][j]==1){//如果这条边存在,那么就将其删除(将其权值赋值为0)
arcs[i][j]=0;
arcNum--;//边数自减1
}
return;
case UDG:
if(arcs[i][j]==1){
arcs[i][j]=arcs[j][i]=0;
arcNum--;
}
return;
}
}
//增加点
public void addVex(){
System.out.println("请输入所加入的顶点");
Scanner sc=new Scanner(System.in);
Object vexs2[]=new Object[vexNum+1];//数组的扩容 新建一个数组比原数组大一个
vexs2[vexNum]=sc.next();
//复制数组arraycopy(源数组,源数组要复制的起始位置,目的数组,目的数组放置的起始位置,复制的长度)
System.arraycopy(vexs, 0, vexs2, 0, vexNum);
vexs=new Object[vexNum+1];//顶点数组扩容
//复制数组arraycopy(源数组,源数组要复制的起始位置,目的数组,目的数组放置的起始位置,复制的长度)
System.arraycopy(vexs2, 0, vexs, 0, vexNum+1);
//arcs2起到中间变量的作用
int arcs2[][]=new int[vexNum+1][vexNum+1];
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
arcs2[i][j]=arcs[i][j];//把数组arcs复制到arcs2里面来
}
}
vexNum++;
//将最外圈的赋值为0,新加入的点初始值,因为是从 0号位开始进行矩阵行列计数的 此时的vexNum已经自加1
for(int i=0;i<vexNum;i++){
arcs2[vexNum-1][i]=0;
arcs2[i][vexNum-1]=0;
}
//此时vexNum已经+1,重新将arc定义新的vexNum大小的二维数组存放矩阵
arcs=new int[vexNum][vexNum];
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
arcs[i][j]=arcs2[i][j];//把arcs2的矩阵中的数据传给完成扩容后的arcs矩阵
}
}
}
public void deleteArc(int i,int j ){//此方法用于下面删除点的时候用来调用的方法
switch(kind){
case DG:
if(arcs[i][j]==1){//把有边相连的置0,就不相连了
arcs[i][j]=0;
arcNum--;
}
return;
case UDG:
if(arcs[i][j]==1){
arcs[i][j]=arcs[j][i]=0;
arcNum--;
}
return;
}
}
//删除点
public void deleteVex(){
System.out.println("请输入所删除的顶点:");
Scanner sc=new Scanner(System.in);
int i=locateVex(sc.next());//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给i
for(int n=0;n<vexNum;n++){//删除边操作
deleteArc(i,n);
deleteArc(n,i);
}
for(int j=i;j<vexNum-1;j++){
vexs[j]=vexs[j+1];//从删除结点位置开始,将后边的结点赋值给前边的结点,用来补充前边的结点
}
vexs[vexNum-1]=null;//最后一个结点置为空值
//进行的是边的操作
for(int k=0;k<vexNum;k++){
for(int l=i;l<vexNum-1;l++){
arcs[l][k]=arcs[l+1][k];//把后边一行的权值赋值到前边空缺的一行,以此类推,直至完成为止
}
arcs[vexNum-1][k]=0;//最后一行赋值为0
}
for(int k=0;k<vexNum;k++){
for(int l=i;l<vexNum-1;l++){
arcs[k][l]=arcs[k][l+1];//把后边一列的权值赋值到前边空缺的一列,以此类推,直至完成为止//将刚才移动列以后的矩阵进行 的移动
}
arcs[k][vexNum-1]=0;//最后一列赋值为0
}
vexNum--;//结点数自减1 完成矩阵的减小
}
//求各个顶点的度
public void getDegree(int v){
//初始化出度和入度都为0
int inDegree=0;
int outDegree=0;
int UDegree=0;//在无向图中用
int Degree=0;//在有向图中用
switch(kind){
case DG://有向图中
for(int i=0;i<vexNum;i++){
if(arcs[v][i]==1)//由v指向其他结点的边 出度+1
outDegree++;
}
for(int j=0;j<vexNum;j++){
if(arcs[j][v]==1)//由其他结点指向v的边 入度+1
inDegree++;
}
System.out.println("顶点"+getVex(v)+"的入度为:"+inDegree+" "+"出度为:"+outDegree+" ");
Degree=inDegree+outDegree;
System.out.println("顶点"+getVex(v)+"的度为:"+Degree);
return;
case UDG://无向图
for(int i=0;i<vexNum;i++){
if(arcs[v][i]==1)//只要存在边度自加1
UDegree++;
}
System.out.println("顶点"+getVex(v)+"度为"+UDegree);
return;
}
}
//递归调用得到度的方法
//得到每个节点的度
public void getDegree(){
for(int i=0;i<vexNum;i++){
getDegree(i);
}
}
//输出矩阵 存在边的话权值为1 不存在的话权值为0
public void display(){
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
if(arcs[i][j]==1){//如果这条边存在,通过getVex方法,得出结点1->结点2
System.out.print(getVex(i)+"->"+getVex(j)+" ");
}
}
}
System.out.println();
//得到的是邻接矩阵的形式
System.out.println("该图的存储结构为:");
for(int i = 0; i < vexNum; i++){
for(int j = 0; j< vexNum; j++){
System.out.print(arcs[i][j] + " ");
}
System.out.println();
}
}
}
``
邻接表:
节点类——
```java
package keshe;
public class VNode {
public Object data;//顶点信息
public ArcNode firstArc;//指向第一条依附于该顶点的弧
public VNode(){
this(null,null);//无参数的构造方法
}
public VNode(Object data){//只有一个参数的构造方法
this(data,null);
}
public VNode(Object data,ArcNode firstArc){//两个参数的构造方法
this.data=data;
this.firstArc=firstArc;
}
}
package keshe;
public class ArcNode {//边结点的定义
public int adjVex;//该弧所指向的顶点位置
public int value;
public ArcNode nextArc;//指向下一条弧(边)
public ArcNode(){//无参数的构造方法
this(-1,0,null);
}
public ArcNode(int adjVex){//一个参数的构造方法
this(adjVex,0,null);
}
public ArcNode(int adjvex,int value) {//两个参数的构造方法
this(adjvex,value,null);
}
public ArcNode(int adjVex,int value,ArcNode nextArc){//三个参数的构造方法
this.adjVex=adjVex;
this.nextArc=nextArc;
this.value=value;
}
}
方法——
package keshe;
import java.util.Scanner;
public class ALGraph implements IGraph{//继承接口
private GraphKind kind;//定义图的种类
private int vexNum,arcNum;//定义顶点总数和边总数
private VNode vexs[];// 顶点数组
public ALGraph(){//无参的构造方法
this(null,0,0,null);
}
public ALGraph(GraphKind kind,int vexNum,int arcNum,VNode vexs[]){//3个参数的构造方法
this.vexNum=vexNum;
this.arcNum=arcNum;
this.vexs=vexs;
this.kind=kind;
}
public GraphKind getKind(){
return kind;//得到图的种类
}
public void setKind(GraphKind k){
kind=k;//置图的种类
}
public int getArcNum(){
return arcNum;//得到边的总数
}
public void setArcNum(int n){
arcNum=n;//置边总数
}
public int getVexNum(){
return vexNum;//得到顶点的总数
}
public void setVexNum(int n){
vexNum=n;//置定点总数
}
public VNode[] getVexs(){
return vexs;//得到顶点的集合
}
public void setVexs(VNode p[]){
vexs=p;//置顶点
}
//创建图的算法
public void createGraph(){
Scanner sc=new Scanner(System.in);
System.out.println("请输入图的类型:");
kind=GraphKind.valueOf(sc.next());//从键盘键入图的类型
switch(kind){//switch的用法是判断case后面的表达式和switch后面的表达式是否相匹配,
//一旦case匹配,就会顺序执行后面的程序代码,而不管后面的case是否匹配,直到遇见break。
case UDG:
createUDG();//创建无向图
break;
case DG:
createDG();//创建有向图
break;
}
}
//创建无向图的算法
public void createUDG(){
Scanner sc=new Scanner(System.in);
System.out.println("请分别输入图的顶点数、图的边数");
vexNum=sc.nextInt();//从键盘键入顶点的数目
arcNum=sc.nextInt();//从键盘键入边的数目
vexs=new VNode[vexNum];//顶点数组
System.out.println("请分别输入图的各顶点:");
for(int i=0;i<vexNum;i++){
vexs[i]=new VNode(sc.next());//每个顶点都是从键盘键入
}
System.out.println("请输入各边的顶点:");
for(int i=0;i<arcNum;i++){
int v=locateVex(sc.next());//起点
int u=locateVex(sc.next());//终点
addArc(v,u);//因为无向所以加入两条边
addArc(u,v);
}
}
//创建有向图
public void createDG(){
Scanner sc=new Scanner(System.in);
System.out.println("请分别输入图的顶点数、图的边数");
vexNum=sc.nextInt();//从键盘键入顶点的数目
arcNum=sc.nextInt();//从键盘键入边的总数
vexs=new VNode[vexNum];//顶点数组
System.out.println("请分别输入图的各顶点:");
for(int i=0;i<vexNum;i++){//循环输入图的结点
vexs[i]=new VNode(sc.next());
}
System.out.println("请输入各边的顶点:");
for(int i=0;i<arcNum;i++){
int v=locateVex(sc.next());//起点
int u=locateVex(sc.next());//终点
addArc(v,u);//加边方向固定
}
}
public int locateVex(Object vex){//查找结点的位置
for(int i=0;i<vexNum;i++){
if(vexs[i].data.equals(vex))//找到结点
return i;
}
return -1;
}
//将边结点插入对应表(头插法)
public void addArc(int v,int u){
ArcNode arc=new ArcNode(u);//新建一个边结点
arc.nextArc=vexs[v].firstArc;//将之前指向u的firstArc赋值给新插入结点的nextArc
vexs[v].firstArc=arc;//再把新插入的结点地址传给重新赋值给firstArc
}//加边算法
public void addArc(){
System.out.println("请输入所加边的两个顶点");
Scanner sc=new Scanner(System.in);
int i=locateVex(sc.next());通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给i;
int j=locateVex(sc.next());//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给j
switch(kind){//switch的用法是判断case后面的表达式和switch后面的表达式是否相匹配,
//一旦case匹配,就会顺序执行后面的程序代码,而不管后面的case是否匹配,直到遇见break。
case DG:
addArc(i,j);//有方向
arcNum++;
break;
case UDG://无方向
addArc(i,j);//加两条边
addArc(j,i);
arcNum++;
default://default就是如果没有符合的case就执行它,default并不是必须的.
break;
}
}
//删边算法(两个参数)
public void deleteArc(int v,int u){
ArcNode q=vexs[v].firstArc;//v的第一个邻接点
if(q!=null) {
if(q.adjVex==u)//定位所想要删除的位置
vexs[v].firstArc=q.nextArc;//指向下一个邻接点
while(q.nextArc!=null){
if(q.nextArc.adjVex==u){//下个节点的位置是u
q.nextArc=q.nextArc.nextArc;//往下指
break;//退出while循环(break当前循环)
}
q=q.nextArc;
}
}
}//自己从键盘键入要删除的结点的起点和终点
public void deleteArc(){
System.out.println("请输入所删除边的两个顶点");
Scanner sc=new Scanner(System.in);
int c=locateVex(sc.next());//起点位置//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给c
int d=locateVex(sc.next());//终点位置//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给d
switch(kind){
case DG:
deleteArc(c,d);
arcNum--;//删除之后边的总数减1
break;
case UDG:
deleteArc(c,d);
deleteArc(d,c);
arcNum--;//边的总数减1
break;
default:
break;
}
}
//添加结点的算法
public void addVex(){
System.out.println("请输入所加顶点");
Scanner sc=new Scanner(System.in);
VNode s=new VNode(sc.next());//从键盘输入要插入的节点的值
VNode vexs2[]=new VNode[vexNum+1];//新建一个数组//数组的扩容 新建一个数组比原数组大一个
System.arraycopy(vexs, 0, vexs2, 0, vexNum);//复制数组arraycopy(源数组,源数组要复制的起始位置,目的数组,目的数组放置的起始位置,复制的长度)
vexs2[vexNum]=s;vex2起到中间变量的作用
vexs=new VNode[vexNum+1];
System.arraycopy(vexs2,0,vexs,0,vexNum+1);//复制数组arraycopy(源数组,源数组要复制的起始位置,目的数组,目的数组放置的起始位置,复制的长度)
vexNum++;//顶点总数加一
}
//删除结点
public void deleteVex(){
System.out.println("请输入所删除顶点");
Scanner sc=new Scanner(System.in);
int n=locateVex(sc.next());//通过locateVex方法,将输入的数据元素转换为数组的下标,赋值给n
vexs[n]=null;//把他置空
for(int j=n;j<vexNum-1;j++){
vexs[j]=vexs[j+1];//从删除结点位置开始,将后边的结点赋值给前边的结点,用来补充前边的结点
}
vexs[vexNum-1]=null;//最后一个节点置空
for(int i=0;i<vexNum-1;i++){
deleteArc(i,n);//递归调用删除边的算法
ArcNode p=vexs[i].firstArc;//p为i的第一个邻接点
while(p!=null){
if(p.adjVex>n){
p.adjVex--;//因为删去了一个节点,所以其他的节点依次前移,因此其下标各减去1
}
p=p.nextArc;//往后走
}
}
vexNum--;
}
//找到第一个邻接点的算法
public int firstAdjVex(int v){
if(v<0||v>=vexNum)//未找到
System.out.println("第"+v+"个顶点不存在");
VNode vex=vexs[v];
if(vex.firstArc!=null)
return vex.firstArc.adjVex;//返回第一个邻接点的位置
else
return -1;//找不到返回-1
}
//找到v,相对于w的下一个邻接点
public int nextAdjVex(int v,int w){
if(v<0||v>=vexNum)
System.out.println("第"+v+"个顶点不存在");
VNode vex=vexs[v];//vex为v位置上的结点
ArcNode arcvw=null;//置空
for(ArcNode arc=vex.firstArc;arc!=null;arc=arc.nextArc){//arc有邻接点,arc不空,arc还有下一个邻接点
if(arc.adjVex==w){
arcvw=arc;//如果arc的下一个临界点就是w就跳出循环
break;
}
}
if(arcvw!=null&&arcvw.nextArc!=null){//下一个邻接点不空且该节点不空
return arcvw.nextArc.adjVex;//返回下一个邻接点的位置
}
else
return -1;
}
//得到结点的值
public Object getVex(int v) {
if(v<0||v>=vexNum)
System.out.println("第"+v+"个顶点不存在");
return vexs[v].data;//返回节点的值
}
//求度的算法
public void getDegree(int v){
int in=0;//入度
int out=0;//出度
int num=0;//总数
switch(kind){
case DG:
ArcNode p=vexs[v].firstArc;
while(p!=null){
p=p.nextArc;//出度加一
out++;
}
for(int i=0;i<vexNum;i++){
for(ArcNode ar=vexs[i].firstArc;ar!=null;ar=ar.nextArc){//adjvex该弧所指向的顶点位置
if(ar.adjVex==v){
in++;//入度加一
break;
}
}
}
System.out.println("顶点"+getVex(v)+"的入度为:"+in+"出度为:"+out);
break;
case UDG:
ArcNode q=vexs[v].firstArc;
while(q!=null){
q=q.nextArc;//对于无向图来说不分入度和出度所以都加1
num++;
}
System.out.println("顶点"+getVex(v)+"的度为:"+num);
break;//switch的用法是判断case后面的表达式和switch后面的表达式是否相匹配,
//一旦case匹配,就会顺序执行后面的程序代码,而不管后面的case是否匹配,直到遇见break。
default://default就是如果没有符合的case就执行它,default并不是必须的.
break;
}
}
//递归调用求度算法
public void getDegree(){
for(int i=0;i<vexNum;i++){
getDegree(i);
}
}
//展示图
public void display(){
for(int i=0;i<vexNum;i++){
for(ArcNode p=vexs[i].firstArc;p!=null;p=p.nextArc){//如果这条边存在,通过getVex方法,得出结点1->结点2
System.out.print(getVex(i)+"->"+getVex(p.adjVex)+" ");//adj就是i的第一个邻接点
}
}
System.out.println();
//得到的是邻接表的形式
System.out.println("该图的存储结构:");
for(int i=0;i<vexNum;i++){
System.out.print(vexs[i].data);
for(ArcNode p=vexs[i].firstArc;p!=null;p=p.nextArc){//第一个邻接点的位置上的节点不空,找下一个邻接点
System.out.print("-->"+getVex(p.adjVex));
}
System.out.println();
}
}
}
图的种类:
package keshe;
public enum GraphKind {//枚举法列举无向图有向图
UDG, // 无向图(UnDirected Graph)
DG, // 有向图(Directed Graph)
}
两种存储形式的互相转换、求最短路径、连通分量个数的确定:
package keshe;
import java.util.Queue;
import java.util.LinkedList;
public class Transform {
//转换:矩阵到邻接表
public ALGraph convertMToAL(MGraph G){
ALGraph G2=new ALGraph();//新建一个邻接表存储的图
G2.setKind(G.getKind());//得到图的种类标志 : 无向图还是有向图
G2.setVexNum(G.getVexNum());//获取邻接矩阵中结点的个数
G2.setArcNum(G.getArcNum());//获取邻接矩阵中边的条数
Object a[]=G.getVexs();//把获得的结点存放到数组a[]
VNode b[]=new VNode[G.getVexNum()];//新建一个结点数目大小的数组 是顶点结点类的
for(int i=0;i<G.getVexNum();i++){
b[i]=new VNode(a[i]);
}
G2.setVexs(b);
int c[][]=G.getArcs();//把邻接矩阵中的边存放到二维数组c中
for(int i=0;i<G.getVexNum();i++){
for(int j=0;j<G.getVexNum();j++){
if(c[i][j]==1){
G2.addArc(i,j);//把边结点加到邻接表存储的图里
}
}
}
return G2;
}
//转换:邻接表到邻接矩阵
public MGraph convertALToM(ALGraph g){
MGraph G3=new MGraph();
G3.setKind(g.getKind());//得到图的种类标志 : 无向图还是有向图
G3.setVexNum(g.getVexNum());//获取邻接矩阵中结点的个数
G3.setArcNum(g.getArcNum());//获取邻接矩阵中边的条数
VNode a[]=g.getVexs();//把获得的结点存放到数组a[]
Object b[]=new Object[g.getVexNum()];//新建一个结点数目大小的数组 是顶点结点类的
for(int i=0;i<g.getVexNum();i++){
b[i]=a[i].data;
}
G3.setVexs(b);//置顶点在邻接矩阵存储结构里
int c[][]=new int[g.getVexNum()][g.getVexNum()];//二维数组来放邻接矩阵
for(int i=0;i<g.getVexNum();i++){
for(int j=0;j<g.getVexNum();j++){
c[i][j]=0;//初始化0
}
}
for(int j=0;j<g.getVexNum();j++){
ArcNode p=a[j].firstArc;//p是a[j]的第一个邻接点
while(p!=null){
c[j][p.adjVex]=1;//j到p有边
p=p.nextArc; //p就等于p的下一个邻接点
}
}
G3.setArcs(c);
return G3;
}
private boolean visited[];//访问标志数组
private boolean find;
//广度优先
public void BFS(IGraph G,int v){
visited[v]=true;//v已被访问
System.out.print(G.getVex(v).toString()+" ");
Queue Q=new LinkedList();//辅助队列Q,但是LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
//使用offer()来加入元素,使用poll()循环获取并移出元素,//使用element()或者peek()方法取得顶端第一个元素。
Q.offer(v);//v入队列
while(!Q.isEmpty()){
int u=(Integer)Q.poll();//队头的元素出队列并赋值给u,此时u等于v
for(int w=G.firstAdjVex(u);w>=0;w=G.nextAdjVex(u,w)){//w是u的第一个邻接点的位置即adjvex,u相对于w的下一个邻接点
if(!visited[w]){//w为u尚为访问的邻接顶点
visited[w]=true;//就访问它
System.out.print(G.getVex(w).toString()+" ");
Q.offer(w); //w入队
}
}
}
}
//判断图的连通性,图中任意两个顶点之间都连通,则称该图为连通图,否则,将其中的较大连通子图称为连通分量。
//强连通图只有一个强连通分量,即本身,非强连通图有多个强连通分量。
//任何连通图的连通分量只有一个,即为其本身。
public void judge_BFS(IGraph G){
int j=0;
visited=new boolean[G.getVexNum()];
for(int i=0;i<G.getVexNum();i++){
//初始化数组visited,都没有访问
visited[i]=false;//i没有被访问
}
for(int i=0;i<G.getVexNum();i++){
if(!visited[i]){//没有被访问的话
System.out.println("第"+ ++j+"个连通分量是:");//判断连通分量是否大于1,判断连通分量的个数
BFS(G,i);
System.out.println();
}
}
//若连通分量大于1,就说明此图是非连通图,
if(j>1)
System.out.println("该图是非连通图");
else
System.out.println("该图是连通图");
System.out.println("连通分量是"+j);
}
//寻找简单路径
public void FindPath(IGraph G,int u,int v,int path[],int d){
int w,i;
visited[u]=true;
d++;
path[d]=u;//记录所寻找的简单路径
if(u==v){
find=true;//find为是否找到简单路径的标志
System.out.println("一条简单路径为:");
for(i=0;i<=d;i++){//输出简单路径
System.out.print(G.getVex(path[i]).toString()+" ");
}
System.out.println();
return;
}
//在u!=v的时候,寻找第一个邻接点,找不到返回-1
w=G.firstAdjVex(u);
while(w!=-1){// w不存在的情况下w=-1
if(visited[w]==false)//节点还未被访问
FindPath(G,w,v,path,d);
w=G.nextAdjVex(u,w); //找u除了w以外的其他邻接点,找不到返回-1
}
}
//判断路径
public void Path(IGraph G,int u,int v){
find=false;//没有路径
int path[]=new int[100];
visited=new boolean[G.getVexNum()];
for(int i=0;i<G.getVexNum();i++){
//访问标志数组初始化
visited[i]=false;
}
FindPath(G,u,v,path,-1);//调用FindPath方法
if(!find)
System.out.println("不存在简单路径");
}
}
接口:
```java
package keshe;
public interface IGraph {//接口类里面包括所有需要用到的方法和变量
void createGraph();//创建图
int getVexNum();//顶点总数
int getArcNum();//边的总数
Object getVex(int v);//得到第v个位置上的结点值是object型的
int locateVex(Object vex);//查找vex顶点的位置
int firstAdjVex(int v);//第一个邻接点的位置
int nextAdjVex(int v,int w);//返回v相对于w的下一个邻接点,若w是v的最后一个邻接点,则返回-1,其中:0≤v, w<vexNum;nextAdjVex(v,w)
void addArc();//加边方法
void deleteArc();//删除边的方法
void addVex();//添加结点的方法
void deleteVex();//删除节点的算法
void getDegree();//求顶点的度,有向图包括入度和出度
void display();
}
测试:
package keshe;
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
IGraph g=null;
Transform g1=new Transform();
Scanner sc=new Scanner(System.in);
boolean flag=true;//设立一个标志变量flag,让选择功能的语句一直输出
while(flag){
System.out.println("1.创建一个以邻接矩阵为存储结构的图 2.创建一个以邻接表为存储结构的图 ");
System.out.println("3.增加一条边 4.删除一条边");
System.out.println("5.增加一个顶点 6.删除一个顶点");
System.out.println("7.求各顶点的度 8.存储结构的转换 9.判断图的连通性并输出连通分量 10.判断两顶点是否存在简单路径");
System.out.println("请输入所选操作:");
int select=sc.nextInt();
switch(select){switch的用法是判断case后面的表达式和switch后面的表达式是否相匹配,
//一旦case匹配,就会顺序执行后面的程序代码,而不管后面的case是否匹配,直到遇见break。
case 1:
g=new MGraph();//新建一个临接矩阵存储的图
g.createGraph();//创建一个图
g.display();//展示图
break;
case 2:
g=new ALGraph();//新建一个邻接链表存储的图
g.createGraph();//创建一个图
g.display();//展示图
break;
case 3:
g.addArc();//增加边
g.display();
break;
case 4:
g.deleteArc();//删除边
g.display();
break;
case 5:
g.addVex();//增加顶点
g.display();
break;
case 6:
g.deleteVex();//删除顶点
g.display();
break;
case 7:
g.getDegree();//得到度
break;
case 8:
System.out.println("请输入你要选择的转换方式1或2!");
System.out.println("1:从邻接矩阵转为邻接表 ;2:从邻接表转为邻接矩阵 ");
int select1=sc.nextInt();
//选择两种转变方式,1:从邻接矩阵转为邻接表 ;2:从邻接表转为邻接矩阵
switch(select1){
case 1:
g1.convertMToAL((MGraph) g).display();//把邻接矩阵存储的结构变成邻接表存储的结构
break;
case 2:
g1.convertALToM((ALGraph) g).display();//把邻接表存储的结构转化成邻接矩阵存储的结构
break;
}
break;
case 9:
g1.judge_BFS(g);//判断连通分量
break;
case 10:
System.out.println("请输入所查找简单路径的两顶点");
int v=g.locateVex(sc.next());
int u=g.locateVex(sc.next());
g1.Path(g, v, u);//在图g里寻找从v到u的路径
break;
}
}
}
}