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

来源:互联网 发布:动易cms模板 编辑:程序博客网 时间:2024/06/16 04:12

       参考:数据结构与算法分析——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=v5int dist;          //从开始顶点到此顶点的最短距离//构造vertexpublic 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:

        运行结果:

**********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的拓扑排序:
该图有圈,没有拓扑排序!