本文是图论算法的补充, 详见我另一篇博客 图的算法
截止目前 我是实现了 最小生成树,最短路径,遍历,拓扑排序和关键路径算法。 一再强调 图上的算法不依赖于图的具体实现,我给出了邻接矩阵和邻接表上的实现作为参考
开始之前,回顾一下上期我们定义在图上的API
package lee.graph;
import java.io.FileNotFoundException;
public interface Graph {
public void initGraph(String path) throws FileNotFoundException;
public int getWeight(int v , int w);
public int getFirstNeighbor(int v);
public int getNextNeighbor(int v, int w);
public void setWeight(int v, int w , int power);
public void printGraph();
public int getNumVertex();
public int getNumEdge();
public int getOutDegree(int v);
public int getInDegree(int v);
public int getFirstPro(int v);
public int getNextPro(int v, int w);
}
getFirstPro(v);
getNextPro(v, w)
是这一期增加的, 其作用后面阐述。
拓扑排序:
public static int[] topologicalSort(Graph g){
int[] inDegree = new int[g.getNumVertex()];
int [] sortResult = new int[g.getNumVertex()];
int count = 0 ;
Stack<Integer> stack1 = new ArrayStack<Integer>();
Stack<Integer> stack2 = new LinkedStack<Integer>();
for(int i=0; i<g.getNumVertex();i++){
inDegree[i] = g.getInDegree(i);
if(inDegree[i]==0){
stack1.push(i);
}
}
while(stack1.isEmpty()){
int v = stack1.pop();
stack2.push(v);
sortResult[count++] = v;
int w = g.getFirstNeighbor(v);
while(w!=-1){
inDegree[w]--;
if(inDegree[w]==0){
stack1.push(w);
}
w = g.getNextNeighbor(v, w);
}
}
if(count != g.getNumVertex()){
System.out.println("This is circle in the graph!!!");
}
return sortResult;
}
关键路径:
public static void criticalPaht(Graph g){
int[] inDegree = new int[g.getNumVertex()];
//Stack<Integer> stack1 = new ArrayStack<Integer>();
Queue<Integer> queue = new CircleQueue<Integer>();
Stack<Integer> stack2 = new LinkedStack<Integer>();
int[] etv = new int[g.getNumVertex()];
for(int i=0; i<g.getNumVertex();i++){
etv[i] = 0;
inDegree[i] = g.getInDegree(i);
if(g.getInDegree(i)==0){
queue.enQuene(i);
}
}
int count=0;
while(!queue.isEmpty()){
int v = queue.deQueue();
count++;
stack2.push(v);
for(int w = g.getFirstNeighbor(v); w!=-1;w = g.getNextNeighbor(v, w)){
//inDegree[w]--;
if(--inDegree[w]==0){
queue.enQuene(w);
}
if(etv[v]+g.getWeight(v, w)>etv[w]){
etv[w]=etv[v]+g.getWeight(v, w);
}
}
}
if(count!=g.getNumVertex()){
System.out.println("This is circle in the graph!!!");
System.out.println(count);
}
//-------------------------------------------------------------------
int[] ltv = new int[g.getNumVertex()];
for(int i=0; i<g.getNumVertex();i++){
ltv[i] = etv[g.getNumVertex()-1];
}
while(!stack2.isEmpty()){
int v = stack2.pop();
for(int w = g.getFirstPro(v);w!=-1;w=g.getNextPro(v, w)){
if(ltv[v]-g.getWeight(w, v) < ltv[w]){
ltv[w]=ltv[v]-g.getWeight(w, v);
}
}
}
//-----------------------out put the critical Path---------------------------
for(int i=0;i<g.getNumVertex();i++){
for(int w=g.getFirstNeighbor(i); w!=-1; w=g.getNextNeighbor(i, w)){
//System.out.println(" w="+w);
if(etv[i] == ltv[w]-g.getWeight(i, w)){
System.out.println(i + "->" + w);
}
}
}
}
关键路径算法 前面部分是拓扑排序算法。 为了好玩, 我在关键路径算法中用 队列替换了工作栈。
getFirstPro(v);
getNextPro(v, w)
这一对方法很奇怪吧。
getFirstPro(v);
其实是有向图v节点的第一个前驱节点
同理
getNextPro(v, w)
是v节点对相对w节点的下一个前驱节点。
Matrix实现的图还好。 邻接表实现,如果没有逆邻接表,时间复杂度对会上来。我就没有写逆邻接表。曾经认为逆邻接表没啥用。图大了还是需要空间换取时间的
在Matrix上的实现
@Override
public int getFirstPro(int v) {
for(int i=0; i<numVertex;i++){
if(matrix[i][v]!=999 && i!=v){
return i;
}
}
return -1;
}
@Override
public int getNextPro(int v, int w) {
for(int i=w+1; i<numVertex;i++){
if(matrix[i][v]!=999 && v!=i){
return i;
}
}
return -1;
}
邻接表上的实现:
public int getFirstPro(int v) {
for(int i=0;i<numVertex;i++){
GNode p = arr[i].next;
while(p!=null){
if(p.num == v){
return i;
}
p=p.next; // 更正
}
}
return -1;
}
@Override
public int getNextPro(int v, int w) {
for(int i=w+1;i<numVertex;i++){
GNode p = arr[i].next;
while(p!=null){
if(p.num == v){
return i;
}
p=p.next;// 更正
}
}
return -1;
}
这些代码是 上一篇 图上算法的一部分。 可以拷贝到GraphAlgorithms中运行, 所用栈,队列,堆等工具是我自己写的。 如果要运行需要调用JAVA提供的库,或者自己实现。
我的工具类实现可以参考blog中的 工具类实现篇
测试数据:
0 1 3
0 2 4
1 4 6
1 3 5
2 3 8
2 5 7
3 4 3
4 6 9
4 7 4
5 7 6
6 9 2
7 8 5
8 9 3
执行结果:
0->2
2->3
3->4
4->7
7->8
8->9