图的创建及相关操作

题目要求:
以邻接矩阵和邻接表作为图的存储结构,分别实现增加、删除边,增加、删除顶点,求各顶点的度(有向图包括入度和出度),存储结构之间的转换,判断图的连通性和输出连通分量的个数,给出顶点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;
			}				
		}
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值