Java数据结构——关键路径

文章目录

  • 一、更新ALGraph
    • (一)、Java代码
  • 四、关键路径
    • (一)、定义
    • (二)、Java代码
      • 1. 由邻接表获取各个顶点入度
      • 2. 基于邻接表的拓扑排序
      • 3. 关键路径算法
      • 4. 源码
      • 5. 输出样例

一、更新ALGraph

(一)、Java代码

package graph;

/**
 * 邻接表
 * 
 * Adjacency List Graph
 * 
 * @author 己千之
 * @time 2022/2/16(周三)
 */
public class ALGraph {
	
	/**
	 * @vexs
	 * 顶点数组
	 */
	public VexNode[] vexs;
	
	/**
	 * @arcs 
	 * 边数组 
	 */
	ArcNode[] arcs;
	
	
	/**
	 * 顶点,边个数
	 */
	int vexNum, arcNum;

	/**
	 * 边的信息结点,如权值
	 */
	static class Info {
		String value;
		int weight;

		public Info(String value) {
			this.value = value;
		}

		public Info(int weight) {
			this.weight = weight;
		}

		public Info(String value, int weight) {
			this.value = value;
			this.weight = weight;
		}
		
		public String toString() {
			return this.value + "=" + this.weight;
		}
	}
	
	/**
	 * 弧结点,表结点
	 */
	class ArcNode {
		int index;
		Info info; // 把int改为String
		ArcNode nextNode;
	}

	/**
	 * 顶点,表头结点
	 */
	class VexNode {
		String data;
		ArcNode firstArc;
	}

	/**
	 * 边关系
	 * 
	 * @static:测试方法中,用到了Edge数组 Edge是个内部类,你要用,只能: 1.把Edge作为参数,可以用 2.把Edge设为静态调用
	 */
	static class Edge {
		String start;
		String end;
		Info info;

		public Edge(String start, String end, Info info) {
			this.start = start;
			this.end = end;
			this.info = new Info(info.value, info.weight);
		}
	}

	/**
	 * 构造方法,构造有向图
	 * 
	 * @param vertex
	 * @param edge
	 */
	public ALGraph(String[] vertexs, Edge[] edges) {
		// TODO
		// 1.设置成员变量大小
		vexs = new VexNode[vertexs.length];
		arcs = new ArcNode[edges.length];
		vexNum = vertexs.length;
		arcNum = edges.length;

		// 2.初始化数组
		for (int i = 0; i < vexs.length; i++) {
			vexs[i] = new VexNode();
			vexs[i].data = vertexs[i];
			vexs[i].firstArc = null;
		}

		// 3.链接
		for (int i = 0; i < edges.length; i++) {

			// 3.1 由new Edge('A', 'B', 10)来链接
			String start = edges[i].start;
			String end = edges[i].end;
			int indexOfStart = indexOfVex(start);
			int indexOfEnd = indexOfVex(end);

			// 3.2 构造一个新的arcNode类型的结点
			ArcNode arcNode = new ArcNode();
			
			// one: index
			arcNode.index = indexOfEnd;
			
			// two: info
			arcNode.info = edges[i].info; // TODO ?

			// three: nextNode
			linkedLast(indexOfStart, arcNode); // 尾插法,有向,不是无向
			
			// 3.3 更新arcs
			arcs[i] = arcNode;
		}
	} // stucture

	/**
	 * 获取顶点元素位置
	 * 
	 * @param v
	 * @return
	 */
	public int indexOfVex(String v) {
		// 定位顶点的位置

		for (int i = 0; i < vexs.length; i++) {
			if (vexs[i].data == v)
				return i;
		}
		return -1;
	} // indexOfVex

	/**
	 * 尾插法,将新元素插在链表尾部
	 * 
	 * @param index
	 * @param node
	 */
	public void linkedLast(int indexofstart, ArcNode arcnode) {
		if (vexs[indexofstart].firstArc == null) {

			// 关键代码
			vexs[indexofstart].firstArc = arcnode;
		} else {
			ArcNode tempNode = vexs[indexofstart].firstArc;
			while (tempNode.nextNode != null) {
				tempNode = tempNode.nextNode;
			}

			// 关键代码
			tempNode.nextNode = arcnode;
		}
	} // linkedLast

	/**
	 * 像邻接表一样打印
	 */
	public void printALGraphByChar() {
		System.out.println("邻接表存储的图:");
		for (int i = 0; i < vexs.length; i++) {

			// 1.打印顶点
			System.out.print(vexs[i].data + "----->");

			// 2.打印挂接链表
			if (vexs[i].firstArc != null) {

				// 2.1 firstArc
				ArcNode tempNode = vexs[i].firstArc;

				// 2.2 nextArc
				while (tempNode.nextNode != null) {
					System.out.print(vexs[tempNode.index].data + "--->");
					tempNode = tempNode.nextNode;
				}

				// 2.2 tempNode.nextNode = null
				System.out.print(vexs[tempNode.index].data);
			} else {
				System.out.print("NULL");
			}

			// 3. 换行
			System.out.println();
		}
	} // printALGraph

	public void printALGraphByIndex() {
		System.out.println("邻接表存储的图:");
		for (int i = 0; i < vexs.length; i++) {

			// 1.打印顶点
			System.out.print(i + "----->");

			// 2.打印挂接链表
			if (vexs[i].firstArc != null) {

				// 2.1 firstArc
				ArcNode tempNode = vexs[i].firstArc;

				// 2.2 nextArc
				while (tempNode.nextNode != null) {
					System.out.print(tempNode.index + "--->");
					tempNode = tempNode.nextNode;
				}

				// 2.2 tempNode.nextNode = null
				System.out.print(tempNode.index);
			} else {
				System.out.print("NULL");
			}

			// 3.换行
			System.out.println();
		}
	} // printALGraph

	/**
	 * 测试
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO
		// 1.顶点集
		String[] vexs = { "A", "B", "C", "D", "E" };

		// 2.边集,输入无向图
        Edge[] edges = {
                new Edge("A", "B", new Info("权值", 10)), 
                new Edge("A", "D", new Info("权值", 10)), 
                new Edge("B", "A", new Info("权值", 20)),
                new Edge("B", "C", new Info("权值", 20)),
                new Edge("B", "E", new Info("权值", 20)), 
                new Edge("C", "B", new Info("权值", 30)), 
                new Edge("C", "D", new Info("权值", 30)), 
                new Edge("C", "E", new Info("权值", 30)), 
                new Edge("D", "A", new Info("权值", 40)),
                new Edge("D", "C", new Info("权值", 40)),
                new Edge("E", "B", new Info("权值", 50)),
                new Edge("E", "C", new Info("权值", 50))
        };

		ALGraph algraph = new ALGraph(vexs, edges);
		// 打印图的邻接表
		algraph.printALGraphByChar();
		algraph.printALGraphByIndex();

	}
}

四、关键路径

(一)、定义

  • AOV-网相对应的是AOE-网,即边表示活动的网。
  • AOE-网,是有向无环图。顶点表示事件,弧表示活动。权表示活动持续时间。
  • AOE-网通常用来估算工程的完成时间,以及哪些活动是影响工程进展的关键。
  • 路径长度:路径上各活动持续时间的总和(即路径上所有权之和)。
  • 完成工程的最短时间:从工程开始点(源点:入度为零,只有一个)到完成点(汇点:出度为零,只有一个)的最长路径称为完成工程的最短时间。
  • 关键路径:路径长度最长的路径称为关键路径。

(二)、Java代码

1. 由邻接表获取各个顶点入度

	/**
	 * 由邻接表获取各个顶点入度
	 * 
	 * @return 入度数组
	 */
	public int[] getInDegrees() {

		// 1. 初始大小
		int[] inDegrees = new int[vexNum];

		// 2. 计算每个顶点入度
		for (int i = 0; i < vexNum; i++) {

			// 2.1 每个顶点算玩,重置
			ArcNode arcNode = null;

			// 2.2 算法
			for (int j = 0; j < vexNum; j++) {

				// one: tempNode, 避免把原来的结构改变
				ArcNode tempNode = vexs[j].firstArc;

				// two: 链表往后走
				while (tempNode != null) {
					inDegrees[tempNode.index]++;

					arcNode = tempNode.nextNode;
					tempNode = arcNode;
				}
			} // end for j
		} // end for i

		// 3. 每个顶点算了 vexNum 次
		for (int i = 0; i < vexNum; i++) {
			inDegrees[i] /= vexNum;
		}

		// 4. 返回
		return inDegrees;
	}

2. 基于邻接表的拓扑排序

	/**
	 * 基于邻接表的拓扑排序
	 * 
	 * @return
	 */
	public boolean topoSort() {
		System.out.println("拓扑排序(基于邻接表):");
		
		// 1. 声明
		int count = 0;
		
		// 2. 入度
		int[] inDegrees = getInDegrees();
		
		// 3. 建立零入度栈
		for(int i=0; i<inDegrees.length; i++) {
			if(inDegrees[i] == 0) {
				stack.push(i);
			}
		}
		
		// 4. while
		while(!stack.isEmpty()) {
			int index = stack.pop();
			count++;
			
			System.out.print(vexs[index].data + " ");
			
			ArcNode tempNode = vexs[index].firstArc;
			while(tempNode != null) {
				int nextIndex = tempNode.index;
				ve[nextIndex] = Math.max(ve[nextIndex], ve[index]+tempNode.info.weight);
				inDegrees[nextIndex]--;
				if(inDegrees[nextIndex] == 0) {
					stack.push(nextIndex);
				}
				tempNode = tempNode.nextNode;
			}
		} // end while
		
		return count == vexNum;
	} // topoSort

3. 关键路径算法

	/**
	 * 关键路径算法
	 */
	public void criticalPath() {
		// TODO
		// 1. 判断
		if(!isTopo()) {
			throw new RuntimeException("是个环装图,没有关键路径");
		}
		
		// 2. vl:事件最晚发生时间
		for(int i=0; i<ve.length; i++) {
			vl[i] = ve[ve.length - 1];
		}
		while(!viewStack.isEmpty()) {
			int index = viewStack.pop();
			ArcNode tempNode = vexs[index].firstArc;
			while(tempNode != null) {
				int nextIndex = tempNode.index;
				vl[index] = Math.min(vl[index], vl[nextIndex]-tempNode.info.weight);
				tempNode = tempNode.nextNode;
			}
		}
		
		// 3. el, ee 活动 ai
		int arcCount = 0;
		for(int i=0; i<vexNum; i++) {
			ArcNode tempNode = vexs[i].firstArc;
			while(tempNode != null) {
				int index = tempNode.index;
				ee[arcCount] = ve[i];
				el[arcCount] = vl[index] - tempNode.info.weight;
				arcCount++;
				tempNode = tempNode.nextNode;
			}
		}
		
        // 4. 关键事件
        System.out.print("关键事件:");
        for(int i=0;i<vexNum-1;i++){
            if(vl[i]==ve[i]){
                System.out.print(vexs[i].data + "----->");
            }
        }
        if(vl[vexNum-1] == ve[vexNum-1]) {
        	System.out.print(vexs[vexNum-1].data);
        } else {
        	System.out.print("NULL");
        }
        System.out.println();
        
        // 5. 关键活动
        System.out.print("关键活动:");
        for(int i=0;i<arcNum;i++){
            if(ee[i]==el[i]){
                System.out.print("  " + arcs[i].info.toString() + "  "); // 前后各两个空格
            }
        }
        System.out.println();
	} // criticalPath

4. 源码

package graph;

import java.util.Stack;

public class CriticalPath extends ALGraph {

	/** ve 事件最早发生时间 */
	int[] ve;
	/** vl 事件最晚发生时间 */
	int[] vl;
	/** ee 活动最早发生时间 */
	int[] ee;
	/** el 活动最晚发生时间 */
	int[] el;
	
	Stack<Integer> stack = new Stack<Integer>();
	Stack<Integer> viewStack = new Stack<Integer>();

	public CriticalPath(String[] vertexs, Edge[] edges) {
		super(vertexs, edges);

		ve = new int[vertexs.length];
		vl = new int[vertexs.length];
		ee = new int[edges.length];
		el = new int[edges.length];
	}

	/**
	 * 由邻接表获取各个顶点入度
	 * 
	 * @return 入度数组
	 */
	public int[] getInDegrees() {

		// 1. 初始大小
		int[] inDegrees = new int[vexNum];

		// 2. 计算每个顶点入度
		for (int i = 0; i < vexNum; i++) {

			// 2.1 每个顶点算玩,重置
			ArcNode arcNode = null;

			// 2.2 算法
			for (int j = 0; j < vexNum; j++) {

				// one: tempNode, 避免把原来的结构改变
				ArcNode tempNode = vexs[j].firstArc;

				// two: 链表往后走
				while (tempNode != null) {
					inDegrees[tempNode.index]++;

					arcNode = tempNode.nextNode;
					tempNode = arcNode;
				}
			} // end for j
		} // end for i

		// 3. 每个顶点算了 vexNum 次
		for (int i = 0; i < vexNum; i++) {
			inDegrees[i] /= vexNum;
		}

		// 4. 返回
		return inDegrees;
	}

	/**
	 * 由邻接表获取各个顶点出度
	 * 
	 * @return 出读数组
	 */
	public int[] getOutDegrees() {
		int[] outDegrees = new int[vexNum];
		int out = 0;

		ArcNode vexNode = null;
		for (int i = 0; i < vexNum; i++) {

			ArcNode tempNode = vexs[i].firstArc;
			while (tempNode != null) {
				out++;

				vexNode = tempNode.nextNode;
				tempNode = vexNode;
			} // end while

			outDegrees[i] = out;
			out = 0;
		} // end for i

		return outDegrees;
	} // getOutdegrees

	/**
	 * 基于邻接表的拓扑排序
	 * 
	 * @return
	 */
	public boolean topoSort() {
		System.out.println("拓扑排序(基于邻接表):");
		
		// 1. 声明
		int count = 0;
		
		// 2. 入度
		int[] inDegrees = getInDegrees();
		
		// 3. 建立零入度栈
		for(int i=0; i<inDegrees.length; i++) {
			if(inDegrees[i] == 0) {
				stack.push(i);
			}
		}
		
		// 4. while
		while(!stack.isEmpty()) {
			int index = stack.pop();
			count++;
			
			System.out.print(vexs[index].data + " ");
			
			ArcNode tempNode = vexs[index].firstArc;
			while(tempNode != null) {
				int nextIndex = tempNode.index;
				ve[nextIndex] = Math.max(ve[nextIndex], ve[index]+tempNode.info.weight);
				inDegrees[nextIndex]--;
				if(inDegrees[nextIndex] == 0) {
					stack.push(nextIndex);
				}
				tempNode = tempNode.nextNode;
			}
		} // end while
		
		return count == vexNum;
	} // topoSort
	
	/**
	 * 是否无环
	 * @return
	 */
	public boolean isTopo() {
		
		// 1. 声明
		int count = 0;

		// 2. 入度
		int[] inDegrees = getInDegrees();
		
		// 3. 建立零入度栈
		for(int i=0; i<inDegrees.length; i++) {
			if(inDegrees[i] == 0) {
				stack.push(i);
				viewStack.push(i); // copy
			}
		}
		
		// 4. while
		while(!stack.isEmpty()) {
			int index = stack.pop();
			count++;
			
			ArcNode tempNode = vexs[index].firstArc;
			while(tempNode != null) {
				int nextIndex = tempNode.index;
				ve[nextIndex] = Math.max(ve[nextIndex], ve[index]+tempNode.info.weight);
				inDegrees[nextIndex]--;
				if(inDegrees[nextIndex] == 0) {
					stack.push(nextIndex);
					viewStack.push(nextIndex); // copy
				}
				tempNode = tempNode.nextNode;
			}
		} // end while
		
		return count == vexNum;
	} // isTopo

	/**
	 * 关键路径算法
	 */
	public void criticalPath() {
		// TODO
		// 1. 判断
		if(!isTopo()) {
			throw new RuntimeException("是个环装图,没有关键路径");
		}
		
		// 2. vl:事件最晚发生时间
		for(int i=0; i<ve.length; i++) {
			vl[i] = ve[ve.length - 1];
		}
		while(!viewStack.isEmpty()) {
			int index = viewStack.pop();
			ArcNode tempNode = vexs[index].firstArc;
			while(tempNode != null) {
				int nextIndex = tempNode.index;
				vl[index] = Math.min(vl[index], vl[nextIndex]-tempNode.info.weight);
				tempNode = tempNode.nextNode;
			}
		}
		
		// 3. el, ee 活动 ai
		int arcCount = 0;
		for(int i=0; i<vexNum; i++) {
			ArcNode tempNode = vexs[i].firstArc;
			while(tempNode != null) {
				int index = tempNode.index;
				ee[arcCount] = ve[i];
				el[arcCount] = vl[index] - tempNode.info.weight;
				arcCount++;
				tempNode = tempNode.nextNode;
			}
		}
		
        // 4. 关键事件
        System.out.print("关键事件:");
        for(int i=0;i<vexNum-1;i++){
            if(vl[i]==ve[i]){
                System.out.print(vexs[i].data + "----->");
            }
        }
        if(vl[vexNum-1] == ve[vexNum-1]) {
        	System.out.print(vexs[vexNum-1].data);
        } else {
        	System.out.print("NULL");
        }
        System.out.println();
        
        // 5. 关键活动
        System.out.print("关键活动:");
        for(int i=0;i<arcNum;i++){
            if(ee[i]==el[i]){
                System.out.print("  " + arcs[i].info.toString() + "  "); // 前后各两个空格
            }
        }
        System.out.println();
	} // criticalPath

	public static void main(String[] args) {
		// TODO
		// 1.顶点集
		String[] vexs = { "V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9" };

		// 2.边集,输入无向图
        Edge[] edges = {
                new Edge("V0", "V1", new Info("a0", 3)),
                new Edge("V0", "V2", new Info("a1", 4)), 
                new Edge("V1", "V3", new Info("a2", 5)), 
                new Edge("V1", "V4", new Info("a3", 6)), 
                new Edge("V2", "V3", new Info("a4", 8)), 
                new Edge("V2", "V5", new Info("a5", 7)), 
                new Edge("V3", "V4", new Info("a6", 3)), 
                new Edge("V4", "V6", new Info("a7", 9)), 
                new Edge("V4", "V7", new Info("a8", 4)), 
                new Edge("V5", "V7", new Info("a9", 6)), 
                new Edge("V6", "V9", new Info("a10", 2)), 
                new Edge("V7", "V8", new Info("a11", 5)),  
                new Edge("V8", "V9", new Info("a12", 3))
        };

		CriticalPath AOE = new CriticalPath(vexs, edges);
		AOE.printALGraphByChar();
		System.out.println("是否无环:" + AOE.isTopo());
		AOE.criticalPath();

	}
} // CriticalPath

5. 输出样例


  • Java数据结构——关键路径_第1张图片

  • Java数据结构——关键路径_第2张图片

你可能感兴趣的:(Java数据结构,java,图论,数据结构)