一、邻接表的特点
1、方便找任一顶点的所有“邻接点”
2、节约稀疏图的空间
- 需要N个头指针+2E个结点(每个结点至少2个域)
3、方便计算任一顶点的“度”?
- 对无向图:是的
- 对有向图:只能计算“出度”;需要构造“逆邻接表”(存指向自己的边)来方便计算“入度”
二、邻接矩阵与邻接表表示法的关系
1、联系:
- 邻接表中每个链表对应于邻接短阵中的一行,链表中结点个数等于一行中非零元素的个数。
2、区别:
- 对于任一确定的无向图,邻接矩阵是唯一的(行列号与顶点编号致),但邻接表不唯一(链接次序与顶点编号无关)
- 邻接矩阵的空间复杂度为O(n),而邻接表的空间复杂度为O(n+e)
- 邻接矩阵多用于密图;而邻接表多用在稀疏图
三、十字链表
1、定义:
- 十字链表的存储结构包含表头结点表与弧表,与邻接表类似,是一种顺序结合链式的存储结构,因此需要有两个指针域分别指向以顶点为弧尾和以顶点为弧头的弧结点。
- 分析:在表头结点中,顶点数据域存储与顶点有关的信息,本例指定为 char类型;指针域firstin指向以顶点为弧头的第一条弧,指针域firstout指向以顶点为弧尾的第一条弧。这也告诉我们,待会在创建弧<v1,v2>时,不仅需要处理v1的firstout,还需要处理v2的firstin。
- 分析:弧尾结点存储该弧尾结点所在图中的位置,弧头结点同理;hlink指向与该弧有相同弧头的弧结点,tlink指向与该弧有相同弧尾的弧结点。
四、十字链表代码实现
//文件名尾:OLGraph.h
#pragma once
#include<iostream>
using namespace std;
#define Maxvex 100 //最大顶点数
//定义权值与顶点的信息类型
typedef int OtherInfo;
typedef char vexType;
//定义弧结点的数据结构
typedef struct ArcBox {
int tvex, hvex; //指向该弧的弧尾与弧头
struct ArcBox* tlink, * hlink; //指向与该弧有相同弧尾与弧头的弧结点
OtherInfo w; //弧上信息
}ArcBox;
//定义表头结点的数据结构
typedef struct VexNode {
vexType data; //数据域
struct ArcBox* firstin, * firstout; //该顶点的第一条入度弧结点与出度弧结点
}VexNode;
//定义十字链表的数据结构
typedef struct OLGraph {
VexNode xlist[Maxvex]; //表头结点表的最大容量为maxvex
int vexnum, arcnum; //顶点与弧的个数
}OLGraph;
//创建十字链表
void CreateUDNol(OLGraph& G);
我们来完成创建十字链表的函数CreateUDNol。这个函数会创建一个图,包括顶点和弧(连接不同顶点的线)。为了实现这个函数,我们通常会:初始化十字链表,设置顶点数和弧的数目。询问用户每个顶点的信息。创建弧的链接,根据头部和尾部顶点,并将弧添加到十字链表中。
#include "OLGraph.h"
#include <iostream>
void CreateUDNol(OLGraph& G) {
std::cout << "输入顶点数和弧数: ";
std::cin >> G.vexnum >> G.arcnum;
if (G.vexnum > Maxvex || G.arcnum > (G.vexnum - 1) * G.vexnum|| G.vexnum<1)
{
cout << "数据有误" << endl;
return;
}
// 初始化顶点表
for (int i = 0; i < G.vexnum; i++) {
std::cout << "输入顶点" << i + 1 << "的数据: ";
std::cin >> G.xlist[i].data;
G.xlist[i].firstin = nullptr;
G.xlist[i].firstout = nullptr;
}
// 处理弧
for (int i = 0; i < G.arcnum; i++) {
int tail, head;
OtherInfo weight;
std::cout << "输入弧" << i + 1 << "的尾顶点和头顶点(索引从0开始)以及权重: ";
std::cin >> tail >> head >> weight;
//判断合法性
if (tail >= G.vexnum || head >= G.vexnum||head<0||tail <0||tail ==head)
{
i--;
cout << "数据有误,请重新输入!" << endl;
continue;
}
// 创建一个新弧结点
ArcBox* newArc = new ArcBox;
newArc->tvex = tail;
newArc->hvex = head;
newArc->w = weight;
// 将新弧添加到尾顶点的出度链表
newArc->tlink = G.xlist[tail].firstout;
G.xlist[tail].firstout = newArc;
// 将新弧添加到头顶点的入度链表
newArc->hlink = G.xlist[head].firstin;
G.xlist[head].firstin = newArc;
}
}
int main() {
OLGraph graph;
CreateUDNol(graph);
// 可以添加代码用于输出或测试已创建的图
return 0;
}
输出各个顶点的出度与入度点:
//文件名:test.cpp
void test()
{
OLGraph G;
CreateUDNol(G);
//输出每个顶点的出度点与入度点
for (int i = 0;i < G.vexnum;i++)
{
cout << "该顶点的下标为:" << i << " 其数据域:" << G.xlist[i].data;
cout << " 该顶点的出度弧:";
ArcBox* p = G.xlist[i].firstout;
while (p)
{
int node = p->hvex;
cout << node << ": " << G.xlist[node].data << " ";
p = p->tlink;
}
cout << "该顶点的入度弧:";
p = G.xlist[i].firstin;
while (p)
{
int node = p->tvex;
cout << node << ": " << G.xlist[node].data << " ";
p = p->hlink;
}
cout << endl;
}
}
int main()
{
test();
return 0;
}
测试结果: