图比树有更复杂的数据关系。数据间是多对多的关系,有多个前驱,多个后继。对于有向图,图的邻接表,只能表示顶点的出边邻接点,即所有以表头顶点为边尾的顶点,求顶点的出度很方便,计算链表里除表头外有几个节点即可。但计算顶点的入度就很复杂。例如有A----->B,为了求顶点B的入度,找到A---->B这条边,只能逆向查找,在顶点A的出边邻接链表里,看是否有邻接点为B,遍历表头数组,即可得到顶点B的入度。
所以就有了图的十字链表储存方式,一个表头带两个链表,一个出边邻接点链表,一个入边邻接点链表。
很多讲解都到了这一步,如下图所示示意图。
对于链表节点具体的建立和连接过程,这里补充下文字描述。本程序里,建立图所需要的顶点信息由main函数里的顶点数组提供,图里边的信息,由main函数里的一个二维数组提供。二维数组的横坐标表示边尾,竖坐标表示边头。二维数组的元素值表示边对应的权。由这俩数组建立图的十字链表。
建立思路是:先横向一行一行,从上到下,扫描二维数组,建立顶点的出边邻接点链表;再竖向一列一列的,从左到右扫描整个二维数组,建立顶点的入边邻接点链表。每一行元素对应的边,都有共同的边尾顶点,组成边尾顶点的出边邻接表,每一列元素对应的边,都有共同的边头顶点,组成边头顶点的入边邻接链表。十字链表里除表头外的每一个节点,对应着一条边,既存储了边头顶点在顶点数组里的下标(借此下标在顶点数组里找到该顶点),也存储了边尾顶点在顶点数组里的下标;还存储了这条边对应的权。该节点被表头数组里的边尾顶点所指向,出现在其出边邻接表里;该节点也被顶点数组里的边头节点所指向,出现在其入边邻接链表里。程序也是以这几句话概括的认识分析思路编写的。
横向扫描数组,完善十字链表节点的权成员(如果有权值的话),出边边头顶点在顶点数组的下标成员,指向下一个出边边头节点的指针成员。每一条边都有边尾和边头,所以扫描完二维数组后,每一条边都会在顶点的出边邻接链表里出现一次。节点数目和边的数目是相同的。然后我们不再生成节点,而是换种遍历数组的方式,完善节点的另两个成员:入边边尾顶点在顶点数组里的下标成员和指向下一个边尾顶点的指针成员。竖向一列一列的,从左到右扫描整个二维数组,该列里出现的所有的边,都以竖坐标对应的顶点为边头,以各横坐标对应的顶点为边尾。在各边尾的出边邻接表里,找到与二维数组元素对应的那个节点,把该节点也纳入到纵坐标对应顶点的入边邻接表里,该节点的边尾顶点下标成员即为二维数组元素的横坐标,指向包含下一个边尾顶点的节点的指针成员以尾插法建立链表的方式赋值。
函数createGraphOrthogonalList建立十字链表。
函数idAndod显示图里各顶点的出度与入度,也是检验建立十字链表的函数是否正确。
完整代码如下(main函数所在源文件):
#include<iostream>
#include<stdio.h>
using namespace std;
#define MAXVERTEX 15
#define INFINI 65555
struct OrthogonalListNode {
int indexEdgeHead;
int indexEdgeTail;
OrthogonalListNode* ptEdgeHead;
OrthogonalListNode* ptEdgeTail;
int weight;
};
struct OrthogonalListHead {
char vertex;
OrthogonalListNode* ptEdgeHead = NULL;
OrthogonalListNode* ptEdgeTail = NULL;
};
struct GraphOrthogonalList {
OrthogonalListHead listHead[MAXVERTEX];
int numVertexes;
int numEdges;
};
extern void createGraphOrthogonalList(GraphOrthogonalList &graphOrthoList,
int numVertexes, int numEdges, int edges[][6], char vertexes[]);
extern void idAndod(GraphOrthogonalList &graphOrthoList);
int main() {
int numVertexes = 6, numEdges = 10;
int edges[][6] = { {0,5,INFINI,7,INFINI,INFINI},
{INFINI,0,4,INFINI,INFINI,INFINI},
{8,INFINI,0,INFINI,INFINI,9},
{INFINI,INFINI,5,0,INFINI,6},
{INFINI,INFINI,INFINI,5,0,INFINI},
{3,INFINI,INFINI,INFINI,1,0} };
char vertexes[] = { '0','1','2','3','4','5'};
GraphOrthogonalList graphOrthoList;
createGraphOrthogonalList(graphOrthoList,numVertexes,numEdges,edges,vertexes);
idAndod(graphOrthoList);
return 0;
}
被调用各函数所在源文件:
#include<iostream>
#include<stdio.h>
using namespace std;
#define MAXVERTEX 15
#define INFINI 65555 //尽量大的一个数,表示俩顶点之间没有边
struct OrthogonalListNode {
int indexEdgeHead;
int indexEdgeTail;
OrthogonalListNode* ptEdgeHead;
OrthogonalListNode* ptEdgeTail;
int weight;
};
struct OrthogonalListHead {
char vertex;
OrthogonalListNode* ptEdgeHead = NULL;
OrthogonalListNode* ptEdgeTail = NULL;
};
struct GraphOrthogonalList {
OrthogonalListHead listHead[MAXVERTEX];
int numVertexes;
int numEdges;
};
void createGraphOrthogonalList(GraphOrthogonalList &graphOrthoList,
int numVertexes, int numEdges, int edges[][6], char vertexes[]) {
graphOrthoList.numVertexes = numVertexes;
graphOrthoList.numEdges = numEdges;
int row, column;
OrthogonalListNode* ptTail = NULL, * ptNew;
for (row = 0; row < numVertexes; row++)
graphOrthoList.listHead[row].vertex = vertexes[row];
for(row = 0 ; row < numVertexes ; row++)//先建立顶点的边头链表,即邻接链表
for(column = 0 ; column < numVertexes ; column++)
if (row != column && edges[row][column] != INFINI) {
ptNew = new OrthogonalListNode;
ptNew->weight = edges[row][column];
ptNew->indexEdgeHead = column;
if (graphOrthoList.listHead[row].ptEdgeHead == NULL) {
ptNew->ptEdgeHead = NULL;
graphOrthoList.listHead[row].ptEdgeHead = ptNew;
ptTail = ptNew;
}
else {
ptNew->ptEdgeHead = ptTail->ptEdgeHead;
ptTail->ptEdgeHead = ptNew;
ptTail = ptNew;
}
}
for(column = 0 ; column < numVertexes ; column++)
for (row = 0; row < numVertexes; row++) //尾插法建立十字链表的两个链
if (column != row && edges[row][column] != INFINI) {
ptNew = graphOrthoList.listHead[row].ptEdgeHead;
while (ptNew->indexEdgeHead != column)
ptNew = ptNew->ptEdgeHead;
if (graphOrthoList.listHead[column].ptEdgeTail == NULL) {
ptNew->ptEdgeTail = NULL;
graphOrthoList.listHead[column].ptEdgeTail = ptNew;
ptTail = ptNew;
}
else {
ptNew->ptEdgeTail = ptTail->ptEdgeTail;
ptTail->ptEdgeTail = ptNew;
ptTail = ptNew;
}
}
}
void idAndod(GraphOrthogonalList &graphOrthoList) {
int inDegree, outDegree;
OrthogonalListNode* ptEdgeHead, * ptEdgeTail;
for (int i = 0; i < graphOrthoList.numVertexes; i++) {
cout << "vertex " << graphOrthoList.listHead[i].vertex << " : ";
outDegree = 0;
ptEdgeHead = graphOrthoList.listHead[i].ptEdgeHead;
while (ptEdgeHead != NULL) {
outDegree++;
ptEdgeHead = ptEdgeHead->ptEdgeHead;
}
inDegree = 0;
ptEdgeTail = graphOrthoList.listHead[i].ptEdgeTail;
while (ptEdgeTail != NULL) {
inDegree++;
ptEdgeTail = ptEdgeTail->ptEdgeTail;
}
cout << "入度 :" << inDegree << " ; 出度 : " << outDegree<<endl;
}
}
测试结果与对应的图如下:
谢谢阅读。