参考:数据结构与算法分析——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=null,visited=false; 起点s 的dist=0,其他顶点的dist=∞。每一个阶段,从所有未被访问过的顶点中选择具有最小的dist的那个记为v,v.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)是对有向无圈图的顶点的一种排序,它使得如果存在一条从vi 到vj的路径,那么排序中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:
运行结果:
**********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的拓扑排序:
该图有圈,没有拓扑排序!