十字链表简介与实现(Java)
- 结构
- 实现
结构
十字链表存储有向图(网)的方式与邻接表有一些相同,都以图(网)中各顶点为首元节点建立多条链表,同时为了便于管理,还将所有链表的首元节点存储到同一数组(或链表)中。
其中,建立个各个链表中用于存储顶点的首元节点结构如图 1 所示:
十字链表中首元节点结构示意图
从图 1 可以看出,首元节点中有一个数据域和两个指针域(分别用 firstin 和 firstout 表示):
firstin 指针用于连接以当前顶点为弧头的其他顶点构成的链表;
firstout 指针用于连接以当前顶点为弧尾的其他顶点构成的链表;
data 用于存储该顶点中的数据;
由此可以看出,十字链表实质上就是为每个顶点建立两个链表,分别存储以该顶点为弧头的所有顶点和以该顶点为弧尾的所有顶点。
注意,存储图的十字链表中,各链表中首元节点与其他节点的结构并不相同,图 1 所示仅是十字链表中首元节点的结构,链表中其他普通节点的结构如图 2 所示:
十字链表中普通节点的结构示意图
从图 2 中可以看出,十字链表中普通节点的存储分为 5 部分内容,它们各自的作用是:
tailvex 用于存储以首元节点为弧尾的顶点位于数组中的位置下标;
headvex 用于存储以首元节点为弧头的顶点位于数组中的位置下标;
hlink 指针:用于链接下一个存储以首元节点为弧头的顶点的节点;
tlink 指针:用于链接下一个存储以首元节点为弧尾的顶点的节点;
info 指针:用于存储与该顶点相关的信息,例如量顶点之间的权值;
比如说,用十字链表存储图 3a) 中的有向图,存储状态如图 3b) 所示:
十字链表存储有向图示意图
拿图 3 中的顶点 V1 来说,通过构建好的十字链表得知,以该顶点为弧头的顶点只有存储在数组中第 3 位置的 V4(因此该顶点的入度为 1),而以该顶点为弧尾的顶点有两个,分别为存储数组第 1 位置的 V2 和第 2 位置的 V3(因此该顶点的出度为 2)。
对于图 3 各个链表中节点来说,由于表示的都是该顶点的出度或者入度,因此没有先后次序之分。
实现
package test;
public class OListDG {int vlen; // 顶点个数int elen; // 边个数VertexNode[] vertexNodeList; // 顶点数组EdgeNode edgeNode;/*** 构造函数* @param vexs* @param edges*/public OListDG(char[] vexs, char[][] edges) {vlen = vexs.length;elen = edges.length;// 初始化顶点,建立顶点表vertexNodeList = new VertexNode[vlen];for (int i = 0; i < vlen; i++) {vertexNodeList[i] = new VertexNode();vertexNodeList[i].vertex = vexs[i];vertexNodeList[i].firstIn = null;vertexNodeList[i].firstOut = null;}// 初始化边,利用头插法建立十字链表for (int i = 0; i < elen; i++) {EdgeNode edgeNode_1 = new EdgeNode();EdgeNode edgeNode_2 = new EdgeNode();int vi = getPosition(edges[i][0], vexs);int vj = getPosition(edges[i][1], vexs);edgeNode_1.tailvex = vi;edgeNode_1.headvex = vj;edgeNode_1.taillink = vertexNodeList[vi].firstOut;vertexNodeList[vi].firstOut = edgeNode_1;edgeNode_2.tailvex = vi;edgeNode_2.headvex = vj;edgeNode_2.headlink = vertexNodeList[vj].firstIn;vertexNodeList[vj].firstIn = edgeNode_2;}}/*** 功能:顶点表结点结构* 参数:vertex --> 顶点域,存储顶点信息* firstIn --> 入边表头指针,指向该顶点的入边表中第一个结点* firstOut --> 出边表头指针,指向该顶点的出边表中第一个结点*/private class VertexNode {char vertex; EdgeNode firstIn;EdgeNode firstOut; }/*** 功能:边表结点* 参数:tailvex --> 弧起点在顶点表的下标* headvex --> 弧终点在顶点表的下标* headlink --> 入边表指针域,指向终点相同的下一条边* taillink --> 边表指针域,指向起点相同的下一条边*/private class EdgeNode {int tailvex;int headvex;EdgeNode headlink;EdgeNode taillink;}/*** 功能:返回ch位置*/private int getPosition(char ch, char[] vexs) {for (int i = 0; i < vlen; i++)if (vexs[i] == ch)return i;return -1;}/*** 功能:打印邻接表和逆邻接表*/public void print() {System.out.printf("AdjList:\n");for (int i = 0; i < vlen; i++) {System.out.print(vertexNodeList[i].vertex + "-->");if (vertexNodeList[i].firstOut != null) {EdgeNode mEdgeNode = new EdgeNode();mEdgeNode = vertexNodeList[i].firstOut;System.out.print(mEdgeNode.headvex);while (mEdgeNode.taillink != null) {mEdgeNode = mEdgeNode.taillink;System.out.print(mEdgeNode.headvex);}System.out.print("\n");} else {System.out.print("\n");}}System.out.print("----------\n");System.out.printf("InvAdjList:\n");for (int i = 0; i < vlen; i++) {System.out.print(vertexNodeList[i].vertex + "<--");if (vertexNodeList[i].firstIn != null) {EdgeNode mEdgeNode = new EdgeNode();mEdgeNode = vertexNodeList[i].firstIn;System.out.print(mEdgeNode.tailvex);while (mEdgeNode.headlink != null) {mEdgeNode = mEdgeNode.headlink;System.out.print(mEdgeNode.tailvex);}System.out.print("\n");} else {System.out.print("\n");}}}/*** 主函数*/public static void main(String args[]) {// 顶点数组char[] vexs = {'A', 'B', 'C', 'D'};// 边数组char[][] edges = new char[][] {{'A', 'B'}, {'A', 'C'}, {'A', 'D'}, {'B', 'D'}, {'C', 'D'}};OListDG listUDG = new OListDG(vexs, edges);listUDG.print();}
}