資料結構——圖常用演算法實現(DFS,BFS,最小生成樹,最短路徑,拓撲序列)
阿新 • • 發佈:2019-02-13
最近在複習資料結構的圖的部分,所以就把這一部分的演算法實現了一下,程式碼有註釋,都能看明白,粘在編譯器上就可以跑。如果有寫的不好的地方請在留言區說明。
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());
}
}