|1,若(vi,vj)∈E
M[i][j]=|
|0,其他无向图邻接矩阵的特点
1、无向图的邻接矩阵是一个对称矩阵,并且唯一
2、第i行或第i列非零元素的个数正好是第i个顶点的度
|1,若<vi,vj>∈E
M[i][j]=|
|0,其他有向图邻接矩阵的特点
1、有向图的邻接矩阵不一定是对称的
2、第i行非零元素的个数正好是第i个顶点的出度,第i列非零元素的个数正好是第i个顶点的入度
网是带权的图,需要存储边的权值,则邻接矩阵表示为
|wij,若(vi,vj)∈E或<vi,vj>∈E
M[i][j]=|
|∞,其他(无穷表达不可达)vexnum
3
edgenum
3
input vex info
0 1 2
input (i,j) format: i j
0 1
0 2
1 2
[
0 1 1
1 0 1
1 1 0
]样例代码
//main1.cpp
#include <iostream>
using namespace std;
#define MaxVnum 100 //顶点最大值
typedef char VexType; //顶点存储数据类型
using EdgeType = int; //权值数据类型
using AMGragh = class
{
public:
VexType Vex[MaxVnum];
EdgeType Edge[MaxVnum][MaxVnum];
int vexnum, edgenum; //顶点数,边数
void print();
void CreateAMGraph();
};
void AMGragh::print()
{
cout << "[\n";
for (int i = 0; i < vexnum; i++)
{
for (int j = 0; j < vexnum; j++)
{
cout << Edge[i][j] << " ";
}
cout << endl;
}
cout << "]\n";
}
/**
* @brief 创建无向图的邻接矩阵
*
* @param G
*/
void AMGragh::CreateAMGraph()
{
AMGragh &G = *this;
VexType u, v;
cout << "vexnum" << endl;
cin >> G.vexnum; //输入顶点数量
cout << "edgenum" << endl;
cin >> G.edgenum; //输入边数量
cout << "input vex info" << endl;
for (int i = 0; i < G.vexnum; i++)
{
cin >> G.Vex[i]; //输入顶点存储数据
}
//初始化邻接矩阵所有值为0
for (int i = 0; i < G.vexnum; i++)
{
for (int j = 0; j < G.vexnum; j++)
{
G.Edge[i][j] = 0;
}
}
cout << "input (i,j) format: i j" << endl;
while (G.edgenum--)
{
int i, j;
cin >> i >> j;
if (i >= 0 && j >= 0)
{
G.Edge[i][j] = G.Edge[j][i] = 1;
}
}
}
int main(int argc, char **argv)
{
AMGragh g;
g.CreateAMGraph();
g.print();
return 0;
}邻接矩阵的优点
邻接矩阵的缺点
邻接矩阵是图的数组表示法,还有一种边集数组表示法,每个边存储其起点与终点,如果是网,则增加一个权值域
struct Edge{
int u;
int v;
int w;
};
Edge graph[100];//如100个边的图边集数组存储方式计算顶点的度或查找边时都要遍历整个边集数组,时间复杂度为O(e),e为边的个数
(0,3) (0,1) (1,2) (1,3) (2,3)
data first
0 a ---> 3|next--->1|^
1 b ---> 3|next--->2|next--->0|^//链长度为1的度数
2 c ---> 3|next--->1|^
3 d ---> 2|next--->1|next--->0|^
<0,4> <0,2> <0,1> <1,2> <2,4> <2,3> <3,4>
data first
0 a ---> 4|next--->2|next--->1|^
1 b ---> 2|^//链长度为1的出度数
2 c ---> 4|next--->3|^
3 d ---> 4|^
4 e ^<0,4> <0,2> <0,1> <1,2> <2,4> <2,3> <3,4>
data first
0 a ^
1 b ---> 0|^
2 c ---> 0|next--->1|^
3 d ---> 2|^
4 e ---> 0|next--->2|next-->3|^//链的长度为4的入度代码实现
//main2.cpp
#include <iostream> //创建有向图的邻接表
using namespace std;
const int MaxVnum = 100; //顶点数最大值
typedef char VexType; //顶点的数据类型为字符型
typedef struct AdjNode
{ //定义邻接点类型
int v; //邻接点下标
struct AdjNode *next; //指向下一个邻接点
} AdjNode;
typedef struct VexNode
{ //定义顶点类型
VexType data; // VexType为顶点的数据类型,根据需要定义
AdjNode *first; //指向第一个邻接点
} VexNode;
typedef struct
{ //定义邻接表类型
VexNode Vex[MaxVnum];
int vexnum, edgenum; //顶点数,边数
} ALGragh;
//根据顶点数据找到顶点存储下标
int locatevex(ALGragh G, VexType x)
{
for (int i = 0; i < G.vexnum; i++) //查找顶点信息的下标
if (x == G.Vex[i].data)
return i;
return -1; //没找到
}
void insertedge(ALGragh &G, int i, int j) //插入一条边
{
AdjNode *s;
s = new AdjNode;
s->v = j;
s->next = G.Vex[i].first; //头插法
G.Vex[i].first = s;
}
void printg(ALGragh G) //输出邻接表
{
cout << "----------邻接表如下:----------" << endl;
for (int i = 0; i < G.vexnum; i++)
{
AdjNode *t = G.Vex[i].first;
cout << G.Vex[i].data << ": ";
while (t != NULL)
{
cout << "[" << t->v << "] ";
t = t->next;
}
cout << endl;
}
}
void CreateALGraph(ALGragh &G) //创建有向图邻接表
{
int i, j;
VexType u, v;
cout << "请输入顶点数和边数:" << endl;
cin >> G.vexnum >> G.edgenum;
cout << "请输入顶点信息:" << endl;
for (i = 0; i < G.vexnum; i++) //输入顶点信息,存入顶点信息数组
cin >> G.Vex[i].data;
for (i = 0; i < G.vexnum; i++)
G.Vex[i].first = NULL;
cout << "请依次输入每条边的两个顶点u,v" << endl;
while (G.edgenum--)
{
cin >> u >> v;
i = locatevex(G, u); //查找顶点u的存储下标
j = locatevex(G, v); //查找顶点v的存储下标
if (i != -1 && j != -1)
insertedge(G, i, j);
else
{
cout << "输入顶点信息错!请重新输入!" << endl;
G.edgenum++; //本次输入不算
}
}
}
int main()
{
ALGragh G;
CreateALGraph(G); //创建有向图邻接表
printg(G); //输出邻接表
return 0;
}邻接表的优点
1、便于访问所有邻接点,访问所有顶点的邻接点,时间复杂度为O(n+e)
2、空间复杂度低,顶点表占用n个空间,无向图的邻接点表占用n+2e个空间,有向图的邻接点表占用n+e个空间,总体空间复杂度为O(n+e),而邻接矩阵的空间复杂度为O(n^2)
3、便于增删节点
邻接表的缺点
1、不便于判断两顶点之间是否有边,要判断两顶点是否有边,需要遍历该顶点后面的邻接点链表
2、不便于计算各顶点的度,在无向图邻接表中,顶点的度为该顶点后面单链表中的节点数目,在有向图邻接表中,顶点的出度为该顶点后面单链表中的节点数,但求入度比较难。在有向图逆邻接表中,顶点的入度为该顶点后面单链表中的节点数,但求出度有困难
弧节点类型
typedef struct arcNode{
int tail;//弧尾下标
int head;//弧头下标
struct arcNode *hlink;//指向同弧头节点
struct arcNode *tlink;//指向同弧尾的弧
}arcNode;顶点节点
typedef struct vexNode{
VexType data;
arcNode*firstin;//指向第一个入弧
arcNode*firstout;//指向第一个出弧
}十字链表结构
typedef struct{
VexNode Vex[100];
int vexnum,edgenum;//顶点数,边数
}十字链表虽然结构复杂,但创建十字链表的时间复杂度和邻接表相同,十字链表存储有向图,可以高效访问每个顶带你的出弧和入弧,很容易得到顶点的出度和入度。
邻接多重表是无向图的另一种链式存储结构,邻接表关注的是顶点,而邻接多重表关注的是边
邻接多重表结构
边节点
struct edgeNode{
int i;//顶点下标
int j;//顶点下标
struct edgeNode*ilink;//指向与i同顶点的边
struct edgeNode*jlink;//指向与j同顶点的边
};顶点节点
struct vexNode{
VexType data;
edgeNode* firstedge;//指向第一条连接边
}多重表结构
typedef struct{
VexNode Vex[MaxVnum];
int vexnum,edgenum;
}AMLGraph;