图(三)—— 赋权有向图的几个算法

       参考:数据结构与算法分析——Java语言描述  (Mark Allen Weis 

       包括:

        1、Djkstra算法求最短路径

        2、深度优先遍历

        3、广度优先遍历

        4、拓扑排序算法

        一、Djkstra算法

        顶点类有三个属性值得注意:①boolean visited,表示该顶点是否被访问 ②Vertex path,从某一个开始顶点到此顶点的最短路径的前一个顶点,如若此顶点是v7,开始顶点是v1,最短路径是v1-v3-v5-v7 ,则path=v5  ③ int dist从起点到此顶点的最短距离。

         初始状态时,所有顶点的 path=nullvisited=false; 起点dist=0,其他顶点的dist=。每一个阶段,从所有未被访问过的顶点中选择具有最小的dist的那个记为vv.visited=true,遍历它的所有相邻顶点w,如果v.dist+w.dist<w.dist ,那么w.dist=v.dist+w.dist ,同时w.path=v

        二、深度优先遍历

        假定给定图G的初态是所有顶点均未曾访问过,在G中任选一顶点v为出发点,则深度优先搜索可定义如下:从指定的起点v出发(先访问v,并将其标记为已访问过),访问它的任意相邻接的顶点w1,再访问w1的任一个未访问的相邻接顶点w2,如此下去,直到某顶点的所有相邻接的顶点都被访问过的,就回溯到它的前驱。如果这个访问和回溯过程返回到遍历开始的顶点,就结束遍历过程。如果图中仍存在一些未访问过的顶点,就另选一个未访问过的顶点重新开始深度优先搜素。

        三、广度优先遍历

        假定给定图G的初态是所有顶点均未曾访问过,在G中任选一顶点v为出发点,则广度优先搜索可定义如下:从指定的起点v出发,访问与它相邻接的所有顶点w1,w2,……;然后再再依次访问w1,w2,……邻接的尚未被访问的所有顶点,重复这一过程,直到所有的顶点都被访问过为止。如果图中仍存在一些未访问过的顶点,就另选一个未访问过的顶点重新开始广度优先搜素。

        四、拓扑排序算法

        拓扑排序(topological sort)是对有向无圈图的顶点的一种排序,它使得如果存在一条从vivj的路径,那么排序中vj出现在Vi的后面。显然,如果图含有圈,那么拓扑排序是不可能的。

        一个简单的求拓扑排序的算法是:首先计算每个顶点的入度,然后将所有入度为0的顶点放入一个初始为空的队列中。当队列不为空时,删除队首的顶点v,并将v的邻接的所有顶点的入度减1,只要入度降为0,就把该顶点放入队列中。拓扑排序就是顶点出队的顺序。

 

        代码:

        Vertex.java:

/**
 * 顶点类
 */
package com.weightgraph;

import java.util.LinkedList;

public class Vertex {
	
	String name;  //顶点名称
	LinkedList<Node>  adj;   //存放从此顶点出发的边的信息    ,Node中有两个信息,一是边指向的顶点,二是权
	boolean visited;   //该顶点是否被访问
	Vertex path;       //从某一个开始顶点到此顶点的最短路径的前一个顶点
	                   //如若此顶点是v7,开始顶点是v1,最短路径是v1-v3-v5-v7 ,则path=v5
	
	int dist;          //从开始顶点到此顶点的最短距离

	//构造vertex
	public Vertex(String name){
		
		this.name=name;
		adj=new LinkedList<Node>();
		visited=false;
		path=null;
		dist=Graph.INFINITY;
	}
	public void reset(){
		
		visited=false;
		dist=Graph.INFINITY;
		path=null;
	}
	
	class Node {		
		Vertex v;
		int weight;
		
        Node(Vertex v,int weight){
			
			this.v=v;
			this.weight=weight;
			
		}
	}
}

        Graph.java:

package com.weightgraph;

import java.util.HashMap;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Map.Entry;

import com.weightgraph.Vertex.Node;

public class Graph {
	
	public static final int INFINITY=Integer.MAX_VALUE;
	
	//顶点名称的映射	
	HashMap<String, Vertex> vertexMap=new HashMap<String, Vertex>();
	
	/**
	 * 让vertexMap取得对Vertex对象的引用
	 * @param vertexName   顶点名称
	 * @return v           顶点
	 */
	private Vertex getVertex(String vertexName){
		
		Vertex v=vertexMap.get(vertexName);
		if(v==null)
		{
			v=new Vertex(vertexName);
			vertexMap.put(vertexName,v);
		}
		return v;		
	}
	
	/**
	 * 找出未被访问的顶点的dist最小的一个
	 * @return  Vertex vt
	 */
	private Vertex findMin(){
		
		int minDist=Graph.INFINITY;;
		Vertex vt = null;
		for(Iterator<Vertex> itr=vertexMap.values().iterator();
		     itr.hasNext();)
		{
			Vertex v=itr.next();
			if(v.visited==false && v.dist<minDist )
			{
				minDist=v.dist;
				vt=v;
			}
		}
		return vt;
	}
	
	/**
	 * 显示最短路径
	 * @param dest 终点顶点
	 */
	private void printPath(Vertex dest){
				
		if(dest.path!=null)
		{   		
			printPath(dest.path);
			System.out.print("-");			
		}
		System.out.print(dest.name);
	}
	/**
	 * 返回所有顶点中第一个未被访问的顶点,如果全部被访问则返回null
	 * @return Vertex
	 */
	private Vertex firstUnvisited(){
		
		for(Iterator<Vertex> itr=vertexMap.values().iterator();itr.hasNext();)
		{
			Vertex vertex=itr.next();
			if(vertex.visited==false)
				return vertex;
		}
		return null;
	}
	/**
	 * 某一顶点后的所有顶点是否都被访问
	 * @param Vertex v
	 * @return boolean
	 */
	private boolean isAllVisited(Vertex v){
		
		boolean result=true;
		if(v.visited==false)
			return false;
		else{
			for(Iterator<Node> itr=v.adj.iterator();itr.hasNext();)
			{
				Node node=itr.next();
				if(node.v.visited==false)
					result=false;			
			}
		}
		return result;		
	}
	
	/**
	 * 深度优先遍历方法
	 * @param start  遍历的起点
	 */
	private void DFSearch(Vertex start){
		
		  if(start.visited==false)
			{
				System.out.print(start.name+" ");
				start.visited=true;
			}
			for(Iterator<Node> itr=start.adj.iterator();itr.hasNext();)
			{
				Node node=itr.next();
				if(node.v.visited==false)
				{
					DFSearch(node.v);
				}
			}
			if(firstUnvisited()!=null)
			    DFSearch(firstUnvisited());
	 
		
	}
	
	
	
	/**
	 * 广度优先遍历方法
	 * @param start  遍历的起点
	 */
	private void BFSearch(Vertex start){
				  
		  if(start.visited==false)
			{
				System.out.print(start.name+" ");
				start.visited=true;
			}
			for(Iterator<Node> itr=start.adj.iterator();itr.hasNext();)
			{
				Node node=itr.next();
				if(node.v.visited==false)
				{
					System.out.print(node.v.name+" ");
					node.v.visited=true;
					
				}
			}
			for(Iterator<Node> itr=start.adj.iterator();itr.hasNext();)
			{
				Node node=itr.next();
				if(!isAllVisited(node.v))
				  BFSearch(node.v);
			}
            if(firstUnvisited()!=null)
            	BFSearch(firstUnvisited());
	}
	
	
	/**
	 * 初始化 
	 */
	public void clearAll(){
		
		for(Iterator<Vertex> itr=vertexMap.values().iterator();itr.hasNext();)
		{
			itr.next().reset();
		}
	}
		
	/**
	 * 增加一条边
	 * @param sourceName 起点
	 * @param destName   终点
	 * @param weight     权
	 */
	public void addEdge(String sourceName,String destName,int weight){
		
		Vertex v=getVertex(sourceName); //起点
		Vertex w=getVertex(destName);   //终点
		Node node=v.new Node(w,weight);     
		v.adj.add(node);		
	}
	
    /**
     * Dijkstra(迪卡斯特拉)算法计算最短路径
     * @param startVerName  起点
     */
	public void dijkstra(String startVerName){
		
		clearAll();
		System.out.println("以"+startVerName+"为起点:");
		Vertex start=getVertex(startVerName);
		start.dist=0;
		while(true){
			
			Vertex ver=findMin();
			if (ver==null)
				break;
			ver.visited=true;
			
			for(Iterator<Node> itr=ver.adj.iterator();itr.hasNext();)
			{
				Node node=itr.next();
				if(node.v.visited==false && ver.dist+node.weight<node.v.dist)
				{
					node.v.dist=ver.dist+node.weight;
					node.v.path=ver;
				}
			}
		}
	}
	/**
	 * 打印最短路径
	 * @param destName  终点
	 */
	public void printShortestPath(String destName){
		
		System.out.print("到 "+destName+" 的最短路径:");
		Vertex dest=getVertex(destName);
		if(dest==null){
			System.out.println("不存在该终点顶点!");
		}else{			
			printPath(dest);
		}
		System.out.print("\t 路径长:"+dest.dist);
		System.out.println();
	}
	
	/**
	 * 深度优先遍历
	 * @param startName  起点
	 */
	public void DFS(String startName){
		
		clearAll();
		System.out.println("以"+startName+"为起点深度优先遍历图:");
		Vertex start=getVertex(startName);
		DFSearch(start);
		System.out.println();
	}
		
	/**
	 * 广度优先遍历
	 * @param startName   起点
	 */
	public void BFS(String startName){
		
		clearAll();
		System.out.println("以"+startName+"为起点广度优先遍历图:");
		Vertex start=getVertex(startName);
		BFSearch(start);
		System.out.println();
	}
	
	/**
	 * 拓扑排序算法
	 */
	public void topSort(){
		
		clearAll();
		//放拓扑排序的各个顶点名
		Queue<String> topsort=new PriorityQueue<String>();
	   //首先计算各顶点的入度
		HashMap<String,Integer> inDegree=new  HashMap<String, Integer>();
		for(Iterator<Vertex> itr=vertexMap.values().iterator();itr.hasNext();)
		{
			Vertex vertex=itr.next();
			if(inDegree.get(vertex.name)==null)
				inDegree.put(vertex.name, 0);
			
			
			for(Iterator<Node> inode=vertex.adj.iterator();inode.hasNext();)
			{
				
				Node temp=inode.next();
				int i;
				if(inDegree.get(temp.v.name)==null)
					i=0;
				else {
					i=inDegree.get(temp.v.name);
				}	
				inDegree.put(temp.v.name, ++i);
			}
			
		}
		
		//队列,用来放入度为0的顶点名
		Queue<String> q=new PriorityQueue<String>();
		for(Iterator<Entry<String, Integer>> itr=inDegree.entrySet().iterator();itr.hasNext();)
		{
			Entry<String, Integer> entry=itr.next();
			int degree=entry.getValue();
			if(degree==0){	
				q.offer(entry.getKey());
			}
				
		}
		if(q.size()==0)
			System.out.println("该图有圈,没有拓扑排序!");
		while(!q.isEmpty())
		{
			String deQueueVerName=q.poll();
			//打印出队
			//System.out.print(deQueueVerName+" ");
			topsort.offer(deQueueVerName);
			getVertex(deQueueVerName).visited=true;
			for(Iterator<Node> itr=getVertex(deQueueVerName).adj.iterator();itr.hasNext();)
			{
				Node node=itr.next();
				int degree=inDegree.get(node.v.name);
				inDegree.put(node.v.name, --degree);
				if(degree==0)
					q.offer(node.v.name);
			}
			if(q.size()==0 && firstUnvisited()!=null)
				System.out.println("该图有圈,没有拓扑排序!");
		}
		//打印出拓扑排序  topsort中的顶点数等于图中的顶点数才说明图中没有圈
		if(topsort.size()==vertexMap.size()){
			for(Iterator<String> itr=topsort.iterator();itr.hasNext();)
			{
				System.out.print(itr.next()+" ");
			}
		}		
	}
}

         Main.java:

package com.weightgraph;

public class Main {

	public static void main(String[] args) {

		  //构建图
		  Graph graph=new Graph();
		  graph.addEdge("v1", "v2",2);
          graph.addEdge("v1", "v4",1);
          graph.addEdge("v2", "v4",3);
          graph.addEdge("v2", "v5",10);
          graph.addEdge("v3", "v1",4);
          graph.addEdge("v3", "v6",5);
          graph.addEdge("v4", "v3",2);
          graph.addEdge("v4", "v5",2);
          graph.addEdge("v4", "v6",8);
          graph.addEdge("v4", "v7",4);
          graph.addEdge("v5", "v7",6);
          graph.addEdge("v7", "v6",1);

          
          System.out.println("**********graph***************");
          // dijkstra算法计算最短路径 ,起点 v1
          graph.dijkstra("v1");
          
          //打印从v1到个顶点的最短路径 
          for(int i=1;i<=7;i++){
        	  graph.printShortestPath("v"+i);
          }   
          
          //以v3为起点广度优先遍历图
          graph.BFS("v3");
          //以v3为起点深度优先遍历图
          graph.DFS("v3");
          System.out.println();
          //graph的拓扑排序
          graph.topSort();
          
          //构建另一个无圈的图graph2,可以打印出拓扑排序
		  Graph graph2=new Graph();
		  graph2.addEdge("v1", "v2",2);
          graph2.addEdge("v1", "v4",1);
          graph2.addEdge("v1", "v3",3);
          graph2.addEdge("v2", "v4",10);
          graph2.addEdge("v2", "v5",4);
          graph2.addEdge("v3", "v6",5);
          graph2.addEdge("v4", "v3",2);
          graph2.addEdge("v4", "v6",8);
          graph2.addEdge("v4", "v7",4);
          graph2.addEdge("v5", "v4",2);
          graph2.addEdge("v5", "v7",6);
          graph2.addEdge("v7", "v6",1);
          
          System.out.println("**********graph2***************");
          System.out.println("graph2的拓扑排序:");
          graph2.topSort(); 
          
          
          Graph graph3=new Graph();
	 graph3.addEdge("v1", "v2",2);
          graph3.addEdge("v2", "v4",10);
          graph3.addEdge("v4", "v3",2);
          graph3.addEdge("v3", "v2",3);
          System.out.println();
          System.out.println("**********graph3***************");
          System.out.println("graph3的拓扑排序:");
          graph3.topSort(); 
	}	
}


 

         graph:  

        graph2:

        graph3:

图(三)—— 赋权有向图的几个算法_第1张图片

        运行结果:

**********graph***************
以v1为起点:
到 v1 的最短路径:v1  路径长:0
到 v2 的最短路径:v1-v2  路径长:2
到 v3 的最短路径:v1-v4-v3  路径长:3
到 v4 的最短路径:v1-v4  路径长:1
到 v5 的最短路径:v1-v4-v5  路径长:3
到 v6 的最短路径:v1-v4-v7-v6  路径长:6
到 v7 的最短路径:v1-v4-v7  路径长:5
以v3为起点广度优先遍历图:
v3 v1 v6 v2 v4 v5 v7
以v3为起点深度优先遍历图:
v3 v1 v2 v4 v5 v7 v6

该图有圈,没有拓扑排序!
**********graph2***************
graph2的拓扑排序:
v1 v2 v5 v4 v3 v7 v6
**********graph3***************
graph3的拓扑排序:
该图有圈,没有拓扑排序!

你可能感兴趣的:(图(三)—— 赋权有向图的几个算法)