参考:数据结构与算法分析——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的拓扑排序:
该图有圈,没有拓扑排序!