反圈法解决最短路径问题

反圈发求最短路径算法介绍:

 

Java实现代码:

 

package sy3;

/*
 * 构造图的边结构
 */
public class Arc {
	public Vnode firstNode;//定义边的始点
	public Vnode LastNode;//定义边的中点
	public Arc NextArc;//定义边的下一条边
	public boolean flag;//标识边是否访问过
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public Arc getNextArc() {
		return NextArc;
	}
	public void setNextArc(Arc nextArc) {
		NextArc = nextArc;
	}
	public Vnode getLastNode() {
		return LastNode;
	}
	public void setLastNode(Vnode lastNode) {
		LastNode = lastNode;
	}
	public int weight;//定义边对应的权值
	//建立一条具有权值weight的边
	public Arc(int weight){
		this(null,null,null,weight);
	}
	public Arc(Vnode firstNode,Vnode LastNode,int weight){
		this(firstNode,LastNode,null,weight);
	}
	public Arc(Vnode firstNode,Vnode LastNode,Arc NextArc,int weight){
		this.firstNode=firstNode;
		this.LastNode=LastNode;
		this.NextArc=NextArc;
		this.weight=weight;
	}
	public Vnode getFirstNode() {
		return firstNode;
	}
	public void setFirstNode(Vnode firstNode) {
		this.firstNode = firstNode;
	}
	public int getWeight() {
		return weight;
	}
	public void setWeight(int weight) {
		this.weight = weight;
	}
}


package sy3;

public enum GraphKind {
	Gra,//无向图
	Digra,//有向图
	FlowGra;//网络流图
}


package sy3;

/*
 * 新建一个图结构接口
 */
public interface IGraph {
	public void createGraph() throws Exception;//新建图
	public int getVexNum();//返回图中顶点数
	public int getArcNum();//返回图中边数
	public String getVex(int v) throws Exception;//给定顶点v所处的位置,返回顶点对应顶点名称
	public int locateVex(String v) throws Exception;//给定顶点名称,返回对应顶点的位置
	public int firstAdjVex(int v) throws Exception;//给定顶点所处位置,返回该顶点的第一个邻接点
	public int nextAdjVex(int v,int w) throws Exception;//给定顶点v以及它的邻接点w对应的位置,返回顶点相对于w的下一个邻接点的位置
}


package sy3;

import java.util.Scanner;
/*
 * 构造图结构
 */
public class MyGraph implements IGraph {
	public final static int Inf=Integer.MAX_VALUE;//表示整型无穷大,用来标识两个顶点是否有边相连
	public GraphKind kind;//定义图的类型
	public int vexNum,arcNum;//定义顶点以及边的数目
	public int vw[];//顶点位置集
	public Vnode[] V;//顶点结构
	public Arc[] A;//边结构
	public String[] v;//顶点名称集
	public int[][] arcs;//邻接矩阵也就是边的关系
	public int[][][] F;//网络流图构成的邻接矩阵
	public void createGraph() throws Exception {
		Scanner s=new Scanner(System.in);
		System.out.println("请输入所要构建的图的类型:");
		GraphKind kind=GraphKind.valueOf(s.next());
		//开关语句选择构图方法
		switch(kind){
		case Gra:
			createGra();//构造无向图
			return;
		case Digra:
			createDigra();//构造有向图
			return;
		case FlowGra:
			createFlow();//构造有向图
			return;
		}	
	}
	/*
	 * 构造网络流图
	 */
	private void createFlow() throws Exception {
		Scanner s=new Scanner(System.in);
		System.out.println("请分别输入图的顶点数和边数;");
		vexNum=s.nextInt();//输入顶点数
		arcNum=s.nextInt();//输入边数
		v=new String[vexNum];
		vw=new int[vexNum];
		V=new Vnode[vexNum];
		for(int i=0;i<vexNum;i++){
			V[i]=new Vnode(v[i],vw[i]);//定义顶点集
		}
		System.out.println("请分别输入图的各个顶点:");
		for(int i=0;i<vexNum;i++){
			String S=s.next();
			int vw=s.nextInt();
			V[i].setV(S);
			V[i].setVw(vw);
		}
		//初始化邻接矩阵
		F=new int[vexNum][vexNum][3];
		for(int i=0;i<vexNum;i++){
			for(int j=0;j<vexNum;j++){
				F[i][j][0]=0;//初始化容量值
				F[i][j][1]=0;//初始化流量值
				F[i][j][2]=Inf;//初始化费用值,表示不存在这条弧
			}
		}
		System.out.println("请输入每条边的两个顶点及其相关参数:");
		for(int i=0;i<arcNum;i++){
			int v=locateVex(s.next());//输入起始点
			int u=locateVex(s.next());//输入终点
			for(int j=0;j<3;j++){
				F[v][u][j]=s.nextInt();
			}
		}
	}
	/*
	 * 构造有向图
	 */
	private void createDigra() throws Exception {
		Scanner s=new Scanner(System.in);
		System.out.println("请分别输入图的顶点数和边数;");
		vexNum=s.nextInt();//输入顶点数
		arcNum=s.nextInt();//输入边数
		v=new String[vexNum];
		vw=new int[vexNum];
		V=new Vnode[vexNum];
		A=new Arc[arcNum];
		for(int i=0;i<vexNum;i++){
			V[i]=new Vnode(v[i],vw[i]);//定义顶点集
		}
		System.out.println("请分别输入图的各个顶点:");
		for(int i=0;i<vexNum;i++){
			String S=s.next();
			int vw=s.nextInt();
			V[i].setV(S);
			V[i].setVw(vw);
		}
		//初始化邻接矩阵
		arcs=new int[vexNum][vexNum];
		for(int i=0;i<vexNum;i++){
			for(int j=0;j<vexNum;j++){
				arcs[i][j]=Inf;
			}
		}
		System.out.println("请输入每条边的两个顶点及其权值:");
		for(int i=0;i<arcNum;i++){
			int v=locateVex(s.next());//输入起始点
			int u=locateVex(s.next());//输入终点
			arcs[v][u]=s.nextInt();//输入顶点v到顶点u的弧的权重值
			A[i]=new Arc(V[v],V[u],arcs[v][u]);
		}
	}
	/*
	 * 构造无向图
	 */
	private void createGra() throws Exception {
		Scanner s=new Scanner(System.in);
		System.out.println("请分别输入图的顶点数和边数;");
		vexNum=s.nextInt();//输入顶点数
		arcNum=s.nextInt();//输入边数
		v=new String[vexNum];
		vw=new int[vexNum];
		V=new Vnode[vexNum];
		for(int i=0;i<vexNum;i++){
			V[i]=new Vnode(v[i],vw[i]);//定义顶点集
		}
		System.out.println("请分别输入图的各个顶点:");
		for(int i=0;i<vexNum;i++){
			String S=s.next();
			int vw=s.nextInt();
			V[i].setV(S);
			V[i].setVw(vw);
		}
		//初始化邻接矩阵
		arcs=new int[vexNum][vexNum];
		for(int i=0;i<vexNum;i++){
			for(int j=0;j<vexNum;j++){
				arcs[i][j]=Inf;
			}
		}
		System.out.println("请输入每条边的两个顶点及其权值:");
		for(int i=0;i<arcNum;i++){
			int v=locateVex(s.next());//输入起始点
			int u=locateVex(s.next());//输入终点
			arcs[v][u]=arcs[u][v]=s.nextInt();//输入顶点v到顶点u的弧的权重值
		}
	}
	public int getVexNum() {
		return vexNum;
	}
	public int getArcNum() {
		return arcNum;
	}
	
	//如果已知顶点在邻接矩阵中的位置,返回顶点的名称
	public String getVex(int v) throws Exception{
		if(v<0 || v>vexNum){
			throw new Exception("第"+v+"个顶点不存在");
		}
		return V[v].getV();
	}

	//判断所给的顶点名称是否是图中的顶点,如果是则返回顶点所在的位置
	public int locateVex(String v) throws Exception {
		for(int i=0;i<vexNum;i++){
			//System.out.println(v);
			if(getVex(i).equals(v)){
				//System.out.println(i);
				return i;
			}
		}
		return -1;
	}
	//返回v位置顶点的下一个邻接点的位置
	public int firstAdjVex(int v) throws Exception {
		if(v<0 || v>vexNum){
			throw new Exception("第"+v+"个顶点不存在");
		}
		for(int i=0;i<vexNum;i++){
			if(arcs[v][i]!=-1 && arcs[v][i]<Inf){
				return i;
			}
		}
		return -1;
	}
	//已知始点和终点的位置,返回该边在边集合A中的下标
	public int FirstIndex(int a,int b){
		for(int i=0;i<arcNum;i++){
			if(A[i].getFirstNode().getVw()==a && A[i].getLastNode().getVw()==b){
				return i;
			}
		}
		return -1;
	}
	//返回v位置顶点的下一个邻接点的位置(即查找v的下一个邻接点)
	public int nextAdjVex(int v, int w) throws Exception {
		if(v<0 || v>=vexNum){
			throw new Exception("第"+v+"个顶点不存在!");
		}// if
		Vnode vex=V[v];//找到位置v上的顶点
		Arc arcvw=null;//初始化一条边
		for(Arc arc=vex.getAw()[0];arc!=null;arc=arc.NextArc){
			if(arc.getFirstNode().getVw()==w){
				arcvw=arc;
				break;
			}// if
		}// for
		if(arcvw!=null && arcvw.getNextArc()!=null){
			return arcvw.getNextArc().getFirstNode().getVw();
		}
		else
			return -1;
	}
	public static void main(String args[]) throws Exception{
		int n=0,m=0;
		MyGraph G=new MyGraph();
		G.createGraph();
		System.out.println("请输出所构建图的邻接矩阵:");
		for(int i=-1;i<G.getVexNum();i++){
			if(i<0){
				System.out.print("     ");
				for(int j=0;j<G.getVexNum();j++){
					System.out.print(G.getVex(j)+"    ");
				}
				System.out.println();
			}else{
				for(int j=-1;j<G.getVexNum();j++){
					if(j<0){
						System.out.print(G.getVex(i)+"    ");
					}
					else{
                         if(G.arcs[i][j]==Inf){
                        	 System.out.print("INF"+"    ");
						}//  if
                         else{
                        	 System.out.print(G.arcs[i][j]+"    ");	
                         }//else					
					}// else
				}// for
				System.out.println();
			}// else
		}// if
	}
}


package sy3;
/*
 * 建立一个图中的顶点结构
 */
public class Vnode {
	public int vw;//顶点的序数(即表示第几个顶点)
	public String V;//顶点的名称
	public boolean flag;//标识顶点是否访问过
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public String getV() {
		return V;
	}
	public void setV(String v) {
		V = v;
	}
	public int getVw() {
		return vw;
	}
	public void setVw(int vw) {
		this.vw = vw;
	}
	public Arc aw[];//表明从顶点出发的边的权重集
	public Vnode next[];//从顶点出发的边的终点
	public Vnode[] getNext() {
		return next;
	}
	public Arc[] getAw() {
		return aw;
	}
	public void setNext(Vnode next[]) {
		this.next = next;
	}
	public Vnode(String V,int vw,Arc aw[],Vnode next[],boolean flag){
		this.vw=vw;
		this.next=next;
		this.aw=aw;
		this.V=V;
		this.flag=flag;
	}
	//返回顶点位置
	public Vnode(String V,int vw){
		this(V,vw,null,null,false);
	}
}


package sy3;

/*
 * 构造路径长度路径结构
 */
public class lengthOfPath {
	int lamda;//路径长度
	String recent;//本路径中当下顶点的前一个顶点
	String past;
	//初始化路径
	public lengthOfPath(String v){
		this.lamda=0;
		this.recent=v;
		this.past=null;
	}
	public int getLamda() {
		return lamda;
	}
	public void setLamda(int lamda) {
		this.lamda = lamda;
	}
	public String getRecent() {
		return recent;
	}
	public void setRecent(String recent) {
		this.recent = recent;
	}
	public String getPast() {
		return past;
	}
	public void setPast(String past) {
		this.past = past;
	}
}


package sy3;

/*
 * 定义网络流图的边结构
 */
public class FlowArc {
	public Vnode firstNode;//定义边的始点
	public Vnode LastNode;//定义边的中点
	public Arc NextArc;//定义边的下一条边
	public int flow;//定义边的流量值
	public int Capacity;//定义边的容量值
	public int cost;//定义边的费用值
	//构造函数
	public FlowArc(Vnode u,Vnode v,Arc s,int c,int f,int b){
		this.firstNode=u;
		this.LastNode=v;
		this.NextArc=s;
		this.flow=f;
		this.Capacity=c;
		this.cost=b;
	}
	//始点
	public Vnode getFirstNode() {
		return firstNode;
	}
	public void setFirstNode(Vnode firstNode) {
		this.firstNode = firstNode;
	}
	//终点
	public Vnode getLastNode() {
		return LastNode;
	}
	public void setLastNode(Vnode lastNode) {
		LastNode = lastNode;
	}
	//邻接边
	public Arc getNextArc() {
		return NextArc;
	}
	public void setNextArc(Arc nextArc) {
		NextArc = nextArc;
	}
	//流量
	public int getFlow() {
		return flow;
	}
	public void setFlow(int flow) {
		this.flow = flow;
	}
	//容量
	public int getCapacity() {
		return Capacity;
	}
	public void setCapacity(int capacity) {
		Capacity = capacity;
	}
	//费用
	public int getCost() {
		return cost;
	}
	public void setCost(int cost) {
		this.cost = cost;
	}
}


package sy3;

import java.util.Scanner;

/*
 * 利用反圈法实现最短路径算法
 */
public class ShortestPath {
	public final static int Inf=Integer.MAX_VALUE;//表示整型无穷大,用来标识两个顶点是否有边相连
	public int[][] M;//有向图邻接矩阵
	public Vnode[] V;//有向图的顶点集
	public Vnode X[];//反圈中的顶点集
	public Arc F[];//反圈中的边集合
	public String Vs;//路径起始点
	public String Vt;//路径终止点
	public int ArcNum;
	public int num;//反圈中的边数
	lengthOfPath L[];//始点到对应位置顶点的路径长度
	public boolean Flag=true;
	public int SL=0;;
	//构造方法
	public ShortestPath(int M[][],Vnode V[],int ArcNum){
		this.M=M;
		this.V=V;
		this.ArcNum=ArcNum;
		X=new Vnode[V.length];
		F=new Arc[ArcNum];
		for(int i=0;i<ArcNum;i++){
			F[i]=new Arc(null,null,Inf);
		}
		L=new lengthOfPath[X.length];
	}
	/*
	 * 最短路径实现方法
	 */
	public String ShortPath(String Vs,String Vt){
		int num=0;
		int k=0;
		this.Vs=Vs;
		this.Vt=Vt;
		String st="";
		String S[]=new String[X.length];
		//初始化S
		for(int i=0;i<S.length;i++){
			S[i]="";
		}
		for(int i=0;i<ArcNum;i++){
			F[i]=new Arc(Inf);
		}
		S[k]=Vt;
		k++;
		for(int i=0;i<X.length;i++){
			X[i]=new Vnode("",i);
			//System.out.println(V[i].getV()+"空");
			L[i]=new lengthOfPath(X[i].getV());
		}
		for(int i=0;i<V.length;i++){
			V[i].setFlag(false);
			//System.out.println(Vs+V[i].getV());
			if(Vs.equals(V[i].getV())){
				X[i].setV(V[i].getV());  //对反圈的顶点集进行初始化
			//	System.out.println(X[i].getV());
				V[i].setFlag(true);//表示V[i]点已经被访问过
				L[i]=new lengthOfPath(Vs);
				L[i].setPast(null);//初始化Vs的前一个顶点的名称为null
				L[i].setLamda(0);//初始化Vs-->Vs的路径长度为0
			}
			if(Vt.equals(V[i].getV())){
				num=i;
				//S[i]=Vt;
			}
		}
		boolean f1=ZX(Vt);
		if(f1==true){
			Flag=false;
			return "";
		}
		else{
			String temp=L[num].getRecent();//得到终点的名称
			SL=L[num].getLamda();
			while(!temp.equals(Vs) && k<X.length){
				for(int i=0;i<X.length;i++){
					if(temp.equals(X[i].getV())){
						num=i;
					}
				}
				temp=L[num].getPast();
				S[k]=temp;
				k++;
			}
			k=0;
			for(int i=S.length-1;i>0;i--){
				if(!S[i].equals("")){
					st=st+S[i]+"---->";
				}
			}
			st=st+S[0];
			return st;
		}
	}
	/*
	 * 构造正向反圈的方法
	 */
	public boolean ZX(String Vt){
		boolean f=false;//用来判断是否有路
		boolean flag=false;//用来判断反圈中的顶点集是否等于图的顶点集
		boolean flag1=true;//用来判断反圈是否为空
		int min=Inf;//用来求取最小权值弧长
		int index1=0;//用来表示权值最小的非反圈对应点的位置
		int index2=0;//用来表示权值最小的反圈对应点的位置
		//如果反圈中顶点集不等于图的顶点集,就继续反圈运算
		/*
		for(int i=0;i<X.length;i++){
			if(X[i].getV().equals(Vt)){
				flag=false;
				break;
			}
		}*/
		while(flag==false){
			flag1=true;
			min=Inf;
			for(int i=0;i<X.length;i++){
				if(!X[i].getV().equals("")){
					for(int j=0;j<V.length;j++){
						if(V[j].isFlag()==false && M[X[i].getVw()][V[j].getVw()]!=Inf){
							if((L[i].lamda+M[X[i].getVw()][V[j].getVw()])<min){
								min=M[X[i].getVw()][V[j].getVw()]+L[i].lamda;
								index1=j;
								index2=i;
								//System.out.println(index2+" "+index1);
							}// if
						}// if
					}// for
				}// if
			}// for
			if(index1!=-1 && index2!=-1){
				X[index1]=new Vnode(V[index1].getV(),index1);//将该点加入反圈的点集合
				V[index1].setFlag(true);
				L[index1]=new lengthOfPath(X[index1].getV());
				L[index1].setPast(X[index2].getV());//设置当前顶点的前一个顶点
				//System.out.println(X[index1].getV()+" "+L[index1].getPast()+" "+index1);
				L[index1].setLamda(L[index2].getLamda()+M[index2][index1]);//计算路径长度
				F[num]= new Arc(X[index2],V[index1],M[index2][index1]);
			}
			index1=-1;
			index2=-1;
			for(int i=0;i<F.length;i++){
				if(F[i].getWeight()!=Inf){
					flag1=false;//表示反圈中已有元素,即不为空
					//System.out.println(flag1+"  "+Inf+"    "+F[i].getWeight());
					break;
				}
			}
			for(int i=0;i<F.length;i++){
				F[i]= new Arc(null,null,Inf);
				//System.out.println(F[num].getWeight());
			}
			for(int i=0;i<X.length;i++){
				if(X[i].getV().equals(Vt)){
					flag=true;
					break;
				}
			}
			//System.out.println(flag+"  "+flag1);
			if(flag1==true){
				f=true;
				break;
			}
		}// while
		return f;
	}
	public static void main(String args[]) throws Exception{
		//实现最短路径
		String L;
		String vs;
		String vt;
		Scanner s=new Scanner(System.in);
		MyGraph G=new MyGraph();
		G.createGraph();
		System.out.println("请输出所构建图的邻接矩阵:");
		for(int i=-1;i<G.getVexNum();i++){
			if(i<0){
				System.out.print("          ");
				for(int j=0;j<G.getVexNum();j++){
					System.out.print(G.getVex(j)+"         ");
				}
				System.out.println();
			}else{
				for(int j=-1;j<G.getVexNum();j++){
					if(j<0){
						System.out.print(G.getVex(i)+"         ");
					}
					else{
                         if(G.arcs[i][j]==Inf){
                        	 System.out.print("INF"+"         ");
						}
                         else{
                        	 System.out.print(G.arcs[i][j]+"         ");	
                         }					
					}//else
				}// if
				System.out.println();
			}// for
		}//else
		ShortestPath S=new ShortestPath(G.arcs,G.V,G.getArcNum());
		System.out.println("请输入初始点:");
		vs=s.next();
		System.out.println("请输入终点:");
		vt=s.next();
		System.out.println("请输出所求的最短路径:");
		L=S.ShortPath(vs, vt);
		if(!L.equals("")){
			System.out.println(L);
		}
		System.out.println("这条最短路径的长度: "+S.SL);
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值