对于有向图来说,邻接表是有缺陷的。关心了出度问题,要获取入度就必须要遍历整个图才能知道。在这里我们将要介绍十字链表。
1.知识点讲解
我们需要重新定义表结点结构如下图:
其中 firstin 表示边表头指针,指向该顶点的入边表中第一个结点,firstout 表示出边表的表头指针,指向该顶点的出边表中的第一个结点。
重新定义边表结点如下图:
其中 tailvex 是指弧起点在顶点表的下标,headvex 是指弧终点在顶点表中的下标,headlink 是指入边表指针域,指向终点相同的下一条边,taillink 是边表指针域,指向起点相同的下一条边。如果是网,可以再加一个 weight 域来存储权值。
如下图 A,顶点依然存入一个数组{V0,V1,V2,V3}。以顶点 V0 来说,firstout 指向出边表中的第一个结点 V3。所以 V0 边结点 的 headvex = 3,而 tailvex 其实就是当前结点 V0 的下标0,由于 V0 只有一个出边顶点,所以 headlink 和 taillink 都是空。
我们重点讲解一下蓝色箭头的含义,他其实此图的逆邻接表的表示。对于 V0 来说,它有两个顶点V1 和 V2 的入边。因此 V0 的 firstin 指向顶点 V1 的边表结点中 headvex 为 0 的结点,就是①。接着由入边结点的 headlink 指向下一个入边顶点 V2,如②。对于 V1,有一个入边顶点 V2,所以它的 firstin 指向 V2 的边表结点中 headvex 为1的结点,如图中的③。顶点 V2 和 V3 也是同样有一个入边顶点,④和⑤。
十字链表的好处就是把邻接表和逆邻接表整合在了一起,这样既容易找到以 Vi 为尾的弧,也容易找到以 Vi 为头的弧,因而容易求得顶点的入度和出度。它除了复杂一点外,创建图算法的时间复杂度是和邻接表相同的,因此在有向图的应用中,十字链表是非常好的数据结构模型。
2.代码实现
这是测试图:
代码:
/**
*
*/
package datastructure.graph;
/**
****************************
* TODO
* @author Chen Fan
* @version 1.0
* time 2022年1月23日
****************************
*/
public class OrthogonalList {
// 内部结点
class OrthogonalNode{
// 行索引
int row;
// 列索引
int column;
// 下一个出结点
OrthogonalNode nextOutNode;
// 下一个入结点
OrthogonalNode nextInNode;
// 构造方法
public OrthogonalNode(int paraRow, int paraColumn) {
row = paraRow;
column = paraColumn;
nextInNode = null;
nextOutNode = null;
} // Of OrthogonalNode
} // Of class OrthogonalNode
// 结点的数目,可能是多余的,因为结点数始终等于headers.length
int numNodes;
// 每一行的表头
OrthogonalNode[] headers;
// 十字链表的构造方法
public OrthogonalList(int[][] paraMatrix) {
numNodes = paraMatrix.length;
// 第一步:初始化十字链表,表头的数据域没有意义
OrthogonalNode tempPreViousNode,tempNode;
headers = new OrthogonalNode[numNodes];
// 第二步:链接外部结点
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
// 创建一个新结点
tempNode = new OrthogonalNode(i, j);
// link
tempPreViousNode.nextOutNode = tempNode;
tempPreViousNode = tempNode;
} // Of for j
} // Of for i
// 第三步:链接入结点,这一步更难
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].nextOutNode; // 表头第一个出结点
while(tempNode != null) {
// tempNode.column 列索引,就是回到入结点的位置,给它的入结点设置引用tempNode
tempColumnNodes[tempNode.column].nextInNode = tempNode;
tempColumnNodes[tempNode.column] = tempNode;
tempNode = tempNode.nextOutNode;
} // Of while
} // Of for i
} // Of constructor
public String toString() {
String resultString = "Out arce: \n";
OrthogonalNode tempNode;
for (int i = 0; i < numNodes; i++) {
tempNode = headers[i].nextOutNode;
while(tempNode != null) {
resultString +="(" + tempNode.row + "," + tempNode.column +")";
tempNode = tempNode.nextOutNode;
} // Of while
resultString += "\r\n";
} // Of for i
resultString += "\r\n In arcs: \n";
for (int i = 0; i < numNodes; i++) {
tempNode = headers[i].nextInNode;
while(tempNode != null) {
resultString += " (" + tempNode.row + "," + tempNode.column + ")";
tempNode = tempNode.nextInNode;
} // Of while
resultString += "\r\n";
}// Of for i
return resultString;
} // Of toString
// 测试程序入口
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
}
代码需要讲解的在评论区留言哦!