十字链表(Orthogonal List)是有向图的另一种链式存储结构,可以看成是将有向图的邻接表和逆邻接表相结合起来得到的一种链表。
就是在邻接表每个头节点加上入度链表
分析有向图的邻接表表示法存在的问题:
虽然邻接表表示可以方便计算某个节点的出度,但不方便计算它的入度 可以方便找到所有从该某个点出发的边,但不方便找到所有指向某个顶点的边
为了解决这样的问题,对邻接表做一些改进,对每一个顶点,增加了一条链表,用于存储指向该顶点的所有边
所以每个顶点都对应着2个链表,一个链表记录所有从该顶点出发的边,另一个链表记录所有指向该顶点的边。
代码实现(c++):
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 1024; //最大顶点数
const int maxm = maxn * (maxn - 1); //最大边数
int n, m; //顶点个数,边的个数,顶点的编号从1-n
struct Edge{
int fromValue, toValue;
Edge* fnext;
Edge* tnext;
};
struct VNode {
Edge* inLinkFirst;
Edge* outLinkFist;
}Vlink[maxn];
/*
* 十字链表建边
*
*/
void Link(int from, int to) {
//建边
Edge* p = new Edge();
//赋值
p->fromValue = from;
p->toValue = to;
//头插法插入入度头节点
p->fnext = Vlink[from].outLinkFist;//f表示出
Vlink[from].outLinkFist = p;
//头插法插入出度头节点
p->tnext = Vlink[to].inLinkFirst;
Vlink[to].inLinkFirst = p;
}
/*
打印这个十字链表
*/
void print() {
for (int i = 1; i <= n; ++i) {
cout << "V" << i << ":" << endl;
//以顶点i为尾(指向顶点i)的边
cout << "in: ";
Edge* e = Vlink[i].inLinkFirst;
while (e) {
cout << e->fromValue << " -> ";
e = e->tnext;
}
cout << "^" << endl;
//以顶点i为头(从顶点i出发)的边
cout << "out: ";
e = Vlink[i].outLinkFist;
while (e) {
cout << e->toValue << " -> ";
e = e->fnext;
}
cout << "^" << endl;
}
}
int main() {
//测试
n = 4;
m = 7;
//初始化vlist
for (int i = 1; i <= n; ++i) {
Vlink[i].inLinkFirst = NULL;
Vlink[i].outLinkFist = NULL;
}
//以下m行建图
Link(1, 2);
Link(1, 3);
Link(3, 4);
Link(3, 1);
Link(4, 1);
Link(4, 2);
Link(4, 3);
print();
return 0;
}
运行结果
表示的有向图如图所示: