這是我們學校做的數據結構課設,要求分別輸出關鍵路徑,我查遍資料java版的只能找到關鍵路徑,但是無法分別輸出關鍵路徑
c++有可以分別輸出的,所以在明白思想后自己寫了一個java版的
函數帶有輸入函數也有已經存進去的圖
如上圖關鍵路徑被分別輸出(采用了DFS算法):
例:AOE 圖如下:
算法設計如下:
1. 首先,要求關鍵路徑,要先要先寫拓撲排序,如果圖中有環,就無法進行關鍵路徑的求解,直接跳出。
拓撲排序:利用棧stack,先將入度為0事件節點的加入棧中,然后編歷后面的活動節點,每次給活動節點的入度減一,然后將入度為0的加入棧 stack中,每次出棧的加入棧stack中,stack1中的元素拓撲排序的逆序,然后根據核心算法:
if(etv[p.getData()]+p1.getInfo()>etv[p1.getAdjvex()]){
etv[p1.getAdjvex()]=etv[p.getData()]+p1.getInfo();
}
計算出事件的最早發生時間的事件存入etv
然后根據count來判斷圖中有沒有回路。
2. 然后對拓撲排序的逆序求事件的最晚發生時間,根據核心法:
if (ltv[p1.getAdjvex()]-p1.getInfo()<ltv[p.getData()]){
ltv[p.getData()]=Math.abs(ltv[p1.getAdjvex()]-p1.getInfo());
}
算出事件的最晚發生時間存入數組ltv中。
3. 接着求活動的最晚發生時間el和活動的最早發生時間ee
ee=etv[i];
el=ltv[pn.getAdjvex()]-pn.getInfo();
當ee和el相等的時候,本活動就為關鍵活動,關鍵路徑算出來了。
解決輸出每條關鍵路徑:
4. 輸出每一條關鍵路徑:利用DFS算法(回溯算法),
圖在遍歷以前已經將關鍵路徑用visited數組標記了,
故遍歷的時候只遍歷關鍵路徑,
原理如圖:
當visited為0的時候為關鍵節點,然后遍歷此節點進入遞歸:
while (p!=null){
if(visited[p.getAdjvex()]!=1){
/**
* 是關鍵路徑就入棧
*/
stack2.push(p);
DFS(p.getAdjvex());
/**
* 遇見死路回退的時候出棧
*/
stack2.pop();
}
本算法類似走迷宮,並且每走一個點,就把這個點入棧,並且將visited數組中的本節點的值存為1,代表遍歷過了:
visited[k]=1;
然后遞歸進入下一個節點,遇見終點就先遍歷並輸出棧中的元素:
if(k==finall) {
for (int i=0;i<stack2.size;i++) {
System.out.print(stack2.peekTravel(i).getName() + "->");
}
}
一邊退回一邊出棧:
DFS(p.getAdjvex());
/**
* 遇見死路回退的時候出棧
*/
stack2.pop();
即棧中元素只到分岔點,退回上一次的分岔點,然后遞歸進入下一條路,直到遍歷結束,然后輸出了全部的關鍵路徑。
DFS算法全部代碼:
/**
* 為了輸出每條關鍵路徑用到DFS算法
* @param k 起點再數組中的下標
*/
public void DFS(int k){
visited[k]=1;
lineNode p=new lineNode();
p=vexList[k].getNext();
/**
* 終點永遠是關鍵節點
*/
visited[finall]=0;
/**
* 如果走到終點遍歷棧的元素
*/
if(k==finall) {
for (int i=0;i<stack2.size;i++) {
System.out.print(stack2.peekTravel(i).getName() + "->");
}
}
/**
* 遍歷節點后面的鏈表
*/
while (p!=null){
if(visited[p.getAdjvex()]!=1){
/**
* 是關鍵路徑就入棧
*/
stack2.push(p);
DFS(p.getAdjvex());
/**
* 遇見死路回退的時候出棧
*/
stack2.pop();
}
System.out.println();
p=p.getNext();
}
}
DFS算法流程圖:
(流程圖有問題,左端回退出棧少了循環)
源代碼如下:
package dataproject.importanRoad; import dataproject.stack.myArrayStack; import java.util.Scanner; /** * @author 李正陽 17060208112 */ public class lessonWord { /** * 頂點數組 */ private vertexNode[] vexList; /** * etv 事件最早發生時間(即頂點) * ltv 事件最晚發生時間 */ private int etv[], ltv[]; /** * 拓撲排序所用到的棧 */ private myArrayStack<vertexNode> stack=new myArrayStack<>(); private myArrayStack<vertexNode> stack1=new myArrayStack<>(); /** * 為了輸出關鍵路徑,標明哪些是關鍵節點 */ static int MAX_VERTEXNUM = 100; static int [] visited = new int[MAX_VERTEXNUM]; /** *關鍵路徑用到的棧隊,數組 */ private myArrayStack<lineNode> stack2=new myArrayStack<>(); private static int finall; /** * 使用鍵盤輸入來構建圖構建圖 */ public void createGraph(){ vertexNode pl1=new vertexNode(); lineNode pl=new lineNode(); int n; Scanner in=new Scanner(System.in); System.out.println("請輸入共有多少個節點:"); n=in.nextInt(); vexList = new vertexNode[n]; for (int i=0;i<n;i++){ int p; /** * 構建節點 */ System.out.println("節點"+(i+1)+"節點存在數組中的位置 "+"節點名字"); vertexNode v= new vertexNode( in.nextInt(), null, in.nextLine()); pl1=v; pl=v.getNext(); System.out.println("請輸入此節點后有多少個邊:"); p=in.nextInt(); for (int j=0;j<p;j++){ /** * 構建活動即事件 */ System.out.println("邊"+(j+1)+"后續節點在數組中的位置 "+"權值 "+"邊的名字"); lineNode a= new lineNode(in.nextInt(),in.nextInt(), in.nextInt(), null,in.next(),false); if(j==0){ pl1.setNext(a); pl=pl1.getNext(); }else { pl.setNext(a); pl=pl.getNext(); } } vexList[i]=v; } } /** * 存儲下的圖,和我畫出的圖一樣,方便測試 */ public void craeteGraph() { /** * 所有節點即事件 */ vertexNode v1 = new vertexNode( 0, null, "v1"); vertexNode v2 = new vertexNode( 1, null, "v2"); vertexNode v3 = new vertexNode(2, null, "v3"); vertexNode v4 = new vertexNode( 3, null, "v4"); vertexNode v5 = new vertexNode(4, null, "v5"); vertexNode v6 = new vertexNode(5, null, "v6"); vertexNode v7 = new vertexNode(6, null, "v7"); vertexNode v8 = new vertexNode(7, null, "v8"); vertexNode v9 = new vertexNode(8, null, "v9"); /** * v1節點 * a1活動,a2活動,a3活動 */ lineNode v12 = new lineNode(0,1, 6, null,"a1",false); lineNode v13 = new lineNode(0,2, 4, null,"a2",false); lineNode v14 = new lineNode(0,3, 5, null,"a3",false); lineNode a12=new lineNode(0,9,5,null,"a12",false); v1.setNext(v12); v12.setNext(v13); v13.setNext(v14); v14.setNext(a12); /** * v2節點 * a4活動 */ lineNode v25 = new lineNode(1, 4, 1, null,"a4",false); v2.setNext(v25); /** * v3節點 * a5活動 */ lineNode v35 = new lineNode(2,4, 1, null,"a5",false); v3.setNext(v35); /** * v4節點 * a6活動 */ lineNode v46 = new lineNode(3,5, 2, null,"a6",false); v4.setNext(v46); /** * v5節點 * a7活動 a8活動 */ lineNode v57 = new lineNode(4,6, 9, null,"a7",false); lineNode v58 = new lineNode(4,7, 7, null,"a8",false); v5.setNext(v57); v57.setNext(v58); /** * v6節點 * a9活動 */ lineNode v68 = new lineNode(5,7, 4, null,"a9",false); v6.setNext(v68); /** * v7節點 * a10活動 */ lineNode v79 = new lineNode(6,8, 2, null,"a10",false); v7.setNext(v79); /** * v8節點 * a11活動 */ lineNode v89 = new lineNode(7,8, 4, null,"a11",false); v8.setNext(v89); /** * v10節點 * */ vertexNode v10=new vertexNode(9,null,"西安"); /** * v11節點 */ vertexNode v11=new vertexNode(10,null,"北京"); lineNode a13=new lineNode(8,10,6,null,"a13",false); lineNode a14=new lineNode(9,8,7,null,"a14",false); v11.setNext(a14); v10.setNext(a13); /** * 對象數組:vexList,保存節點構建了圖 */ vexList = new vertexNode[11]; vexList[0] = v1; vexList[1] = v2; vexList[2] = v3; vexList[3] = v4; vexList[4] = v5; vexList[5] = v6; vexList[6] = v7; vexList[7] = v8; vexList[8] = v9; vexList[9]=v10; vexList[10]=v11; } /** * 拓撲排序 * @return true 排序成功 false 失敗 */ public boolean topologicalSort() { /** * 計算入度:初始化所有節點的入度 */ for (int i=0;i<vexList.length;i++){ vexList[i].setIn(0); } /** * 遍歷每個節點后面的鏈表,然后就給弧尾頂點加一 */ for (int i=0;i<vexList.length;i++){ lineNode p=new lineNode(); p=vexList[i].getNext(); while (p!=null){ vertexNode vertexNode=new vertexNode(); vertexNode=vexList[p.getAdjvex()]; vertexNode.setIn(vertexNode.getIn()+1); p=p.getNext(); } } /** * 計數:用來判斷是否無環 */ int count=0; vertexNode p = new vertexNode(); lineNode p1 = new lineNode(); System.out.println("拓撲排序:"); /** * 對事件最早發生時間數組初始化 */ etv=new int[vexList.length]; for (int i=0;i<etv.length;i++){ etv[i]=0; } /** * 將度為0的入棧 */ for (int i = 0; i < vexList.length; i++) { if (vexList[i].in == 0) { stack.push(vexList[i]); } } /** * 遍歷領接表里面邊結點,遍歷到入度就減一 */ while (!stack .empty()) { p=stack.pop(); count++; /** * 拓撲排序的逆序加入棧2中 */ stack1.push(p); System.out.print(p.getName()+" "); if(p.getNext()!=null){ p1=p.getNext(); } /** * 核心算法計算事件最早發生時間etv */ while (p1!=null){ vexList[p1.getAdjvex()].setIn(vexList[p1.getAdjvex()].getIn()-1); if(vexList[p1.getAdjvex()].getIn()==0){ stack.push(vexList[p1.getAdjvex()]); } if(etv[p.getData()]+p1.getInfo()>etv[p1.getAdjvex()]){ etv[p1.getAdjvex()]=etv[p.getData()]+p1.getInfo(); } p1=p1.getNext(); } } /** * 計數小於節點數就有回路 * 等於就無回路 */ if(count!=vexList.length){ System.out.println(); System.out.println("圖有回路!!"); return true; }else { System.out.println(); System.out.println("圖無回路!!"); } return false; } /** * 關鍵路徑的方法 */ public void criticalPath() { /** * 活動的最早發生時間 ee * 活動發生的最晚時間 el * p 指針掃描事件節點 * p1 掃描活動節點 */ int ee,el; vertexNode p = new vertexNode(); lineNode p1 = new lineNode(); /** * 先進行拓撲排序判斷圖有沒有環 */ if (topologicalSort()){ return; } /** * 初始化ltv數組 */ finall=stack1.peek().getData(); ltv=new int[vexList.length]; for (int i=0;i<vexList.length;i++){ ltv[i]=etv[finall]; } /** * 已經獲得了拓撲排序的逆序stack2,所以對逆序求最晚發生時間 */ while (!stack1.empty()){ p=stack1.pop(); if(p.getNext()!=null){ p1=p.getNext(); } while (p1!=null){ if (ltv[p1.getAdjvex()]-p1.getInfo()<ltv[p.getData()]){ ltv[p.getData()]=ltv[p1.getAdjvex()]-p1.getInfo(); } p1=p1.getNext(); } } for (int i=0;i<visited.length;i++){ visited[i]=1; } System.out.print("關鍵活動為:"); /** * 求ee,el和關鍵路徑 count1表示關鍵活動的數量 */ int count1=0; for (int i=0;i<vexList.length;i++){ lineNode pn=new lineNode(); pn=vexList[i].getNext(); while (pn!=null){ ee=etv[i]; el=ltv[pn.getAdjvex()]-pn.getInfo(); if(ee==el){ count1++; visited[vexList[i].getData()]= 0; System.out.print(pn.getName()+","); } pn=pn.getNext(); } } System.out.println(); System.out.println("最大時間:"+ltv[finall]); System.out.println(); } /** * 為了輸出每條關鍵路徑用到DFS算法 * @param k 起點再數組中的下標 */ public void DFS(int k){ visited[k]=1; lineNode p=new lineNode(); p=vexList[k].getNext(); /** * 終點永遠是關鍵節點 */ visited[finall]=0; /** * 如果走到終點遍歷棧的元素 */ if(k==finall) { for (int i=0;i<stack2.size;i++) { System.out.print(stack2.peekTravel(i).getName() + "->"); } } /** * 遍歷節點后面的鏈表 */ while (p!=null){ if(visited[p.getAdjvex()]!=1){ /** * 是關鍵路徑就入棧 */ stack2.push(p); DFS(p.getAdjvex()); /** * 遇見死路回退的時候出棧 */ stack2.pop(); } System.out.println(); p=p.getNext(); } } /** * 活動節點 */ class lineNode { /** * 存儲該頂點對應的下標 */ private int adjvex; /** * 存儲權值 */ private int info; /** * 指向下一個節點 */ private lineNode next; /** * 活動的名字 */ private String name; public boolean isMark() { return mark; } public void setMark(boolean mark) { this.mark = mark; } /** * 標識位:標識有沒有遍歷過 */ private boolean mark; /** * 弧頭 * */ private int head; public int getHead() { return head; } public void setHead(int head) { this.head = head; } public String getName() { return name; } public void setName(String name) { this.name = name; } public lineNode(int head,int adjvex, int info, lineNode next, String name,boolean mark) { this.adjvex = adjvex; this.info = info; this.next = next; this.name=name; this.mark=mark; this.head=head; } public lineNode() { } public int getAdjvex() { return adjvex; } public void setAdjvex(int adjvex) { this.adjvex = adjvex; } public int getInfo() { return info; } public void setInfo(int info) { this.info = info; } public lineNode getNext() { return next; } public void setNext(lineNode next) { this.next = next; } } /** * 事件節點 */ class vertexNode { /** * 節點的名字 */ private String name; /** * 入度 */ private int in; /** * 儲存頂點數組的下標 */ private int data; /** * 邊的節點的頭指針 */ private lineNode next; public int getIn() { return in; } public int getData() { return data; } public lineNode getNext() { return next; } public void setIn(int in) { this.in = in; } public void setData(int data) { this.data = data; } public void setNext(lineNode next) { this.next = next; } public vertexNode( int data, lineNode next, String name) { this.data = data; this.next = next; this.name = name; } public vertexNode() { } public String getName() { return name; } public void setName(String name) { this.name = name; } } public static void main(String[] args) { lessonWord a=new lessonWord(); a.craeteGraph(); // a.createGraph(); a.criticalPath(); a.DFS(0); } } 棧: package dataproject.stack; import dataproject.importanRoad.lessonWord; import java.util.Arrays; /** * @author 李正陽 17060208112 * @param <E> 泛型 */ public class myArrayStack<E> { private int DEFAULT_SIZE = 16;//定義棧的初始默認長度 private int capacity;//保存順序棧的長度 public int size;//保存順序棧中元素的個數 private Object[] elementData;//定義一個數組用於保存順序棧中的元素 public myArrayStack() { capacity = DEFAULT_SIZE; elementData = new Object[capacity]; } //以指定的大小來創建棧 public myArrayStack(int initSize){ capacity = 1; while(capacity < initSize) { capacity <<= 1;//將capacity設置成大於initSize的最小2次方 elementData = new Object[capacity]; } } //返回當前順序棧中元素的個數 public int length() { return size; } public E pop() { if(empty()) { throw new IndexOutOfBoundsException("棧空,不能出棧"); } E oldValue = (E)elementData[size - 1]; elementData[--size] = null;//讓垃圾回收器及時回收,避免內存泄露 return oldValue; } public void push(E element) { ensureCapacity(size + 1); elementData[size++] = element; } private void ensureCapacity(int minCapacity){ if(minCapacity > capacity){ while(capacity < minCapacity) { capacity <<= 1; elementData = Arrays.copyOf(elementData, capacity); } } } //獲取棧頂元素,不會將棧頂元素刪除 public E peek() { if(size == 0) { throw new ArrayIndexOutOfBoundsException("棧為空"); } return (E)elementData[size - 1]; } /** * @param d 這個元素的位置 * @return 返回d所處的元素 */ public E peekTravel(int d) { if(size == 0) { throw new ArrayIndexOutOfBoundsException("棧為空"); } return (E)elementData[d]; } public boolean empty() { return size == 0; } public void clear() { for(int i = 0; i < size; i++) { elementData[i] = null; } size = 0; } public static void main(String[] args) { } }
流程圖: