日撸 Java 三百行(31-40天,图)

目录

总述
01-10天,基本语法
11-20天,线性数据结构
21-30天,树与二叉树
31-40天,图
41-50天,查找与排序
51-60天,kNN 与 NB
61-70天,决策树与集成学习
71-80天,BP 神经网络
81-90天,CNN 卷积神经网络

第 31 天: 整数矩阵及其运算

这个代码以前有基础. 原想着写矩阵连通性, 把这个当成开胃菜的, 后来发现这个的代码量已经够了. 良心发现, 把这个做成一天的工作.

  1. 矩阵对象的创建.
  2. getRows 等: getter, setter 在 java 里面很常用. 主要是为了访问控制.
  3. 整数矩阵的加法、乘法.
  4. Exception 的抛出与捕获机制.
  5. 用 this 调用其它的构造方法以减少冗余代码.
  6. 代码看起来多, 但矩阵运算我们以前写过.
  7. 把数据类型修改成 double, 获得 DoubleMatrix.java, 以后会很有用.
  8. getIdentityMatrix: 单位矩阵.
  9. resultMatrix.data[i][i]: 成员变量的访问权限: 在同一类里面是可以直接使用的.
package matrix;

import java.util.Arrays;

/**
 * Int matrix. For efficiency we do not define ObjectMatrix. One can revise it
 * to obtain DoubleMatrix.
 * 
 * @author Fan Min [email protected].
 */
public class IntMatrix {
	/**
	 * The data.
	 */
	int[][] data;

	/**
	 *********************
	 * The first constructor.
	 * 
	 * @param paraRows
	 *            The number of rows.
	 * @param paraColumns
	 *            The number of columns.
	 *********************
	 */
	public IntMatrix(int paraRows, int paraColumns) {
		data = new int[paraRows][paraColumns];
	}// Of the first constructor

	/**
	 *********************
	 * The second constructor. Construct a copy of the given matrix.
	 * 
	 * @param paraMatrix
	 *            The given matrix.
	 *********************
	 */
	public IntMatrix(int[][] paraMatrix) {
		data = new int[paraMatrix.length][paraMatrix[0].length];

		// Copy elements.
		for (int i = 0; i < data.length; i++) {
			for (int j = 0; j < data[0].length; j++) {
				data[i][j] = paraMatrix[i][j];
			} // Of for j
		} // Of for i
	}// Of the second constructor

	/**
	 *********************
	 * The third constructor. Construct a copy of the given matrix.
	 * 
	 * @param paraMatrix
	 *            The given matrix.
	 *********************
	 */
	public IntMatrix(IntMatrix paraMatrix) {
		this(paraMatrix.getData());
	}// Of the third constructor

	/**
	 *********************
	 * Get identity matrix. The values at the diagonal are all 1.
	 * 
	 * @param paraRows
	 *            The given rows.
	 *********************
	 */
	public static IntMatrix getIdentityMatrix(int paraRows) {
		IntMatrix resultMatrix = new IntMatrix(paraRows, paraRows);
		for (int i = 0; i < paraRows; i++) {
			// According to access control, resultMatrix.data can be visited
			// directly.
			resultMatrix.data[i][i] = 1;
		} // Of for i
		return resultMatrix;
	}// Of getIdentityMatrix

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		return Arrays.deepToString(data);
	}// Of toString

	/**
	 *********************
	 * Get my data. Warning, the reference to the data instead of a copy of the
	 * data is returned.
	 * 
	 * @return The data matrix.
	 *********************
	 */
	public int[][] getData() {
		return data;
	}// Of getData

	/**
	 *********************
	 * Getter.
	 * 
	 * @return The number of rows.
	 *********************
	 */
	public int getRows() {
		return data.length;
	}// Of getRows

	/**
	 *********************
	 * Getter.
	 * 
	 * @return The number of columns.
	 *********************
	 */
	public int getColumns() {
		return data[0].length;
	}// Of getColumns

	/**
	 *********************
	 * Set one the value of one element.
	 * 
	 * @param paraRow
	 *            The row of the element.
	 * @param paraColumn
	 *            The column of the element.
	 * @param paraValue
	 *            The new value.
	 *********************
	 */
	public void setValue(int paraRow, int paraColumn, int paraValue) {
		data[paraRow][paraColumn] = paraValue;
	}// Of setValue

	/**
	 *********************
	 * Get the value of one element.
	 * 
	 * @param paraRow
	 *            The row of the element.
	 * @param paraColumn
	 *            The column of the element.
	 *********************
	 */
	public int getValue(int paraRow, int paraColumn) {
		return data[paraRow][paraColumn];
	}// Of getValue

	/**
	 *********************
	 * Add another matrix to me.
	 * 
	 * @param paraMatrix
	 *            The other matrix.
	 *********************
	 */
	public void add(IntMatrix paraMatrix) throws Exception {
		// Step 1. Get the data of the given matrix.
		int[][] tempData = paraMatrix.getData();

		// Step 2. Size check.
		if (data.length != tempData.length) {
			throw new Exception("Cannot add matrices. Rows not match: " + data.length + " vs. "
					+ tempData.length + ".");
		} // Of if
		if (data[0].length != tempData[0].length) {
			throw new Exception("Cannot add matrices. Rows not match: " + data[0].length + " vs. "
					+ tempData[0].length + ".");
		} // Of if

		// Step 3. Add to me.
		for (int i = 0; i < data.length; i++) {
			for (int j = 0; j < data[0].length; j++) {
				data[i][j] += tempData[i][j];
			} // Of for j
		} // Of for i
	}// Of add

	/**
	 *********************
	 * Add two existing matrices.
	 * 
	 * @param paraMatrix1
	 *            The first matrix.
	 * @param paraMatrix2
	 *            The second matrix.
	 * @return A new matrix.
	 *********************
	 */
	public static IntMatrix add(IntMatrix paraMatrix1, IntMatrix paraMatrix2) throws Exception {
		// Step 1. Clone the first matrix.
		IntMatrix resultMatrix = new IntMatrix(paraMatrix1);

		// Step 2. Add the second one.
		resultMatrix.add(paraMatrix2);

		return resultMatrix;
	}// Of add

	/**
	 *********************
	 * Multiply two existing matrices.
	 * 
	 * @param paraMatrix1
	 *            The first matrix.
	 * @param paraMatrix2
	 *            The second matrix.
	 * @return A new matrix.
	 *********************
	 */
	public static IntMatrix multiply(IntMatrix paraMatrix1, IntMatrix paraMatrix2)
			throws Exception {
		// Step 1. Check size.
		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 + ".");
		} // Of if

		// Step 2. Allocate space.
		int[][] resultData = new int[tempData1.length][tempData2[0].length];

		// Step 3. Multiply.
		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];
				} // Of for k
			} // Of for j
		} // Of for i

		// Step 4. Construct the matrix object.
		IntMatrix resultMatrix = new IntMatrix(resultData);

		return resultMatrix;
	}// Of multiply

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args
	 *            Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		IntMatrix tempMatrix1 = new IntMatrix(3, 3);
		tempMatrix1.setValue(0, 1, 1);
		tempMatrix1.setValue(1, 0, 1);
		tempMatrix1.setValue(1, 2, 1);
		tempMatrix1.setValue(2, 1, 1);
		System.out.println("The original matrix is: " + tempMatrix1);

		IntMatrix tempMatrix2 = null;
		try {
			tempMatrix2 = IntMatrix.multiply(tempMatrix1, tempMatrix1);
		} catch (Exception ee) {
			System.out.println(ee);
		} // Of try
		System.out.println("The square matrix is: " + tempMatrix2);

		IntMatrix tempMatrix3 = new IntMatrix(tempMatrix2);
		try {
			tempMatrix3.add(tempMatrix1);
		} catch (Exception ee) {
			System.out.println(ee);
		} // Of try
		System.out.println("The connectivity matrix is: " + tempMatrix3);
	}// Of main

}// Of class IntMatrix

第 32 天: 图的连通性检测

令图的连通矩阵为 M M M, M 0 = I M^0 = I M0=I 为单位矩阵. 要知道图的连通性, 只需要计算 M a = M 0 + M 1 + ⋯ + M n − 1 M_a = M^0 + M^1 + \dots + M^{n - 1} Ma=M0+M1++Mn1, 其中 n n n 是节点个数. M a M_a Ma 中第 i i i 行第 j j j 列元素为 0 的话, 就表示从节点 i i i 到节点 j j j 不可达.

  1. 适用于有向图. 反正无向图是有向图的特殊形式.
  2. 0 次方的时候是单位矩阵.
  3. 为每一个方法写一个独立的测试方法. 测试代码有时比正常使用的代码更多.
  4. 第一个测试用例是无向图, 第二个是有向图. 可以看到, 后者从节点 1 不能到达节点 0.
  5. Matrix 基础代码准备好之后, 其它的算法真的很方便. 后面会进一步体会到其威力.
package datastructure.graph;

import matrix.IntMatrix;

/**
 * Directed graph. Note that undirected graphs are a special case of directed
 * graphs.
 * 
 * @author Fan Min [email protected].
 */
public class Graph {

	/**
	 * The connectivity matrix.
	 */
	IntMatrix connectivityMatrix;

	/**
	 *********************
	 * The first constructor.
	 * 
	 * @param paraNumNodes
	 *            The number of nodes in the graph.
	 *********************
	 */
	public Graph(int paraNumNodes) {
		connectivityMatrix = new IntMatrix(paraNumNodes, paraNumNodes);
	}// Of the first constructor

	/**
	 *********************
	 * The second constructor.
	 * 
	 * @param paraMatrix
	 *            The data matrix.
	 *********************
	 */
	public Graph(int[][] paraMatrix) {
		connectivityMatrix = new IntMatrix(paraMatrix);
	}// Of the second constructor

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		String resultString = "This is the connectivity matrix of the graph.\r\n"
				+ connectivityMatrix;
		return resultString;
	}// Of toString

	/**
	 *********************
	 * Get the connectivity of the graph.
	 * 
	 * @throws Exception
	 *             for internal error.
	 *********************
	 */
	public boolean getConnectivity() throws Exception {
		// Step 1. Initialize accumulated matrix: M_a = I.
		IntMatrix tempConnectivityMatrix = IntMatrix
				.getIdentityMatrix(connectivityMatrix.getData().length);

		// Step 2. Initialize M^1.
		IntMatrix tempMultipliedMatrix = new IntMatrix(connectivityMatrix);

		// Step 3. Determine the actual connectivity.
		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);
		} // Of for i

		// Step 4. Check the connectivity.
		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;
				} // Of if
			} // Of for j
		} // Of for i

		return true;
	}// Of getConnectivity

	/**
	 *********************
	 * Unit test for getConnectivity.
	 *********************
	 */
	public static void getConnectivityTest() {
		// Test an undirected graph.
		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);
		} // Of try.

		System.out.println("Is the graph connected? " + tempConnected);

		// Test a directed graph.
		// Remove one arc to form a directed graph.
		tempGraph2.connectivityMatrix.setValue(1, 0, 0);

		tempConnected = false;
		try {
			tempConnected = tempGraph2.getConnectivity();
		} catch (Exception ee) {
			System.out.println(ee);
		} // Of try.

		System.out.println("Is the graph connected? " + tempConnected);
	}// Of getConnectivityTest

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args
	 *            Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		System.out.println("Hello!");
		Graph tempGraph = new Graph(3);
		System.out.println(tempGraph);

		// Unit test.
		getConnectivityTest();
	}// Of main

}// Of class Graph

第 33 天: 图的广度优先遍历

  1. 与树的广度优先遍历类似. 温故而知新. 应该看懂了自己写一遍.
  2. 为每个核心方法写一个测试方法吧. 这叫单元测试
  3. 如果图不一定是连通的, 就需要用到 张星移的代码.
	/**
	 *********************
	 * Breadth first traversal.
	 * 
	 * @param paraStartIndex The start index.
	 * @return The sequence of the visit.
	 *********************
	 */
	public String breadthFirstTraversal(int paraStartIndex) {
		CircleObjectQueue tempQueue = new CircleObjectQueue();
		String resultString = "";
		
		int tempNumNodes = connectivityMatrix.getRows();
		boolean[] tempVisitedArray = new boolean[tempNumNodes];
		
		//Initialize the queue.
		//Visit before enqueue.
		tempVisitedArray[paraStartIndex] = true;
		resultString += paraStartIndex;
		tempQueue.enqueue(new Integer(paraStartIndex));
		
		//Now visit the rest of the graph.
		int tempIndex;
		Integer tempInteger = (Integer)tempQueue.dequeue();
		while (tempInteger != null) {
			tempIndex = tempInteger.intValue();
					
			//Enqueue all its unvisited neighbors.
			for (int i = 0; i < tempNumNodes; i ++) {
				if (tempVisitedArray[i]) {
					continue; //Already visited.
				}//Of if
				
				if (connectivityMatrix.getData()[tempIndex][i] == 0) {
					continue; //Not directly connected.
				}//Of if
				
				//Visit before enqueue.
				tempVisitedArray[i] = true;
				resultString += i;
				tempQueue.enqueue(new Integer(i));
			}//Of for i
			
			//Take out one from the head.
			tempInteger = (Integer)tempQueue.dequeue();
		}//Of while
		
		return resultString;
	}//Of breadthFirstTraversal
	
	/**
	 *********************
	 * Unit test for breadthFirstTraversal.
	 *********************
	 */
	public static void breadthFirstTraversalTest() {
		// Test an undirected graph.
		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);
		} // Of try.

		System.out.println("The breadth first order of visit: " + tempSequence);
	}//Of breadthFirstTraversalTest

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args
	 *            Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		System.out.println("Hello!");
		Graph tempGraph = new Graph(3);
		System.out.println(tempGraph);

		// Unit test.
		getConnectivityTest();
		
		breadthFirstTraversalTest();
	}// Of main

第 34 天: 图的深度优先遍历

  1. 与二叉树的深度优先遍历类似. 但要难一点.
  2. 又见 while (true), 循环的退出条件是栈为空.
  3. 需要在前面 import ObjectStack.
	/**
	 *********************
	 * Depth first traversal.
	 * 
	 * @param paraStartIndex The start index.
	 * @return The sequence of the visit.
	 *********************
	 */
	public String depthFirstTraversal(int paraStartIndex) {
		ObjectStack tempStack = new ObjectStack();
		String resultString = "";
		
		int tempNumNodes = connectivityMatrix.getRows();
		boolean[] tempVisitedArray = new boolean[tempNumNodes];
		
		//Initialize the stack.
		//Visit before push.
		tempVisitedArray[paraStartIndex] = true;
		resultString += paraStartIndex;
		tempStack.push(new Integer(paraStartIndex));
		System.out.println("Push " + paraStartIndex);
		System.out.println("Visited " + resultString);
		
		//Now visit the rest of the graph.
		int tempIndex = paraStartIndex;
		int tempNext;
		Integer tempInteger;
		while (true){
		//Find an unvisited neighbor.
			tempNext = -1;
			for (int i = 0; i < tempNumNodes; i ++) {
				if (tempVisitedArray[i]) {
					continue; //Already visited.
				}//Of if
				
				if (connectivityMatrix.getData()[tempIndex][i] == 0) {
					continue; //Not directly connected.
				}//Of if
				
				//Visit this one.
				tempVisitedArray[i] = true;
				resultString += i;
				tempStack.push(new Integer(i));
				System.out.println("Push " + i);
				tempNext = i;
				
				//One is enough.
				break;
			}//Of for i
			
			if (tempNext == -1) {
				tempInteger = (Integer) tempStack.pop();
				System.out.println("Pop " + tempInteger);
				if (tempStack.isEmpty()) {
					// No unvisited neighbor. Backtracking to the last one
					// stored in the stack.
					// Attention: This is the terminate condition!
					break;
				} else {
					// Back to the previous node, however do not remove it.
					tempInteger = (Integer) tempStack.pop();
					tempIndex = tempInteger.intValue();
					tempStack.push(tempInteger);
				} // Of if
			} else {
				tempIndex = tempNext;
			} // Of if
		}//Of while
		
		return resultString;
	}//Of depthFirstTraversal
	
	/**
	 *********************
	 * Unit test for depthFirstTraversal.
	 *********************
	 */
	public static void depthFirstTraversalTest() {
		// Test an undirected graph.
		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);
		} // Of try.

		System.out.println("The depth first order of visit: " + tempSequence);
	}//Of depthFirstTraversalTest

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args
	 *            Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		System.out.println("Hello!");
		Graph tempGraph = new Graph(3);
		System.out.println(tempGraph);

		// Unit test.
		getConnectivityTest();
		
		breadthFirstTraversalTest();

		depthFirstTraversalTest();
	}// Of main

第 35 天: 图的 m 着色问题

  1. 经典的回溯算法. 万能的暴力解题法, 一定要掌握啊!
  2. 调拭时注意 +1, -1 之类的下标控制.
  3. 单独写一个冲突检测方法.
  4. 由于它仅使用了图的连接性, 所以放在这个部分.
	/**
	 *********************
	 * Coloring. Output all possible schemes.
	 * 
	 * @param paraNumColors The number of colors.
	 *********************
	 */
	public void coloring(int paraNumColors) {
		// Step 1. Initialize.
		int tempNumNodes = connectivityMatrix.getRows();
		int[] tempColorScheme = new int[tempNumNodes];
		Arrays.fill(tempColorScheme, -1);

		coloring(paraNumColors, 0, tempColorScheme);
	}// Of coloring

	/**
	 *********************
	 * Coloring. Output all possible schemes.
	 * 
	 * @param paraNumColors The number of colors.
	 * @param paraCurrentNumNodes The number of nodes that have been colored.
	 * @param paraCurrentColoring The array recording the coloring scheme.
	 *********************
	 */
	public void coloring(int paraNumColors, int paraCurrentNumNodes, int[] paraCurrentColoring) {
		// Step 1. Initialize.
		int tempNumNodes = connectivityMatrix.getRows();

		System.out.println("coloring: paraNumColors = " + paraNumColors + ", paraCurrentNumNodes = "
				+ paraCurrentNumNodes + ", paraCurrentColoring" + Arrays.toString(paraCurrentColoring));
		// A complete scheme.
		if (paraCurrentNumNodes >= tempNumNodes) {
			System.out.println("Find one:" + Arrays.toString(paraCurrentColoring));
			return;
		} // Of if

		// Try all possible colors.
		for (int i = 0; i < paraNumColors; i++) {
			paraCurrentColoring[paraCurrentNumNodes] = i;
			if (!colorConflict(paraCurrentNumNodes + 1, paraCurrentColoring)) {
				coloring(paraNumColors, paraCurrentNumNodes + 1, paraCurrentColoring);
			} // Of if
		} // Of for i
	}// Of coloring

	/**
	 *********************
	 * Coloring conflict or not. Only compare the current last node with previous
	 * ones.
	 * 
	 * @param paraCurrentNumNodes The current number of nodes.
	 * @param paraColoring        The current coloring scheme.
	 * @return Conflict or not.
	 *********************
	 */
	public boolean colorConflict(int paraCurrentNumNodes, int[] paraColoring) {
		for (int i = 0; i < paraCurrentNumNodes - 1; i++) {
			// No direct connection.
			if (connectivityMatrix.getValue(paraCurrentNumNodes - 1, i) == 0) {
				continue;
			} // Of if

			if (paraColoring[paraCurrentNumNodes - 1] == paraColoring[i]) {
				return true;
			} // Of if
		} // Of for i
		return false;
	}// Of colorConflict

	/**
	 *********************
	 * Coloring test.
	 *********************
	 */
	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(2);
		tempGraph.coloring(3);
	}// Of coloringTest

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		System.out.println("Hello!");
		Graph tempGraph = new Graph(3);
		System.out.println(tempGraph);

		// Unit test.
		getConnectivityTest();

		breadthFirstTraversalTest();

		depthFirstTraversalTest();

		coloringTest();
	}// Of main

第 36 天: 邻连表

  1. 相当于图的压缩存储. 每一行数据用一个单链表存储.
  2. 重写了广度优先遍历. 可以发现, 使用队列的机制不变. 仅仅是把其中的 for 循环换成了 while, 避免检查不存在的边. 如果图很稀疏的话, 可以降低时间复杂度.
  3. 深度优先遍历可以当作作业, 这里不写了.
package datastructure.graph;

import datastructure.queue.CircleObjectQueue;

/**
 * Adjacency list for directed graph.
 * 
 * @author Fan Min [email protected].
 */
public class AdjacencyList {

	/**
	 * An inner class for adjacent node.
	 */
	class AdjacencyNode {
		/**
		 * The column index.
		 */
		int column;

		/**
		 * The next adjacent node.
		 */
		AdjacencyNode next;

		/**
		 *********************
		 * The first constructor.
		 * 
		 * @param paraColumn
		 *            The column.
		 *********************
		 */
		public AdjacencyNode(int paraColumn) {
			column = paraColumn;
			next = null;
		}// Of AdjacencyNode
	}// Of class AdjacencyNode

	/**
	 * The number of nodes. This member variable may be redundant since it is
	 * always equal to headers.length.
	 */
	int numNodes;

	/**
	 * The headers for each row.
	 */
	AdjacencyNode[] headers;

	/**
	 *********************
	 * The first constructor.
	 * 
	 * @param paraMatrix
	 *            The the matrix indicating the graph.
	 *********************
	 */
	public AdjacencyList(int[][] paraMatrix) {
		numNodes = paraMatrix.length;

		// Step 1. Initialize. The data in the headers are not meaningful.
		AdjacencyNode tempPreviousNode, tempNode;

		headers = new AdjacencyNode[numNodes];
		for (int i = 0; i < numNodes; i++) {
			headers[i] = new AdjacencyNode(-1);
			tempPreviousNode = headers[i];
			for (int j = 0; j < numNodes; j++) {
				if (paraMatrix[i][j] == 0) {
					continue;
				} // Of if

				// Create a new node.
				tempNode = new AdjacencyNode(j);

				// Link.
				tempPreviousNode.next = tempNode;
				tempPreviousNode = tempNode;
			} // Of for j
		} // Of for i
	}// Of class AdjacentTable

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		String resultString = "";

		AdjacencyNode tempNode;
		for (int i = 0; i < numNodes; i++) {
			tempNode = headers[i].next;

			while (tempNode != null) {
				resultString += " (" + i + ", " + tempNode.column + ")";
				tempNode = tempNode.next;
			} // Of while
			resultString += "\r\n";
		} // Of for i

		return resultString;
	}// Of toString

	/**
	 *********************
	 * Breadth first traversal.
	 * 
	 * @param paraStartIndex
	 *            The start index.
	 * @return The sequence of the visit.
	 *********************
	 */
	public String breadthFirstTraversal(int paraStartIndex) {
		CircleObjectQueue tempQueue = new CircleObjectQueue();
		String resultString = "";

		boolean[] tempVisitedArray = new boolean[numNodes];

		tempVisitedArray[paraStartIndex] = true;

		// Initialize the queue.
		// Visit before enqueue.
		tempVisitedArray[paraStartIndex] = true;
		resultString += paraStartIndex;
		tempQueue.enqueue(new Integer(paraStartIndex));

		// Now visit the rest of the graph.
		int tempIndex;
		Integer tempInteger = (Integer) tempQueue.dequeue();
		AdjacencyNode tempNode;
		while (tempInteger != null) {
			tempIndex = tempInteger.intValue();

			// Enqueue all its unvisited neighbors. The neighbors are linked
			// already.
			tempNode = headers[tempIndex].next;
			while (tempNode != null) {
				if (!tempVisitedArray[tempNode.column]) {
					// Visit before enqueue.
					tempVisitedArray[tempNode.column] = true;
					resultString += tempNode.column;
					tempQueue.enqueue(new Integer(tempNode.column));
				} // Of if
				tempNode = tempNode.next;
			} // Of for i

			// Take out one from the head.
			tempInteger = (Integer) tempQueue.dequeue();
		} // Of while

		return resultString;
	}// Of breadthFirstTraversal

	/**
	 *********************
	 * Unit test for breadthFirstTraversal. The same as the one in class Graph.
	 *********************
	 */
	public static void breadthFirstTraversalTest() {
		// Test an undirected graph.
		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);
		AdjacencyList tempAdjList = new AdjacencyList(tempMatrix);
 
		String tempSequence = "";
		try {
			tempSequence = tempAdjList.breadthFirstTraversal(2);
		} catch (Exception ee) {
			System.out.println(ee);
		} // Of try.
 
		System.out.println("The breadth first order of visit: " + tempSequence);
	}// Of breadthFirstTraversalTest
	
	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args
	 *            Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		int[][] tempMatrix = { { 0, 1, 0 }, { 1, 0, 1 }, { 0, 1, 0 } };
		AdjacencyList tempTable = new AdjacencyList(tempMatrix);
		System.out.println("The data are:\r\n" + tempTable);

		breadthFirstTraversalTest();
	}// Of main

}// Of class AdjacencyList

第 37 天: 十字链表

  1. 比邻接表难一点点, 毕竟多一个指针域.
  2. 为控制代码量, 只做了建表和 toString.
package datastructure.graph;

/**
 * Orthogonal List for directed graph.
 * 
 * @author Fan Min [email protected].
 */
public class OrthogonalList {

	/**
	 * An inner class for adjacent node.
	 */
	class OrthogonalNode {
		/**
		 * The row index.
		 */
		int row;

		/**
		 * The column index.
		 */
		int column;

		/**
		 * The next out node.
		 */
		OrthogonalNode nextOut;

		/**
		 * The next in node.
		 */
		OrthogonalNode nextIn;

		/**
		 *********************
		 * The first constructor.
		 * 
		 * @param paraRow    The row.
		 * @param paraColumn The column.
		 *********************
		 */
		public OrthogonalNode(int paraRow, int paraColumn) {
			row = paraRow;
			column = paraColumn;
			nextOut = null;
			nextIn = null;
		}// Of OrthogonalNode
	}// Of class OrthogonalNode

	/**
	 * The number of nodes. This member variable may be redundant since it is always
	 * equal to headers.length.
	 */
	int numNodes;

	/**
	 * The headers for each row.
	 */
	OrthogonalNode[] headers;

	/**
	 *********************
	 * The first constructor.
	 * 
	 * @param paraMatrix The matrix indicating the graph.
	 *********************
	 */
	public OrthogonalList(int[][] paraMatrix) {
		numNodes = paraMatrix.length;

		// Step 1. Initialize. The data in the headers are not meaningful.
		OrthogonalNode tempPreviousNode, tempNode;

		headers = new OrthogonalNode[numNodes];

		// Step 2. Link to its out nodes.
		for (int i = 0; i < numNodes; i++) {
			headers[i] = new OrthogonalNode(i, -1);
			tempPreviousNode = headers[i];
			for (int j = 0; j < numNodes; j++) {
				if (paraMatrix[i][j] == 0) {
					continue;
				} // Of if

				// Create a new node.
				tempNode = new OrthogonalNode(i, j);

				// Link.
				tempPreviousNode.nextOut = tempNode;
				tempPreviousNode = tempNode;
			} // Of for j
		} // Of for i

		// Step 3. Link to its in nodes. This step is harder.
		OrthogonalNode[] tempColumnNodes = new OrthogonalNode[numNodes];
		for (int i = 0; i < numNodes; i++) {
			tempColumnNodes[i] = headers[i];
		} // Of for i

		for (int i = 0; i < numNodes; i++) {
			tempNode = headers[i].nextOut;
			while (tempNode != null) {
				tempColumnNodes[tempNode.column].nextIn = tempNode;
				tempColumnNodes[tempNode.column] = tempNode;

				tempNode = tempNode.nextOut;
			} // Of while
		} // Of for i
	}// Of the constructor

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		String resultString = "Out arcs: ";

		OrthogonalNode tempNode;
		for (int i = 0; i < numNodes; i++) {
			tempNode = headers[i].nextOut;

			while (tempNode != null) {
				resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
				tempNode = tempNode.nextOut;
			} // Of while
			resultString += "\r\n";
		} // Of for i

		resultString += "\r\nIn arcs: ";

		for (int i = 0; i < numNodes; i++) {
			tempNode = headers[i].nextIn;

			while (tempNode != null) {
				resultString += " (" + tempNode.row + ", " + tempNode.column + ")";
				tempNode = tempNode.nextIn;
			} // Of while
			resultString += "\r\n";
		} // Of for i

		return resultString;
	}// Of toString

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	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);
	}// Of main
}// Of class OrthogonalList

第 38 天: Dijkstra 算法与 Prim 算法

  1. 又需要画个几个图, 换几个例子.
  2. Dijkstra 算法需要有向图, Prim 算法需要无向图. 代码中也需要更换后者.
  3. 两个算法的结构相同. 不同之处是比较距离的时候, 是用累计的 (Dijkstra) 还是当前边的 (Prim). 建议先写 Dijkstra, 然后拷贝、修改变成 Prim. 到这个阶段, 应该已经具备这样的能力.
package datastructure.graph;

import java.util.Arrays;

import matrix.IntMatrix;

/**
 * Weighted graphs are called nets.
 * 
 * @author Fan Min [email protected].
 */
public class Net {

	/**
	 * The maximal distance. Do not use Integer.MAX_VALUE.
	 */
	public static final int MAX_DISTANCE = 10000;

	/**
	 * The number of nodes.
	 */
	int numNodes;

	/**
	 * The weight matrix. We use int to represent weight for simplicity.
	 */
	IntMatrix weightMatrix;

	/**
	 *********************
	 * The first constructor.
	 * 
	 * @param paraNumNodes
	 *            The number of nodes in the graph.
	 *********************
	 */
	public Net(int paraNumNodes) {
		numNodes = paraNumNodes;
		weightMatrix = new IntMatrix(numNodes, numNodes);
		for (int i = 0; i < numNodes; i++) {
			// For better readability, you may need to write fill() in class
			// IntMatrix.
			Arrays.fill(weightMatrix.getData()[i], MAX_DISTANCE);
		} // Of for i
	}// Of the first constructor

	/**
	 *********************
	 * The second constructor.
	 * 
	 * @param paraMatrix
	 *            The data matrix.
	 *********************
	 */
	public Net(int[][] paraMatrix) {
		weightMatrix = new IntMatrix(paraMatrix);
		numNodes = weightMatrix.getRows();
	}// Of the second constructor

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		String resultString = "This is the weight matrix of the graph.\r\n" + weightMatrix;
		return resultString;
	}// Of toString

	/**
	 *********************
	 * The Dijkstra algorithm: shortest path from the source to all nodes.
	 * 
	 * @param paraSource
	 *            The source node.
	 * @return The distances to all nodes.
	 *********************
	 */
	public int[] dijkstra(int paraSource) {
		// Step 1. Initialize.
		int[] tempDistanceArray = new int[numNodes];
		for (int i = 0; i < numNodes; i++) {
			tempDistanceArray[i] = weightMatrix.getValue(paraSource, i);
		} // Of for i

		int[] tempParentArray = new int[numNodes];
		Arrays.fill(tempParentArray, paraSource);
		// -1 for no parent.
		tempParentArray[paraSource] = -1;

		// Visited nodes will not be considered further.
		boolean[] tempVisitedArray = new boolean[numNodes];
		tempVisitedArray[paraSource] = true;

		// Step 2. Main loops.
		int tempMinDistance;
		int tempBestNode = -1;
		for (int i = 0; i < numNodes - 1; i++) {
			// Step 2.1 Find out the best next node.
			tempMinDistance = Integer.MAX_VALUE;
			for (int j = 0; j < numNodes; j++) {
				// This node is visited.
				if (tempVisitedArray[j]) {
					continue;
				} // Of if

				if (tempMinDistance > tempDistanceArray[j]) {
					tempMinDistance = tempDistanceArray[j];
					tempBestNode = j;
				} // Of if
			} // Of for j

			tempVisitedArray[tempBestNode] = true;

			// Step 2.2 Prepare for the next round.
			for (int j = 0; j < numNodes; j++) {
				// This node is visited.
				if (tempVisitedArray[j]) {
					continue;
				} // Of if

				// This node cannot be reached.
				if (weightMatrix.getValue(tempBestNode, j) >= MAX_DISTANCE) {
					continue;
				} // Of if

				if (tempDistanceArray[j] > tempDistanceArray[tempBestNode]
						+ weightMatrix.getValue(tempBestNode, j)) {
					// Change the distance.
					tempDistanceArray[j] = tempDistanceArray[tempBestNode]
							+ weightMatrix.getValue(tempBestNode, j);
					// Change the parent.
					tempParentArray[j] = tempBestNode;
				} // Of if
			} // Of for j

			// For test
			System.out.println("The distance to each node: " + Arrays.toString(tempDistanceArray));
			System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
		} // Of for i

		// Step 3. Output for debug.
		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;
	}// Of dijkstra

	/**
	 *********************
	 * The minimal spanning tree.
	 * 
	 * @return The total cost of the tree.
	 *********************
	 */
	public int prim() {
		// Step 1. Initialize.
		// Any node can be the source.
		int tempSource = 0;
		int[] tempDistanceArray = new int[numNodes];
		for (int i = 0; i < numNodes; i++) {
			tempDistanceArray[i] = weightMatrix.getValue(tempSource, i);
		} // Of for i

		int[] tempParentArray = new int[numNodes];
		Arrays.fill(tempParentArray, tempSource);
		// -1 for no parent.
		tempParentArray[tempSource] = -1;

		// Visited nodes will not be considered further.
		boolean[] tempVisitedArray = new boolean[numNodes];
		tempVisitedArray[tempSource] = true;

		// Step 2. Main loops.
		int tempMinDistance;
		int tempBestNode = -1;
		for (int i = 0; i < numNodes - 1; i++) {
			// Step 2.1 Find out the best next node.
			tempMinDistance = Integer.MAX_VALUE;
			for (int j = 0; j < numNodes; j++) {
				// This node is visited.
				if (tempVisitedArray[j]) {
					continue;
				} // Of if

				if (tempMinDistance > tempDistanceArray[j]) {
					tempMinDistance = tempDistanceArray[j];
					tempBestNode = j;
				} // Of if
			} // Of for j

			tempVisitedArray[tempBestNode] = true;

			// Step 2.2 Prepare for the next round.
			for (int j = 0; j < numNodes; j++) {
				// This node is visited.
				if (tempVisitedArray[j]) {
					continue;
				} // Of if

				// This node cannot be reached.
				if (weightMatrix.getValue(tempBestNode, j) >= MAX_DISTANCE) {
					continue;
				} // Of if

				// Attention: the difference from the Dijkstra algorithm.
				if (tempDistanceArray[j] > weightMatrix.getValue(tempBestNode, j)) {
					// Change the distance.
					tempDistanceArray[j] = weightMatrix.getValue(tempBestNode, j);
					// Change the parent.
					tempParentArray[j] = tempBestNode;
				} // Of if
			} // Of for j

			// For test
			System.out.println(
					"The selected distance for each node: " + Arrays.toString(tempDistanceArray));
			System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
		} // Of for i

		int resultCost = 0;
		for (int i = 0; i < numNodes; i++) {
			resultCost += tempDistanceArray[i];
		} // Of for i

		// Step 3. Output for debug.
		System.out.println("Finally");
		System.out.println("The parent of each node: " + Arrays.toString(tempParentArray));
		System.out.println("The total cost: " + resultCost);

		return resultCost;
	}// Of prim

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args
	 *            Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		Net tempNet0 = new Net(3);
		System.out.println(tempNet0);

		int[][] tempMatrix1 = { { 0, 9, 3, 6 }, { 5, 0, 2, 4 }, { 3, 2, 0, 1 }, { 2, 8, 7, 0 } };
		Net tempNet1 = new Net(tempMatrix1);
		System.out.println(tempNet1);

		// Dijkstra
		tempNet1.dijkstra(1);

		// An undirected net is required.
		int[][] tempMatrix2 = { { 0, 7, MAX_DISTANCE, 5, MAX_DISTANCE }, { 7, 0, 8, 9, 7 },
				{ MAX_DISTANCE, 8, 0, MAX_DISTANCE, 5 }, { 5, 9, MAX_DISTANCE, 0, 15 },
				{ MAX_DISTANCE, 7, 5, 15, 0 } };
		Net tempNet2 = new Net(tempMatrix2);
		tempNet2.prim();
	}// Of main
}// Of class Net

第 39 天: 关键路径

  1. 拓扑排序是关键路径的一部分.
  2. 关键路径长度, 其实是最远路径长度. 然而, 它并非最短路径的对偶问题. 我尝试修改 Dijkstra 算法来解决, 然后发现自己傻了.
  3. 正向算每个节点的最早开始时间, 逆向算每个节点的最晚开始时间, 设计太牛了.
  4. 最晚开始时间的初始化容易弄错, 经典算法果然是不好对付的.
  5. 注释很多, 因为我自己花了两小时才弄明白. 大意了啊.
	/**
	 *********************
	 * Critical path. Net validity checks such as loop check not implemented.
	 * The source should be 0 and the destination should be n-1.
	 *
	 * @return The node sequence of the path.
	 *********************
	 */
	public boolean[] criticalPath() {
		// One more value to save simple computation.
		int tempValue;

		// Step 1. The in-degree of each node.
		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]++;
				} // Of if
			} // Of for j
		} // Of for i
		System.out.println("In-degree of nodes: " + Arrays.toString(tempInDegrees));

		// Step 2. Topology sorting.
		int[] tempEarliestTimeArray = new int[numNodes];
		for (int i = 0; i < numNodes; i++) {
			// This node cannot be removed.
			if (tempInDegrees[i] > 0) {
				continue;
			} // Of if

			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;
					} // Of if
					tempInDegrees[j]--;
				} // Of if
			} // Of for j
		} // Of for i

		System.out.println("Earlest start time: " + Arrays.toString(tempEarliestTimeArray));

		// Step 3. The out-degree of each node.
		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]++;
				} // Of if
			} // Of for j
		} // Of for i
		System.out.println("Out-degree of nodes: " + Arrays.toString(tempOutDegrees));

		// Step 4. Reverse topology sorting.
		int[] tempLatestTimeArray = new int[numNodes];
		for (int i = 0; i < numNodes; i++) {
			tempLatestTimeArray[i] = tempEarliestTimeArray[numNodes - 1];
		} // Of for i

		for (int i = numNodes - 1; i >= 0; i--) {
			// This node cannot be removed.
			if (tempOutDegrees[i] > 0) {
				continue;
			} // Of if

			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;
					} // Of if
					tempOutDegrees[j]--;
					System.out.println("The out-degree of " + j + " decreases by 1.");
				} // Of if
			} // Of for j
		} // Of for i

		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;
			} // Of if
		} // Of for i

		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);
			} // Of if
		} // Of for i
		System.out.println();

		return resultCriticalArray;
	}// Of criticalPath

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args
	 *            Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		Net tempNet0 = new Net(3);
		System.out.println(tempNet0);

		int[][] tempMatrix1 = { { 0, 9, 3, 6 }, { 5, 0, 2, 4 }, { 3, 2, 0, 1 }, { 2, 8, 7, 0 } };
		Net tempNet1 = new Net(tempMatrix1);
		System.out.println(tempNet1);

		// Dijkstra
		tempNet1.dijkstra(1);

		// An undirected net is required.
		int[][] tempMatrix2 = { { 0, 7, MAX_DISTANCE, 5, MAX_DISTANCE }, { 7, 0, 8, 9, 7 },
				{ MAX_DISTANCE, 8, 0, MAX_DISTANCE, 5 }, { 5, 9, MAX_DISTANCE, 0, 15, },
				{ MAX_DISTANCE, 7, 5, 15, 0 } };
		Net tempNet2 = new Net(tempMatrix2);
		tempNet2.prim();

		// A directed net without loop is required.
		// Node cannot reach itself. It is indicated by -1.
		int[][] tempMatrix3 = { { -1, 3, 2, -1, -1, -1 }, { -1, -1, -1, 2, 3, -1 },
				{ -1, -1, -1, 4, -1, 3 }, { -1, -1, -1, -1, -1, 2 }, { -1, -1, -1, -1, -1, 1 },
				{ -1, -1, -1, -1, -1, -1 } };

		Net tempNet3 = new Net(tempMatrix3);
		System.out.println("-------critical path");
		tempNet3.criticalPath();
	}// Of main

第 40 天: 小结

描述这 10 天的学习体会, 不少于 10 条.

你可能感兴趣的:(Java,程序设计基础,数据结构)