重点:邻接矩阵(数组)表示法 和 邻接表(链式)表示法
一:数组(邻接矩阵)表示法
1,先建立一个顶点表(记录各个顶点的信息)和一个邻接矩阵(表示各个顶点之间的关系).
顶点表是一个一维数组Vexs[n]
无向图邻接矩阵是一个二维数组arcs[n][n],如果两个顶点间有边相连,那么arcs[n][n] = 1,否则arcs[n][n] = 0(n为两个顶点的下标)n取决于顶点个数
有向图邻接矩阵是一个二维数组arcs[i][j],如果存在着由i顶点发出的,到j顶点弧,那么arcs[i][j] = 1,别的位置都为0
无向图的邻接矩阵特点:
1,无向图的邻接矩阵是对称的;
2,顶点i的度 = 第i行(列)中1的个数;
3,全完图的邻接矩阵中,对角元素为0,其余都是1;
在有向图的邻接矩阵中
第i行含义:以结点vi为尾的弧(即出度边)
第j列含义:以结点vj为头的弧(即入度边)
有向图的邻接矩阵特点:
1,有向图的邻接矩阵可能是不对称的.
2,顶点的出度 = 第i行元素之和;顶点的入度 = 第j列元素之和.
3,顶点的度 = 第i行的元素之和 + 第j列的元素之和
网(即有权图)的邻接矩阵表示法
无向网的邻接矩阵,就是把无向图的邻接矩阵中的1,变成权值,0变成无穷大
有向网的邻接矩阵,就是把有向图的邻接矩阵中的1,变成权值,0变成无穷大
二,邻接矩阵代码实现
邻接矩阵的存储表示:用两个数组分别存储顶点表和邻接矩阵
#include
#include
#define MAXSIZE 100 //最大顶点数
typedef struct{
char vexs[MAXSIZE];//这里的数据类型根据实际情况而定
int arcs[MAXSIZE][MAXSIZE];//这里的数据类型根据实际情况而定
int vexnum, arcnum;//图的当前顶点数和边数
}G;
int main()
{
printf("Hello world!\n");
return 0;
}
采用邻接矩阵表示法创建无向网
算法思想:1,输入总顶点数和总边数
2,依次输入顶点的信息存入顶点表中.
3,初始化邻接矩阵,使每个权值初始化为极大值.
4,构造邻接矩阵.
#include
#include
#define MAXSIZE 100 //最大顶点数
#define MAX_INT 32767//设置最大值
typedef struct{
char vexs[MAXSIZE];//这里的数据类型根据实际情况而定
int arcs[MAXSIZE][MAXSIZE];//这里的数据类型根据实际情况而定
int vexnum, arcnum;//图的当前顶点数和边数
}Graph;
int get_index(char* arr,char m)
{
int i = 0;
for(i = 0; i < MAXSIZE; i++)
{
if(arr[i] == m)return i;
}
return -1;
}
void CreatGraph(Graph* G)
{
int i,j = 0;
printf("请输入顶点和边的数量:>");
scanf("%d%d",&G->vexnum,&G->arcnum);//把输入的值保存到图结构体中
for(i = 0; i < G->vexnum; i++)//初始化邻接矩阵
{
for(j = 0; j < G->vexnum; j++)
{
G->arcs[i][j] = MAX_INT;//边的权值设置为极大值
}
}
for(i = 0; i < G->vexnum; i++)
{
printf("请输入每个顶点的值:>");
getchar();//清除键盘缓存区的回车键
scanf("%c", &G->vexs[i]);//把输入的值保存到顶点数组当中
}
for(i = 0; i < G->arcnum; i++)//有几条边,循环几次
{
char m,n;//用来接收两个顶点
int w;//用来接收权值
int j,k;//接收顶点的下标
printf("请输入哪两个顶点之间有边,以及边的权值,格式为:ab 10:>");
getchar();
scanf("%c%c%d",&m,&n,&w);
j = get_index(G->vexs,m);//得到输入的m的值,在顶点数组中的下标
k = get_index(G->vexs,n);//得到输入的n的值,在顶点数组中的下标
G->arcs[j][k] = w;//给邻接矩阵对应的位置赋权值
G->arcs[k][j] = w;//因为是无向网,所以是对称的,给对称点赋相同值
}
}
int main()
{
Graph G;
CreatGraph(&G);
return 0;
}
会创建无向网了,无向图就只是初始化的时候,全部初始化为零,然后权值都为1
有向网就更容易了,别的都不变,只要把G->arcs[k][j] = w去掉,因为有向网不对称
数组(邻接矩阵)表示法的优点是:
1,直观,简单,好理解
2,方便检查任意一对顶点间是否存在边
3,方便找任一顶点的所有"邻接点"(有边直接相连的顶点)
4,方便计算任一顶点的度(从该点发出的边数为"出度",指向该点的边数为"入度")
无向图:对应行(或列)非0元素的个数
有向图:对应行非0元素的个数是"出度",对应列非0元素的个数是"入度"
数组(邻接矩阵)表示法的缺点是:
1,不便于增加和删除顶点
2,浪费空间--存稀疏图(点很多而边很少)有大量无效元素,空间复杂度为O(n*n)
3,浪费时间--统计稀疏图中一共有多少条边,时间复杂度也为O(n*n)
三,邻接表表示法(链式)
1,顶点
按编号将顶点数据存储在一维数组中;
2,关联同一顶点的边(以顶点为尾的弧)
用线性链表存储
无 向 图特点:
1邻接表不唯一(上面表结点中1和3可以换位置)
2若无向图中有n个顶点,e条边,则其邻接表需要n个头结点和2e个表结点.适宜存储稀疏图
3无向图中顶点vi的度为第i个单链表中的结点数
有 向 图特点:
1顶点vi的出度为第i个单链表中的结点个数
2顶点vi的入度为整个单链表中邻接点域值是i-1的结点个数
这种邻接表,找出度容易,找入度难,所以就有另外一种逆邻接表
特点:
1顶点vi的入度为第i个单链表中的结点个数
2顶点vi的出度为整个单链表中邻接点域值是i-1的结点个数
这种邻接表,找入度容易,找出度难,所以我们可以根据实际情况选择合适的邻接表
四,图(网)的邻接表存储代码实现
用邻接表创建一个无向网:
表结点的结构体:
#include
#include
typedef struct TableNode{
int index;//结点在数组中的下标
TableNode* nextarc;
int info;//权值
}TNode;
int main()
{
return 0;
}
头结点和整个无向网结构体:
#include
#include
typedef struct TableNode{//表结点
int index;//结点在数组中的下标
TableNode* nextarc;
int info;//权值
}TNode;
typedef struct{//头结点
char data;
TNode* firstarc;
}HNode;
typedef struct{//整个无向网的结构体
HNode* Gragh;
int vexnum,arc;
}
int main()
{
return 0;
}
算法思想:1,输入顶点和边的数量,并保存在网结构体中
2,通过输入的顶点个数,创建头结点的数组
3,输入顶点的值,并把顶点的firstarc值赋空值
4,输入每条边的两个顶点值和这条边的权值
5,利用输入的值创建表结点,并用头插法加入到对应的头结点
具体的代码如下:
#include
#include
typedef struct TableNode{//表结点
int index;//结点在数组中的下标
struct TableNode* nextarc;
int info;//权值
}TNode;
typedef struct{//头结点
char data;
TNode* firstarc;
}HNode;
typedef struct{//整个无向网的结构体
HNode* Gragh;
int vexnum,arc;
}UnGragh;
int get_index(HNode* arr, char m, int nums)
{
int i = 0;
for(i = 0; i < nums; i++)
{
if(arr[i].data == m)
{
return i;
}
}
return -1;
}
void CreatUndigraghNet(UnGragh* G)
{
printf("请输入顶点和边的数量:>");
scanf("%d%d",&G->vexnum,&G->arc);
G->Gragh = malloc(sizeof(HNode)*G->vexnum);
int i = 0;
for(i = 0;i < G->vexnum; i++)
{
printf("请输入顶点的值:>");
getchar();
scanf("%c",&G->Gragh[i].data);
G->Gragh[i].firstarc = NULL;
}
for(i = 0;i < G->arc; i++)
{
char m,n;//用来接收两个顶点
int w;//用来接收权值
int j,k;//接收顶点的下标
printf("请输入哪两个顶点之间有边,以及边的权值,格式为:ab 10:>");
getchar();
scanf("%c%c%d",&m,&n,&w);
j = get_index(G->Gragh,m,G->vexnum);//得到输入的m的值,在顶点数组中的下标
k = get_index(G->Gragh,n,G->vexnum);//得到输入的n的值,在顶点数组中的下标
TNode* P1 = (TNode*)malloc(sizeof(TNode));//创建一个表结点
P1->index = k;//给表结点赋值
P1->info = w;
P1->nextarc = G->Gragh[j].firstarc;//利用头插法把表结点插入到头结点后面
G->Gragh[j].firstarc = P1;
//因为是无向的,所以下面还要再建立一个反向的边
TNode* P2 = (TNode*)malloc(sizeof(TNode));
P2->index = j;
P2->info = w;
P2->nextarc = G->Gragh[k].firstarc;
G->Gragh[k].firstarc = P2;
}
}
int main()
{
UnGragh G;
CreatUndigraghNet(&G);
return 0;
}
五,图的 十字链表 表示法
十字链表,就是集中了 邻结表 和 逆邻结表 的优势,找出度和找入度都容易的一种链表,适用于有向图或网的查找
firstout可以理解为邻结表的表头,方便查出度(指向出度)
firstin 可以理解为逆邻结表的表头,方便查入度(指向入度)
headvex 作为出度时对应头结点数组中的下标(弧头位置)
tailvex 作为入度时对应头结点数组中的下标(弧尾位置)
Tlink 弧尾相同的下一条弧
Hlink 弧头相同的下一条弧
继续用上面的例子:
把两个表揉合在一起,组成十字链表为:
用C语言,生成十字链表有向图,代码为:
#include
#include
typedef struct TableNode{//表结点
int headvex;//弧头位置(下标)
int tailvex;//弧尾位置(下标)
struct TableNode* Hlink;//弧头相同的下一条弧
struct TableNode* Tlink;//弧尾相同的下一条弧
}TNode;
typedef struct{//头结点
char data;
TNode* firstin;//指向入度
TNode* firstout;//指向出度
}HNode;
typedef struct{//整个有向图的结构体
HNode* Gragh;
int vexnum,arc;
}UnGragh;
int get_index(HNode* arr, char m, int nums)
{
int i = 0;
for(i = 0; i < nums; i++)
{
if(arr[i].data == m)
{
return i;
}
}
return -1;
}
void CreatUndigraghNet(UnGragh* G)
{
printf("请输入顶点和边的数量:>");
scanf("%d%d",&G->vexnum,&G->arc);
G->Gragh = malloc(sizeof(HNode)*G->vexnum);
int i = 0;
for(i = 0;i < G->vexnum; i++)
{
printf("请输入顶点的值:>");
getchar();
scanf("%c",&G->Gragh[i].data);
G->Gragh[i].firstin = NULL;
G->Gragh[i].firstout = NULL;
}
for(i = 0;i < G->arc; i++)
{
char m,n;//用来接收两个顶点
int j,k;//接收顶点的下标
printf("请按方向输入每一条边的两个顶点值,格式为:ab>");
getchar();
scanf("%c%c",&m,&n);
j = get_index(G->Gragh,m,G->vexnum);//得到输入的m的值,在顶点数组中的下标
k = get_index(G->Gragh,n,G->vexnum);//得到输入的n的值,在顶点数组中的下标
TNode* P1 = (TNode*)malloc(sizeof(TNode));//创建一个表结点
//利用头插法把出度边插入到G->Gragh[j]后面
P1->tailvex = j;
P1->headvex = k;
P1->Tlink = G->Gragh[j].firstout;
G->Gragh[j].firstout = P1;
//利用头插法把入度边插入到G->Gragh[k]后面
P1->Hlink = G->Gragh[k].firstin;
G->Gragh[k].firstin = P1;
}
}
int main()
{
UnGragh G;
CreatUndigraghNet(&G);
return 0;
}
六,邻接多重表
邻接多重表主要是用在无向图中,解决无向图中每条边都要存储两遍,造成浪费的问题
邻接多重表的头结点和表结点的存储结构为:
data:顶点的值
firstedge:连接该顶点的第一条边
ivex, jvex:为一条边的两个顶点的下标
ilink:关联 ivex下标代表的头结点的下一条边
jlink:关联 jvex下标代表的头结点的下一条边
例:
具体的生成代码为:
#include
#include
typedef struct TableNode{//表结点
int ivex;
int jvex;//一条边的两个顶点
struct TableNode* ilink;//关联ivex的下一条边指针
struct TableNode* jlink;//关联jvex的下一条边指针
}TNode;
typedef struct{//头结点
char data;
TNode* firstedge;//指向第一条边
}HNode;
typedef struct{//无向图的结构体
HNode* Gragh;
int vexnum,arc;
}UnGragh;
int get_index(HNode* arr, char m, int nums)//根据输入得到下标
{
int i = 0;
for(i = 0; i < nums; i++)
{
if(arr[i].data == m)
{
return i;
}
}
return -1;
}
void CreatUndigraghNet(UnGragh* G)
{
printf("请输入顶点和边的数量:>");
scanf("%d%d",&G->vexnum,&G->arc);
G->Gragh = malloc(sizeof(HNode)*G->vexnum);
int i = 0;
for(i = 0;i < G->vexnum; i++)
{
printf("请输入顶点的值:>");
getchar();
scanf("%c",&G->Gragh[i].data);
G->Gragh[i].firstedge = NULL;
}
for(i = 0;i < G->arc; i++)
{
char m,n;//用来接收两个顶点
int j,k;//接收顶点的下标
printf("请按方向输入每一条边的两个顶点值,格式为:ab>");
getchar();
scanf("%c%c",&m,&n);
j = get_index(G->Gragh,m,G->vexnum);//得到输入的m的值,在顶点数组中的下标
k = get_index(G->Gragh,n,G->vexnum);//得到输入的n的值,在顶点数组中的下标
TNode* P1 = (TNode*)malloc(sizeof(TNode));//创建一个表结点
//利用头插法把输入的第一条边插入到头结点G->Gragh[j]后
P1->ivex = j;
P1->jvex = k;
P1->ilink = G->Gragh[j].firstedge;
G->Gragh[j].firstedge = P1;
//利用头插法把输入的第一条边插入到头结点G->Gragh[k]后面
P1->jlink = G->Gragh[k].firstedge;
G->Gragh[k].firstedge = P1;
}
}
int main()
{
UnGragh G;
CreatUndigraghNet(&G);
return 0;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)