一、邻接表的定义
邻接表是一种数组与链表相结合的存储方式;用一个一维数组取存放图的顶点,记做顶点表;该数组中的每一个顶点的所有邻接点构成一个链表,无向图称该链表为该顶点的边表,有向图称该链表为该顶点作为弧尾的出边表;
当然,顶点表也可以用链表来存放,不过由于顶点数是确定的,所以用数组较为合适,方便查询;
二、邻接表的结构
EdgeNode 是边节点结构体,其数据成员vexOrderNum是该条边相对于该顶点的邻接点序号,(通俗点讲就是一条边有两个顶点,一个顶点是要将该节点接入的顶点表中的项,另一个顶点就是相对于该项的邻接节点,存在节点上);weight,权值;节点的next指针,指向下一个节点,也就是该顶点的下一个邻接顶点;
VertexNode 是顶点结构体,vexName是顶点名;vNext 指向该顶点链表的第一个邻接节点的指针;vArr[MAXVEX],vArr是一个该结构体数组的类型别名,可以用来定义该结构体数组,不明白去看一下typedef struct~
Graph 是图的结构体,存放图的相关信息,vexArr为顶点结构体数组;vexNum, edgeNum为顶点和边的数量;
typedef struct EdgeNode {
int vexOrderNum;
int weight;
struct EdgeNode* next;
}eNode;
typedef struct VertexNode {
char vexName;
eNode* vNext;
}vNode, vArr[MAXVEX];
typedef struct Graph {
vArr vexArr;
int vexNum, edgeNum;
}pGraph;
三、邻接表的创建
邻接表的创建比较好理解,先创建顶点数组,然后分别建立每个顶点的邻接链表;建立邻接链表的时候采用头插法,还是链表的内容;这里是创建一个无向图的邻接表,所以每一条边都会分别创建两个顶点的邻接节点,能理解吧,因为无向,所以一条边属于两个顶点,所以。。。
newENode->vexOrderNum = x; 当该节点的邻接点序号为x时,说明这个节点应该接到顶点y上,因为x的邻接点是y,可以理解吧,反之也一样;newENode->next = graph.vexArr[y].vNext; graph.vexArr[y].vNext = newENode;头插法增加节点;
void CreateGraph(Graph &graph) {
cout << "请输入顶点数和边数:";
cin >> graph.vexNum >> graph.edgeNum;
for(int i = 0; i < graph.vexNum; ++i) {
cout << "输入顶点名:";
cin >> graph.vexArr[i].vexName;
graph.vexArr[i].vNext = NULL;
}
int x, y, w;
eNode *newENode;
for(int i = 0; i < graph.edgeNum; ++i) {
cout << "输入第" << i+1 << "条边的两个顶点的序号x,y和权重w:";
cin >> x >> y >> w;
newENode = (eNode*)malloc(sizeof(eNode));
newENode->vexOrderNum = x;
newENode->weight = w;
newENode->next = graph.vexArr[y].vNext;
graph.vexArr[y].vNext = newENode;
newENode = (eNode*)malloc(sizeof(eNode));
newENode->vexOrderNum = y;
newENode->weight = w;
newENode->next = graph.vexArr[x].vNext;
graph.vexArr[x].vNext = newENode;
}
}
四、测试
#include<iostream>
#include<cstdlib>
#define MAXVEX 20
using namespace std;
typedef struct EdgeNode {
int vexOrderNum;
int weight;
struct EdgeNode* next;
}eNode;
typedef struct VertexNode {
char vexName;
eNode* vNext;
}vNode, vArr[MAXVEX];
typedef struct Graph {
vArr vexArr;
int vexNum, edgeNum;
}pGraph;
void CreateGraph(Graph &graph) {
cout << "请输入顶点数和边数:";
cin >> graph.vexNum >> graph.edgeNum;
for(int i = 0; i < graph.vexNum; ++i) {
cout << "输入顶点名:";
cin >> graph.vexArr[i].vexName;
graph.vexArr[i].vNext = NULL;
}
int x, y, w;
eNode *newENode;
for(int i = 0; i < graph.edgeNum; ++i) {
cout << "输入第" << i+1 << "条边的两个顶点的序号x,y和权重w:";
cin >> x >> y >> w;
newENode = (eNode*)malloc(sizeof(eNode));
newENode->vexOrderNum = x;
newENode->weight = w;
newENode->next = graph.vexArr[y].vNext;
graph.vexArr[y].vNext = newENode;
newENode = (eNode*)malloc(sizeof(eNode));
newENode->vexOrderNum = y;
newENode->weight = w;
newENode->next = graph.vexArr[x].vNext;
graph.vexArr[x].vNext = newENode;
}
}
int main() {
Graph graph;
CreateGraph(graph);
for(int i = 0; i < graph.vexNum; ++i) {
cout << graph.vexArr[i].vexName << "->";
eNode *p;
p = graph.vexArr[i].vNext;
while(p) {
cout << p->vexOrderNum << "->";
p = p->next;
}
cout << "NULL\n";
}
getchar();
return 0;
}
测试的是构建一个无向图的邻接表,结构就是顶点数组的每一个元素都是该顶点所有邻接节点构成的边的链表的头结点;这句话有点绕,细品一下就理解了~
五、总结
其实如果以上我做的这些解释都能看懂,能理解每一个词每一个步骤的含义,那么说明你对图和邻接表的是理解了的;如果看不明白,觉得云里雾里,可以再去看看图的基本定义,我是参考《大话数据结构物》这本书进行总结的,也可以去看看书上是怎么描述的,书上有配图,很好理解;
邻接矩阵和邻接表是用来存储图的,所以难免会觉得很复杂,但实际上理解了也不难,无非就是将图的顶点和边分成两部分去存储,在用一些方法将这两部分关联起来,如果在脑海中能够对这个存储结构有一个映像,那么再去理解这样的存储结构就很简单了;其实无论去理解什么东西,只靠记忆去记是很没效率的,只有找一些对应的图或者东西去做映射,这样无论是记忆还是理解起来,都会简单许多~