1. 程式人生 > >資料結構——圖常用演算法實現(DFS,BFS,最小生成樹,最短路徑,拓撲序列)

資料結構——圖常用演算法實現(DFS,BFS,最小生成樹,最短路徑,拓撲序列)

最近在複習資料結構的圖的部分,所以就把這一部分的演算法實現了一下,程式碼有註釋,都能看明白,粘在編譯器上就可以跑。如果有寫的不好的地方請在留言區說明。

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
/**
 * 圖的常用演算法實現(鄰接矩陣表示)
 * @author XiaoYe
 *
 */
public class GRAPGAPI  {
        public class Vertex implements Comparable
<Vertex>{
/** * 節點名稱(A,B,C,D) */ private String name; /** * 最短路徑長度 */ private int minPath; /** * 最小生成樹中節點連結的另一個節點的ID,兩個節點之間儲存著最小生成樹中的邊 */ private int anotherIDinminEdge; /** * 節點是否已經出列(是否已經處理完畢)最短路徑用到 */
private boolean isMarked; public Vertex(String name){ this.name = name; this.minPath = Integer.MAX_VALUE; //初始設定最短路徑長度為無窮大 this.anotherIDinminEdge=-1; this.setMarked(false); } public Vertex(String name, int path){ this
.name = name; this.minPath = path; this.setMarked(false); } public int getAnotherIDinminEdge() { return anotherIDinminEdge; } public void setAnotherIDinminEdge(int anotherIDinminEdge) { this.anotherIDinminEdge = anotherIDinminEdge; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPath() { return minPath; } public void setPath(int path) { this.minPath = path; } boolean isMarked(){ return this.isMarked; } void setMarked(boolean flag){ this.isMarked=flag; } @Override public int compareTo(Vertex o) { return o.minPath > minPath?-1:1; } } public class Graph { /** * 頂點 */ private List<Vertex> vertexs; /** * 邊 */ private int[][] edges; /** * 拓撲序列 */ private List<Vertex> topVertexs; /** * 沒有訪問的頂點,用於求最短路徑 */ private Queue<Vertex> unVisited; /** * 最小生成樹儲存在二維陣列中 */ private int[][] minTree; /** * 初始化 * @param vertexs * @param edges */ public Graph(List<Vertex> vertexs, int[][] edges) { this.vertexs = vertexs; this.topVertexs=new ArrayList<GRAPGAPI.Vertex>(); this.edges = edges; this.minTree=new int[this.vertexs.size()][this.vertexs.size()]; initUnVisited(); } /** * 深度優先搜尋 * @param id */ public void DFS(String vertexName){ int id=getIdOfVertexName(vertexName); if(id==-1)return; vertexs.get(id).setMarked(true); System.out.println("遍歷到"+vertexs.get(id).getName()); List<Vertex> neighbors = getNeighbors(vertexs.get(id)); for(int i=0;i<neighbors.size();i++){ if(!neighbors.get(i).isMarked()){ DFS(neighbors.get(i).getName()); } } } /** * 廣度優先搜尋 * @param startID */ public void BFS(String vertexName){ int startID=getIdOfVertexName(vertexName); if(startID==-1) return; List<Vertex> q=new ArrayList<Vertex>(); q.add(vertexs.get(startID)); vertexs.get(startID).setMarked(true); while(!q.isEmpty()){ Vertex curVertex=q.get(0); q.remove(0); System.out.println("遍歷到"+curVertex.getName()); List<Vertex> neighbors = getNeighbors(curVertex); for(int i=0;i<neighbors.size();i++){ if(!neighbors.get(i).isMarked()){ neighbors.get(i).setMarked(true); q.add(neighbors.get(i)); } } } } /** * 獲取最小生成樹 * @return */ public int[][] getMinTree(){ initMinTree();//初始化最小生成樹 while(!allVisited()){ Vertex vertex = vertexs.get(getNotMarkedMinVertex());//設定處理節點 System.out.println("處理:節點"+vertex.getName()); //頂點已經計算出最短路徑,設定為"已訪問" vertex.setMarked(true); //獲取所有"未訪問"的鄰居 List<Vertex> neighbors = getNeighbors(vertex); System.out.println("鄰居個數為:"+neighbors.size()); //更新最小生成樹 updateMinEdge(vertex, neighbors); } System.out.println("search over"); setMinTree(); return minTree; } /** * 設定最小生成樹矩陣對稱 */ public void setMinTree(){ for(int i=0;i<vertexs.size();i++){ if(vertexs.get(i).getAnotherIDinminEdge()!=-1){ minTree[getIdOfVertexName(vertexs.get(i).getName())][vertexs.get(i).getAnotherIDinminEdge()]= edges[getIdOfVertexName(vertexs.get(i).getName())][vertexs.get(i).getAnotherIDinminEdge()]; minTree[vertexs.get(i).getAnotherIDinminEdge()][getIdOfVertexName(vertexs.get(i).getName())]= edges[vertexs.get(i).getAnotherIDinminEdge()][getIdOfVertexName(vertexs.get(i).getName())]; } } } /** * 初始化最小生成樹,將所有節點設定值最大 */ public void initMinTree(){ for(int i=0;i<this.vertexs.size();i++){ for(int j=0;j<this.vertexs.size();j++){ this.minTree[i][j]=Integer.MAX_VALUE; } } } /** * 更新最小生成樹 * @param vertex * @param neighbors */ public void updateMinEdge(Vertex vertex, List<Vertex> neighbors){ //引數檢測 if(!isInGraph(vertex)){ System.out.println("當前節點不在圖中"); return ; } for(Vertex neighbor: neighbors){ int distance = edges[getIdOfVertexName(neighbor.getName())][getIdOfVertexName(vertex.getName())]; if(neighbor.getAnotherIDinminEdge()==-1){ neighbor.setAnotherIDinminEdge(getIdOfVertexName(vertex.getName())); System.out.println(neighbor.getName()+"setEdge To"+vertex.getName()+edges[neighbor.getAnotherIDinminEdge()][getIdOfVertexName(neighbor.getName())]); } else if(distance < edges[getIdOfVertexName(neighbor.getName())][neighbor.getAnotherIDinminEdge()]){ neighbor.setAnotherIDinminEdge(getIdOfVertexName(vertex.getName())); System.out.println(neighbor.getName()+"setEdge To"+vertex.getName()+edges[neighbor.getAnotherIDinminEdge()][getIdOfVertexName(neighbor.getName())]); } } } /** * 拓撲排序 */ public void topSort(){ int[][] tmpEdges=edges; int IDofNullPreVertex=getNullPreVertexID(tmpEdges);//獲得當前圖中無前驅的節點 while(IDofNullPreVertex!=-1){ vertexs.get(IDofNullPreVertex).setMarked(true); topVertexs.add(vertexs.get(IDofNullPreVertex));//拓撲序列增加 //邊銷燬 for(int j=0;j<this.vertexs.size();j++){ if(tmpEdges[IDofNullPreVertex][j]!=Integer.MAX_VALUE){ tmpEdges[IDofNullPreVertex][j]=Integer.MAX_VALUE; } } IDofNullPreVertex=getNullPreVertexID(tmpEdges); } } /** * 獲取當前圖中無前驅的節點 * @param tmpEdges * @return */ public int getNullPreVertexID(int[][] tmpEdges){ int resultId=-1; for(int j=0;j< this.vertexs.size();j++){ boolean flag=false; for(int i=0;i< this.vertexs.size();i++){ if(tmpEdges[i][j]!=Integer.MAX_VALUE){ flag=true; } } if(!flag&&!vertexs.get(j).isMarked()){ resultId=j; break; } } return resultId; } /** * 搜尋各頂點最短路徑 */ public void search(){ while(!unVisited.isEmpty()){ Vertex vertex = unVisited.element(); //頂點已經計算出最短路徑,設定為"已訪問" vertex.setMarked(true); //獲取所有"未訪問"的鄰居 List<Vertex> neighbors = getNeighbors(vertex); //更新鄰居的最短路徑 updatesDistance(vertex, neighbors); pop(); } System.out.println("search over"); } /** * 更新所有鄰居的最短路徑 */ private void updatesDistance(Vertex vertex, List<Vertex> neighbors){ //引數檢測 if(!isInGraph(vertex)){ System.out.println("當前節點不在圖中"); return ; } for(Vertex neighbor: neighbors){ updateDistance(vertex, neighbor); } } /** * 更新鄰居的最短路徑 */ private void updateDistance(Vertex vertex, Vertex neighbor){ //引數檢測 if(!isInGraph(vertex)||!isInGraph(neighbor)){ System.out.println("當前節點不在圖中"); return ; } int distance = getDistance(vertex, neighbor) + vertex.getPath(); if(distance < neighbor.getPath()){ neighbor.setPath(distance); } } /** * 初始化未訪問頂點集合 */ private void initUnVisited() { unVisited = new PriorityQueue<Vertex>(); for (Vertex v : vertexs) { unVisited.add(v); } } /** * 從未訪問頂點集合中刪除已找到最短路徑的節點 */ private void pop() { unVisited.poll(); } /** * 獲取頂點到目標頂點的距離 */ private int getDistance(Vertex source, Vertex destination) { //引數檢測 if(!isInGraph(source)||!isInGraph(destination)){ System.out.println("當前節點不在圖中"); return -1; } int sourceIndex = vertexs.indexOf(source); int destIndex = vertexs.indexOf(destination); return edges[sourceIndex][destIndex]; } /** * 獲取頂點所有(未訪問的)鄰居 */ public List<Vertex> getNeighbors(Vertex v) { //引數檢測 if(!isInGraph(v)){ System.out.println("當前節點不在圖中"); return null; } List<Vertex> neighbors = new ArrayList<Vertex>(); int position = vertexs.indexOf(v); Vertex neighbor = null; int distance; for (int i = 0; i < vertexs.size(); i++) { if (i == position) { //頂點本身,跳過 continue; } distance = edges[position][i]; //到所有頂點的距離 if (distance < Integer.MAX_VALUE) { //是鄰居(有路徑可達) neighbor = getVertex(i); if (!neighbor.isMarked()) { //如果鄰居沒有訪問過,則加入list; neighbors.add(neighbor); } } } return neighbors; } /** * 根據頂點位置獲取頂點 */ private Vertex getVertex(int index) { if(index<0||index>vertexs.size()+1){ System.out.println("獲取ID為"+index+"的頂點失敗"); return null; } return vertexs.get(index); } /** * 列印圖 */ public void printGraph() { int verNums = vertexs.size(); for (int row = 0; row < verNums; row++) { for (int col = 0; col < verNums; col++) { if(Integer.MAX_VALUE == edges[row][col]){ System.out.print("X"); System.out.print(" "); continue; } System.out.print(edges[row][col]); System.out.print(" "); } System.out.println(); } } /** * 判斷是否全部訪問到 * @return */ public boolean allVisited(){ boolean flag=true; for(int i=0;i<vertexs.size();i++){ if(!vertexs.get(i).isMarked()) flag=false; } return flag; } /** * 列印拓撲序列 */ public void printTopSort(){ for(int i=0;i<this.topVertexs.size();i++){ System.out.print(this.topVertexs.get(i).getName()+" "); } System.out.println(); } public int getIdOfVertexName(String name){ int id=-1; for(int i=0;i<vertexs.size();i++){ if(vertexs.get(i).getName().equals(name)) id=i; } return id; } /** * 獲取到連線著未訪問過的節點的最小邊,返回連結節點的ID * @return */ public int getNotMarkedMinVertex(){ int min=10000; int id=0; for(int i=0;i<vertexs.size();i++){ if(!vertexs.get(i).isMarked()&&vertexs.get(i).getAnotherIDinminEdge()!=-1){ if(min>edges[getIdOfVertexName(vertexs.get(i).getName())][vertexs.get(i).getAnotherIDinminEdge()]){ min=edges[getIdOfVertexName(vertexs.get(i).getName())][vertexs.get(i).getAnotherIDinminEdge()]; id=i; } } } return id; } /** * 清除圖中的標記資訊 */ public void clearGraph(){ for(Vertex vertex:vertexs){ vertex.setMarked(false); vertex.setAnotherIDinminEdge(-1); } } private boolean isInGraph(Vertex v){ for(Vertex vertex:vertexs){ if(vertex.getName().equals(v.getName())) return true; } return false; } } public static void main(String[] args) { // TODO Auto-generated method stub GRAPGAPI main=new GRAPGAPI(); //初始化圖 List<Vertex> vertexs = new ArrayList<Vertex>(); Vertex a = main.new Vertex("A",0);//0到第一個節點的最短路徑設定為0 Vertex b = main.new Vertex("B"); Vertex c = main.new Vertex("C"); Vertex d = main.new Vertex("D"); Vertex e = main.new Vertex("E"); Vertex f = main.new Vertex("F"); vertexs.add(a); vertexs.add(b); vertexs.add(c); vertexs.add(d); vertexs.add(e); vertexs.add(f); int[][] edges = { {Integer.MAX_VALUE,6,3,Integer.MAX_VALUE,Integer.MAX_VALUE,Integer.MAX_VALUE}, {6,Integer.MAX_VALUE,2,5,Integer.MAX_VALUE,Integer.MAX_VALUE}, {3,2,Integer.MAX_VALUE,3,4,Integer.MAX_VALUE}, {Integer.MAX_VALUE,5,3,Integer.MAX_VALUE,5,3}, {Integer.MAX_VALUE,Integer.MAX_VALUE,4,5,Integer.MAX_VALUE,5}, {Integer.MAX_VALUE,Integer.MAX_VALUE,Integer.MAX_VALUE,3,5,Integer.MAX_VALUE} }; Graph graph = main.new Graph(vertexs, edges); //遍歷 System.out.println("------------------------------------"); System.out.println("當前圖狀態:"); graph.printGraph(); System.out.println("------------------------------------"); System.out.println("深度優先遍歷:"); System.out.println("開始節點:E"); graph.DFS("E"); graph.clearGraph(); System.out.println("------------------------------------"); System.out.println("廣度優先遍歷:"); System.out.println("開始節點:E"); graph.BFS("E"); graph.clearGraph(); //最小生成樹 System.out.println("------------------------------------"); System.out.println("最小生成樹:"); int[][] minTree=graph.getMinTree(); for(int i=0;i<vertexs.size();i++){ for(int j=0;j<vertexs.size();j++){ if(minTree[i][j]==Integer.MAX_VALUE) minTree[i][j]=0; System.out.print(minTree[i][j]+" "); } System.out.println(); } graph.clearGraph(); //拓撲序列 // System.out.println("------------------------------------"); // System.out.println("拓撲排序(Ps:針對於有向圖,需要修改圖的連結結構!!!)"); // System.out.println(graph.getNullPreVertexID()+"###"); // graph.topSort(); // graph.printTopSort(); // graph.clearGraph(); //最短路徑 System.out.println("------------------------------------"); System.out.println("最短路徑"); graph.search(); System.out.println("節點"+f.getName()+"距離初始節點的最小距離為:"+f.getPath()); } }