目录
第 31 天: 整数矩阵及其运算
矩阵在前十天已经写过相关运算。
今天加了一点新东西:
1、用 this 调用其它的构造方法以减少冗余代码
2、Exception 的抛出与捕获机制:
1 异常的概念
“exception”顾名思义,就是“我对此感到意外”的意思,在程序中可以强制编译器来处理程序运行中的发生的并非由程序本身所造成的错误,这个处理方法在java中就叫做异常捕获,而这些并非程序本身产生的错误就叫做“异常”。
2 基本异常
举一个抛出异常的简单例子,对于对像引用t,传给你的时候可能没有初始化,那么在使用这个对象调用其方法前可以先对它进行检查,像这样:
if(t==null)
throws new NullPointerException();
这就是抛出了一个异常,那么在当前环境下就不必担心这个可能出现的空指针异常了,它将在别的地方获得处理。使用异常处理机制的好处就是异常允许我们强制程序停止运行,并告诉我们出现了什么问题。或者强制程序处理问题,并返回稳定状态。
3 如何捕获异常
如果方法内部出现了异常,这个方法将在抛出异常时结束,如果不希望该方法就此结束,就可以设置一个try代码块,在这个try代码块中尝试调用方法,结果如下:
try{
//要尝试运行的代码
}
4 异常处理程序
如果要将抛出的异常进行恰当的处理,就得准备相应的处理程序,异常处理程序紧跟在try代码块之后,以关键字catch表示:
try{
//要尝试运行的代码
}catch(){
//处理该异常的代码
}
(关于catch语句的异常类型:如果是catch语句的异常类型是CapacityToolException的父类,那么程序也回进入到catch语句中。)
矩阵的三种构造方法:
//1
public IntMatrix(int paraRows, int paraColumns) {
data = new int[paraRows][paraColumns];
}
//2
public IntMatrix(int[][] paraMatrix) {
data = new int[paraMatrix.length][paraMatrix[0].length];
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[0].length; j++) {
data[i][j] = paraMatrix[i][j];
}
}
}
//3
public IntMatrix(IntMatrix paraMatrix) {
this(paraMatrix.getData());
}
矩阵加法:
public void add(IntMatrix paraMatrix) throws Exception {
int[][] tempData = paraMatrix.getData();
if (data.length != tempData.length) {
throw new Exception("Cannot add matrices. Rows not match: " + data.length + " vs. "
+ tempData.length + ".");
}
if (data[0].length != tempData[0].length) {
throw new Exception("Cannot add matrices. Rows not match: " + data[0].length + " vs. "
+ tempData[0].length + ".");
}
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[0].length; j++) {
data[i][j] += tempData[i][j];
}
}
}
public static IntMatrix add(IntMatrix paraMatrix1, IntMatrix paraMatrix2) throws Exception {
IntMatrix resultMatrix = new IntMatrix(paraMatrix1);
resultMatrix.add(paraMatrix2);
return resultMatrix;
}
矩阵乘法:
public static IntMatrix multiply(IntMatrix paraMatrix1, IntMatrix paraMatrix2)
throws Exception {
int[][] tempData1 = paraMatrix1.getData();
int[][] tempData2 = paraMatrix2.getData();
if (tempData1[0].length != tempData2.length) {
throw new Exception("Cannot multiply matrices: " + tempData1[0].length + " vs. "
+ tempData2.length + ".");
}
int[][] resultData = new int[tempData1.length][tempData2[0].length];
for (int i = 0; i < tempData1.length; i++) {
for (int j = 0; j < tempData2[0].length; j++) {
for (int k = 0; k < tempData1[0].length; k++) {
resultData[i][j] += tempData1[i][k] * tempData2[k][j];
}
}
}
IntMatrix resultMatrix = new IntMatrix(resultData);
return resultMatrix;
}
矩阵两种运算运行结果:
第 32 天: 图的连通性检测
令图的连通矩阵为M,若矩阵中第i行第j列元素为0,则表示i不可到达j,要知道图的连通性,则需计算M0+M1+M2+…+Mn-1得到的矩阵中是否有非零元素,如果有就不连通,否则是连通的。其中M0是单位矩阵。
代码:
package tu;
import tu.IntMatrix;
public class Graph {
IntMatrix connectivityMatrix;
public Graph(int paraNumNodes) {
connectivityMatrix = new IntMatrix(paraNumNodes, paraNumNodes);
}
public Graph(int[][] paraMatrix) {
connectivityMatrix = new IntMatrix(paraMatrix);
}
public String toString() {
String resultString = "This is the connectivity matrix of the graph.\r\n"
+ connectivityMatrix;
return resultString;
}
public boolean getConnectivity() throws Exception {
//初始化单位矩阵
IntMatrix tempConnectivityMatrix = IntMatrix
.getIdentityMatrix(connectivityMatrix.getData().length);
//初始化矩阵M1
IntMatrix tempMultipliedMatrix = new IntMatrix(connectivityMatrix);
//计算
for (int i = 0; i < connectivityMatrix.getData().length - 1; i++) {
// M_a = M_a + M^k
tempConnectivityMatrix.add(tempMultipliedMatrix);
// M^k
tempMultipliedMatrix = IntMatrix.multiply(tempMultipliedMatrix, connectivityMatrix);
}
//检查连通性
System.out.println("The connectivity matrix is: " + tempConnectivityMatrix);
int[][] tempData = tempConnectivityMatrix.getData();
for (int i = 0; i < tempData.length; i++) {
for (int j = 0; j < tempData.length; j++) {
if (tempData[i][j] == 0) {
System.out.println("Node " + i + " cannot reach " + j);
return false;
}
}
}
return true;
}
public static void getConnectivityTest() {
//测试无向图
int[][] tempMatrix = { { 0, 1, 0 }, { 1, 0, 1 }, { 0, 1, 0 } };
Graph tempGraph2 = new Graph(tempMatrix);
System.out.println(tempGraph2);
boolean tempConnected = false;
try {
tempConnected = tempGraph2.getConnectivity();
} catch (Exception ee) {
System.out.println(ee);
}
System.out.println("Is the graph connected? " + tempConnected);
//测试有向图
//去掉一条弧形成有向图
tempGraph2.connectivityMatrix.setValue(1, 0, 0);
tempConnected = false;
try {
tempConnected = tempGraph2.getConnectivity();
} catch (Exception ee) {
System.out.println(ee);
}
System.out.println("Is the graph connected? " + tempConnected);
}
public static void main(String args[]) {
System.out.println("Hello!");
Graph tempGraph = new Graph(3);
System.out.println(tempGraph);
getConnectivityTest();
}
}
分别测试了无向图和有向图的连通性:
第 33 天: 图的广度优先遍历
广度优先遍历(BFS)类似于树的层次遍历。
广度优先遍历图解:
如图所示,假设A为起点,A入队,先访问A,A出队,然后访问与A相连的未访问过的相邻的点,B,C,D入队,再访问B,B出队,与B相邻且相连的未访问过的点是E,E入队,再访问C,C出队,C相邻且相连的点有D,E,F,但是D和E都已访问过,所以只有F入队,再访问D,D出队,与G相邻且相连的未放过的只有G,G入队,然后访问E…直到所有元素出队。
代码:
public String breadthFirstTraversal(int paraStartIndex) {
CircleObjectQueue tempQueue = new CircleObjectQueue();
String resultString = "";
int tempNumNodes = connectivityMatrix.getRows();
boolean[] tempVisitedArray = new boolean[tempNumNodes];
tempVisitedArray[paraStartIndex] = true;
//初始化队列
tempVisitedArray[paraStartIndex] = true;
resultString += paraStartIndex;
tempQueue.enqueue(new Integer(paraStartIndex));
//从起点开始广度遍历图
int tempIndex;
Integer tempInteger = (Integer)tempQueue.dequeue();
while (tempInteger != null) {
tempIndex = tempInteger.intValue();
//将所有未访问的相邻点入队
for (int i = 0; i < tempNumNodes; i ++) {
if (tempVisitedArray[i]) {
continue; //已经访问过的不管
}
if (connectivityMatrix.getData()[tempIndex][i] == 0) {
continue; //没有相连
}
//入队前访问点
tempVisitedArray[i] = true;
resultString += i;
tempQueue.enqueue(new Integer(i));
}
//取出队首元素
tempInteger = (Integer)tempQueue.dequeue();
}
return resultString;
}
public static void breadthFirstTraversalTest() {
//测试无向图
int[][] tempMatrix = { { 0, 1, 1, 0 }, { 1, 0, 0, 1 }, { 1, 0, 0, 1}, { 0, 1, 1, 0} };
Graph tempGraph = new Graph(tempMatrix);
System.out.println(tempGraph);
String tempSequence = "";
try {
tempSequence = tempGraph.breadthFirstTraversal(2);
} catch (Exception ee) {
System.out.println(ee);
}
System.out.println("The breadth first order of visit: " + tempSequence);
}
BFS结果:
第 34 天: 图的深度优先遍历
深度优先遍历(dfs):
从图中某顶点v出发:
(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
以昨天的图为例:
假设A为起点,访问A相邻的未访问的点B,再访问B的相邻未访问点E,然后是C、D、G、F。
代码:
public String depthFirstTraversal(int paraStartIndex) {
ObjectStack tempStack = new ObjectStack();
String resultString = "";
int tempNumNodes = connectivityMatrix.getRows();
boolean[] tempVisitedArray = new boolean[tempNumNodes];
tempVisitedArray[paraStartIndex] = true;
//初始化栈
tempVisitedArray[paraStartIndex] = true;
resultString += paraStartIndex;
tempStack.push(new Integer(paraStartIndex));
System.out.println("Push " + paraStartIndex);
System.out.println("Visited " + resultString);
//访问图中的其他点
int tempIndex = paraStartIndex;
int tempNext;
Integer tempInteger;
while (true){
//找到一个未访问的邻点
tempNext = -1;
for (int i = 0; i < tempNumNodes; i ++) {
if (tempVisitedArray[i]) {
continue; //已访问过的跳过
}
if (connectivityMatrix.getData()[tempIndex][i] == 0) {
continue; //不是相邻的
}
//Visit this one.
tempVisitedArray[i] = true;
resultString += i;
tempStack.push(new Integer(i));
System.out.println("Push " + i);
tempNext = i;
break;
}
if (tempNext == -1) {
//没有未访问的邻点则回溯
if (tempStack.isEmpty()) {
break;
}
tempInteger = (Integer)tempStack.pop();
System.out.println("Pop " + tempInteger);
tempIndex = tempInteger.intValue();
} else {
tempIndex = tempNext;
}
}
return resultString;
}
public static void depthFirstTraversalTest() {
//测试一个无向图
int[][] tempMatrix = { { 0, 1, 1, 0 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0}, { 0, 1, 0, 0} };
Graph tempGraph = new Graph(tempMatrix);
System.out.println(tempGraph);
String tempSequence = "";
try {
tempSequence = tempGraph.depthFirstTraversal(0);
} catch (Exception ee) {
System.out.println(ee);
}
System.out.println("The depth first order of visit: " + tempSequence);
}
dfs结果:
第 35 天: 图的 m 着色问题
图的m-着色判定问题——给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色,是否有一种着色法使G中任意相邻的2个顶点着不同颜色?
当涂完一条路线的颜色,回溯(return 返回上一层)
public void coloring(int paraNumColors) {
//初始化
int tempNumNodes = connectivityMatrix.getRows();
int[] tempColorScheme = new int[tempNumNodes];
Arrays.fill(tempColorScheme, -1);
coloring(paraNumColors, 0, tempColorScheme);
}
public void coloring(int paraNumColors, int paraCurrentNumNodes, int[] paraCurrentColoring) {
//初始化
int tempNumNodes = connectivityMatrix.getRows();
System.out.println("coloring: paraNumColors = " + paraNumColors + ", paraCurrentNumNodes = "
+ paraCurrentNumNodes + ", paraCurrentColoring" + Arrays.toString(paraCurrentColoring));
//涂完了所有颜色,回溯
if (paraCurrentNumNodes >= tempNumNodes) {
System.out.println("Find one:" + Arrays.toString(paraCurrentColoring));
return;
}
//尝试所有可能的颜色
for (int i = 0; i < paraNumColors; i++) {
paraCurrentColoring[paraCurrentNumNodes] = i;
if (!colorConflict(paraCurrentNumNodes + 1, paraCurrentColoring)) {
coloring(paraNumColors, paraCurrentNumNodes + 1, paraCurrentColoring);
}
}
}
public boolean colorConflict(int paraCurrentNumNodes, int[] paraColoring) {
for (int i = 0; i < paraCurrentNumNodes - 1; i++) {
if (connectivityMatrix.getValue(paraCurrentNumNodes - 1, i) == 0) {
continue;
}
if (paraColoring[paraCurrentNumNodes - 1] == paraColoring[i]) {
return true;
}
}
return false;
}
public static void coloringTest() {
int[][] tempMatrix = { { 0, 1, 1, 0 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 }, { 0, 1, 0, 0 } };
Graph tempGraph = new Graph(tempMatrix);
tempGraph.coloring(3);
}
第 36 天: 邻连表
图的压缩存储,每一行数据用一个单链表存储。
如下图:
图的邻接表表示:该邻接表里存储了相邻节点和到该节点距离以及next指针域
代码:
class NeighborNode {
int data;
NeighborNode next;
public NeighborNode(int data) {
this.data = data;
next = null;
}
}
int numNodes;
NeighborNode[] headers;
public NeighborList(int[][] paraMatrix) {
numNodes = paraMatrix.length;
NeighborNode tpNode;
headers = new NeighborNode[numNodes];
//连接每个节点的相邻点
for (int i = 0; i < numNodes; i++) {
headers[i] = new NeighborNode(i);
tpNode = headers[i];
for (int j = 0; j < numNodes; j++) {
if (paraMatrix[i][j] == 0) {
continue;
}
tpNode.next = new NeighborNode(j);
tpNode = tpNode.next;
}
}
}
public String toString() {
String resString = "NeighborList: \n";
NeighborNode tpNode;
for (int i = 0; i < numNodes; i++) {
tpNode = headers[i];
while (tpNode.next != null) {
resString += tpNode.data + "->";
tpNode = tpNode.next;
}
resString += tpNode.data + "\n";
}
return resString;
}
//bfs
public String breadthFirstTraversal(int paraStartIndex) {
CircleObjectQueue tpQueue = new CircleObjectQueue();
String resString = "";
boolean[] tpVisitedArray = new boolean[numNodes];
tpVisitedArray[paraStartIndex] = true;
resString += paraStartIndex;
tpQueue.enqueue(paraStartIndex);
//遍历节点
int tpIndex;
Integer tempInteger = (Integer) tpQueue.dequeue();
NeighborNode tpNode;
while (tempInteger != null) {
tpIndex = tempInteger.intValue();
tpNode = headers[tpIndex];
while (tpNode.next != null) {
tpNode = tpNode.next;
if (tpVisitedArray[tpNode.data]) {
continue;
}
tpQueue.enqueue(tpNode.data);
tpVisitedArray[tpNode.data] = true;
resString += tpNode.data;
}
tempInteger = (Integer) tpQueue.dequeue();
}
return resString;
}
public static void breadthFirstTraversalTest() {
//测试无向图
int[][] tempMatrix = {{0, 1, 1, 0}, {1, 0, 0, 1}, {1, 0, 0, 1}, {0, 1, 1, 0}};
NeighborList tpNeighborList = new NeighborList(tempMatrix);
System.out.println(tpNeighborList);
String tempSequence = "";
try {
tempSequence = tpNeighborList.breadthFirstTraversal(2);
} catch (Exception e) {
System.out.println(e);
}
System.out.println("The breadth first order of visit: " + tempSequence);
}
//dfs
public String depthFirstTraversal(int paraStartIndex) {
ObjectStack tpStack = new ObjectStack();
String resString = "";
boolean[] tpVisitedArray = new boolean[numNodes];
tpVisitedArray[paraStartIndex] = true;
NeighborNode tpNode = headers[paraStartIndex];
resString += paraStartIndex;
tpStack.push(tpNode);
int num = 1;
while (num < numNodes) {
while (tpNode.next != null && tpVisitedArray[tpNode.next.data]) {
tpNode = tpNode.next;
}
//回溯
if (tpNode.next == null) {
tpNode = (NeighborNode) tpStack.pop();
continue;
}
resString += tpNode.next.data;
tpStack.push(tpNode.next);
tpVisitedArray[tpNode.next.data] = true;
tpNode = headers[tpNode.next.data];
num++;
}
return resString;
}
public static void depthFirstTraversalTest() {
//测试无向图
int[][] tempMatrix = {{0, 1, 1, 0}, {1, 0, 0, 1}, {1, 0, 0, 0}, {0, 1, 0, 0}};
NeighborList tpNeighborList = new NeighborList(tempMatrix);
System.out.println(tpNeighborList);
String tempSequence = "";
try {
tempSequence = tpNeighborList.depthFirstTraversal(0);
} catch (Exception ee) {
System.out.println(ee);
}
System.out.println("The depth first order of visit: " + tempSequence);
}
第 37 天: 十字链表
十字链表(Orthogonal List)是有向图的另一种链式存储结构。该结构可以看成是将有向图的邻接表和逆邻接表结合起来得到的。用十字链表来存储有向图,可以达到高效的存取效果。
class OrthogonalNode {
int row;
int column;
//出入节点
OrthogonalNode nextOut;
OrthogonalNode nextIn;
public OrthogonalNode(int row, int column) {
this.row = row;
this.column = column;
nextIn = null;
nextOut = null;
}
}
int numNodes;
OrthogonalNode[] headers;
public OrthogonalList(int[][] paraMatrix) {
numNodes = paraMatrix.length;
//初始化
OrthogonalNode tpPreNode, tpNode;
headers = new OrthogonalNode[numNodes];
//连接出节点
for (int i = 0; i < numNodes; i++) {
headers[i] = new OrthogonalNode(i, -1);
tpPreNode = headers[i];
for (int j = 0; j < numNodes; j++) {
if (paraMatrix[i][j] == 0) {
continue;
}
tpNode = new OrthogonalNode(i, j);
tpPreNode.nextOut = tpNode;
tpPreNode = tpNode;
}
}
//连接入节点
OrthogonalNode[] tpColumnNodes = new OrthogonalNode[numNodes];
for (int i = 0; i < numNodes; i++) {
tpColumnNodes[i] = headers[i];
}
for (int i = 0; i < numNodes; i++) {
tpNode = headers[i].nextOut;
while(tpNode!=null){
tpColumnNodes[tpNode.column].nextIn = tpNode;
tpColumnNodes[tpNode.column] = tpNode;
tpNode = tpNode.nextOut;
}
}
}
public String toString() {
String resultString = "Out arcs: \n";
OrthogonalNode tempNode;
for (int i = 0; i < numNodes; i++) {
tempNode = headers[i].nextOut;
while (tempNode != null) {
resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
tempNode = tempNode.nextOut;
}
resultString += "\r\n";
}
resultString += "\r\nIn arcs: \n";
for (int i = 0; i < numNodes; i++) {
tempNode = headers[i].nextIn;
while (tempNode != null) {
resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
tempNode = tempNode.nextIn;
}
resultString += "\r\n";
}
return resultString;
}
public static void main(String args[]) {
int[][] tempMatrix = { { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 0 }, { 0, 1, 1, 0 } };
OrthogonalList tempList = new OrthogonalList(tempMatrix);
System.out.println("The data are:\r\n" + tempList);
}
第 38 天: Dijkstra 算法与 Prim 算法
迪杰斯特拉(Dijkstra)算法是单源最短路径算法,用于计算一个节点到其他节点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。
1.指定一个节点,例如要计算 ‘A’ 到其他节点的最短路径
2.引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,如上图所示,A->C由于没有直接相连 初始时为∞)
3.初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0
4.U集合初始时为 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞
从U集合中找出路径最短的点,加入S集合,例如 A->D = 2
5.更新U集合路径,if ( ‘D 到 B,C,E 的距离’ + ‘AD 距离’ < ‘A 到 B,C,E 的距离’ ) 则更新U
6.循环执行 4、5 两步骤,直至遍历结束,得到A 到其他节点的最短路径
prim算法:
1.输入:一个加权连通图,其中顶点集合为V,边集合为E;
2.初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3.重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4.输出:使用集合Vnew和Enew来描述所得到的最小生成树。
算法代码:
public class Net {
public static final int MAX_DISTANCE = 10000;
int numNodes;
IntMatrix weightMatrix;
public Net(int paraNumNodes) {
numNodes = paraNumNodes;
weightMatrix = new IntMatrix(numNodes, numNodes);
for (int i = 0; i < numNodes; i++) {
Arrays.fill(weightMatrix.getData()[i], MAX_DISTANCE);
}
}
public Net(int[][] paraMatrix) {
weightMatrix = new IntMatrix(paraMatrix);
numNodes = weightMatrix.getRows();
}
public String toString() {
String resultString = "This is the weight matrix of the graph.\r\n" + weightMatrix;
return resultString;
}
public int[] shortestPath(int paraSource) {
//初始化
int[] tempDistanceArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
tempDistanceArray[i] = weightMatrix.getValue(paraSource, i);
}
int[] tempParentArray = new int[numNodes];
Arrays.fill(tempParentArray, paraSource);
//-1表示没有父结点
tempParentArray[paraSource] = -1;
//已访问的节点不考虑
boolean[] tempVisitedArray = new boolean[numNodes];
tempVisitedArray[paraSource] = true;
//主循环
int tempMinDistance;
int tempBestNode = -1;
for (int i = 0; i < numNodes - 1; i++) {
//找出最佳节点
tempMinDistance = Integer.MAX_VALUE;
for (int j = 0; j < numNodes; j++) {
// 节点被访问
if (tempVisitedArray[j]) {
continue;
}
if (tempMinDistance > tempDistanceArray[j]) {
tempMinDistance = tempDistanceArray[j];
tempBestNode = j;
}
}
tempVisitedArray[tempBestNode] = true;
//准备下一轮循环
for (int j = 0; j < numNodes; j++) {
//节点不能到达
if (tempVisitedArray[j]) {
continue;
}
//节点不能到达
if (weightMatrix.getValue(tempBestNode, j) >= MAX_DISTANCE) {
continue;
}
if (tempDistanceArray[j] > tempDistanceArray[tempBestNode]
+ weightMatrix.getValue(tempBestNode, j)) {
//改变距离
tempDistanceArray[j] = tempDistanceArray[tempBestNode]
+ weightMatrix.getValue(tempBestNode, j);
//改变父结点
tempParentArray[j] = tempBestNode;
}
}
//测试
System.out.println("The distance to each node: " + Arrays.toString(tempDistanceArray));
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
}
//输出
System.out.println("Finally");
System.out.println("The distance to each node: " + Arrays.toString(tempDistanceArray));
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
return tempDistanceArray;
}
public int prim() {
//初始化,任何节点都可以是七点
int tempSource = 0;
int[] tempDistanceArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
tempDistanceArray[i] = weightMatrix.getValue(tempSource, i);
}
int[] tempParentArray = new int[numNodes];
Arrays.fill(tempParentArray, tempSource);
//-1表示没有父结点
tempParentArray[tempSource] = -1;
//已访问的节点不考虑
boolean[] tempVisitedArray = new boolean[numNodes];
tempVisitedArray[tempSource] = true;
//主循环
int tempMinDistance;
int tempBestNode = -1;
for (int i = 0; i < numNodes - 1; i++) {
//找出一个最佳节点
tempMinDistance = Integer.MAX_VALUE;
for (int j = 0; j < numNodes; j++) {
//节点被访问
if (tempVisitedArray[j]) {
continue;
}
if (tempMinDistance > tempDistanceArray[j]) {
tempMinDistance = tempDistanceArray[j];
tempBestNode = j;
}
}
tempVisitedArray[tempBestNode] = true;
//准备下一轮
for (int j = 0; j < numNodes; j++) {
//节点被访问
if (tempVisitedArray[j]) {
continue;
}
//节点不能到达
if (weightMatrix.getValue(tempBestNode, j) >= MAX_DISTANCE) {
continue;
}
if (tempDistanceArray[j] > weightMatrix.getValue(tempBestNode, j)) {
//改变距离
tempDistanceArray[j] = weightMatrix.getValue(tempBestNode, j);
//改变父结点
tempParentArray[j] = tempBestNode;
}
}
//测试
System.out.println(
"The selected distance for each node: " + Arrays.toString(tempDistanceArray));
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
}
int resultCost = 0;
for (int i = 0; i < numNodes; i++) {
resultCost += tempDistanceArray[i];
}
//输出
System.out.println("Finally");
System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
System.out.println("The total cost: " + resultCost);
return resultCost;
}
public static void main(String args[]) {
Net tempNet = new Net(3);
System.out.println(tempNet);
int[][] tempMatrix = { { 0, 9, 3, 6 }, { 5, 0, 2, 4 }, { 3, 2, 0, 1 }, { 2, 8, 7, 0 } };
tempNet = new Net(tempMatrix);
System.out.println(tempNet);
tempNet.shortestPath(1);
//需要无向图
tempNet.prim();
}
}
第 39 天: 关键路径
AOE网:在一个表示工程的带权有向图中,用顶点表示事件(如V0),用有向边表示活动(如<v0,v1> = a0),边上的权值表示活动的持续时间,称这样的有向图为边表示的活动的网,简称AOE网(activity on edge network)
如图,起点为V0,终点为V3.
1、只有在进入某顶点的活动都已经结束,该顶点所代表的事件才发生。例如:a1,a2活动都结束了,顶点V2所代表的事件才会发生。
2、只有在某顶点所代表的事件发生后,从该顶点出发的各活动才开始。例如:只有顶点V1所代表的事件结束之后,活动a2和a4才会开始。
事件的最早发生时间:ve[k]
根据AOE网的性质,只有进入Vk的所有活动<Vj, Vk>都结束,Vk代表的事件才能发生,而活动<Vj, Vk>的最早结束时间为ve[j]+len<Vj, Vk>。所以,计算Vk的最早发生时间的方法为:
ve[0] = 0
ve[k] = max(ve[j] + len<Vj, Vk>)
事件的最迟发生时间:vl[k]
vl[k]是指在不推迟整个工期的前提下,事件Vk允许的最迟发生时间。根据AOE网的性质,只有顶点Vk代表的事件发生,从Vk出发的活动<Vk, Vj>才能开始,而活动<Vk, Vj>的最晚开始时间为vl[j] - len<Vk, Vj>。
关键路径的计算:(以下图为例)
事件的最早发生时间:ve[k]
从源点向终点方向计算
ve[0] = 0
ve[1] = ve[0] + a0 = 0 + 4 = 4
ve[2] = max( ve[0] + a1, ve[1] + a2 ) = max(0 + 3, 4 + 2 = 6
ve[3] = max(ve[1] + a4, ve[2] + a3) = max(4 + 6, 3 + 4) = 10
事件的最迟发生时间:vl[k]
从终点向源点方向计算
vl[3] = ve[3] = 10
vl[2] = vl[3] - a3 = 10 - 4 = 6
vl[1] = min(vl[3] - a4, vl[2] - a2) = min(10-6, 6-2) = 4
vl[0] = min(vl[2] - a1, vl[1] - a0) = min(4-4, 4-2) = 0
最早开始时间和最晚开始时间相等,则说明属于关键路径。
代码:
(正向拓扑算每个节点的最早开始时间, 逆向拓扑算每个节点的最晚开始时间)
public boolean[] criticalPath() {
int tempValue;
//算每个节点入度
int[] tempInDegrees = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(i, j) != -1) {
tempInDegrees[j]++;
}
}
}
System.out.println("In-degree of nodes: " + Arrays.toString(tempInDegrees));
//拓扑排序
int[] tempEarliestTimeArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
//节点不能移除
if (tempInDegrees[i] > 0) {
continue;
}
System.out.println("Removing " + i);
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(i, j) != -1) {
tempValue = tempEarliestTimeArray[i] + weightMatrix.getValue(i, j);
if (tempEarliestTimeArray[j] < tempValue) {
tempEarliestTimeArray[j] = tempValue;
}
tempInDegrees[j]--;
}
}
}
System.out.println("Earlest start time: " + Arrays.toString(tempEarliestTimeArray));
//计算每个节点的出度
int[] tempOutDegrees = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(i, j) != -1) {
tempOutDegrees[i]++;
}
}
}
System.out.println("Out-degree of nodes: " + Arrays.toString(tempOutDegrees));
//逆向拓扑排序
int[] tempLatestTimeArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
tempLatestTimeArray[i] = tempEarliestTimeArray[numNodes - 1];
}
for (int i = numNodes - 1; i >= 0; i--) {
// 该节点不能移除
if (tempOutDegrees[i] > 0) {
continue;
}
System.out.println("Removing " + i);
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(j, i) != -1) {
tempValue = tempLatestTimeArray[i] - weightMatrix.getValue(j, i);
if (tempLatestTimeArray[j] > tempValue) {
tempLatestTimeArray[j] = tempValue;
tempOutDegrees[j]--;
System.out.println("The out-degree of " + j + " decreases by 1.");
}
}
System.out.println("Latest start time: " + Arrays.toString(tempLatestTimeArray));
boolean[] resultCriticalArray = new boolean[numNodes];
for (int i = 0; i < numNodes; i++) {
if (tempEarliestTimeArray[i] == tempLatestTimeArray[i]) {
resultCriticalArray[i] = true;
}
}
System.out.println("Critical array: " + Arrays.toString(resultCriticalArray));
System.out.print("Critical nodes: ");
for (int i = 0; i < numNodes; i++) {
if (resultCriticalArray[i]) {
System.out.print(" " + i);
}
}
System.out.println();
return resultCriticalArray;
}
第 40 天: 小结
描述这 10 天的学习体会, 不少于 10 条.
1.复习了用 this 可以调用其它的构造方法以减少冗余代码,和Exception 的抛出与捕获的机制;
2.复习了矩阵运算,发现在图的算法中用矩阵运算会变得很方便;
3.检测图的连通性可以通过矩阵运算实现;
4.复习了dfs和bfs算法,前者用栈后者用队列;
5.复习了经典回溯图的m着色问题,就是dfs;
6.图的压缩存储方式是用邻接表,也可以组成十字链表,就是将有向图的邻接表和逆邻接表结合起来;
7.迪杰斯特拉(Dijkstra)算法是单源最短路径算法,用于计算一个节点到其他节点的最短路径,与它类似的还有克鲁斯卡尔算法,是计算多源最短路径的,即每个节点到其他各节点的最短路径;
8.prim算法用于最小生成树,即要保证图的连通性的最少的边连接而成的最小权值的树;
9.计算关键路径需要用到拓扑排序,用正向拓扑计算关键路径上事件发生的最早时间,逆向拓扑计算最晚时间;
10.最早开始时间和最晚开始时间相等,则说明属于关键路径,这是一种简便方法,实际上还要看路径上的活动的完成最早和最晚的时间,如果相等则是关键路径上的活动。