图 由有穷非空顶点集V和边E组成。每一条边就是一个点对(v,w)。图中的数据元素称为顶点。如果点对是有序的(每一个点的下一个点是固定的),那么图被称为有向图,否则就是无向图。与图的边相关的数值叫做权值,带权的图称为网(network).
与图有关的简单概念:
(1)如果图中含有一条从一个顶点到他自身的边(v,v),那么路径v叫做环。
(2)简单路径是指路径上所有的点都是互异的,但是第一个和最后一个点可能是同一个。
(3)有向图中的圈是从一个顶点出发,回到自己,并且路径的长至少为1。如果这个路径是简单路径,则这个圈是简单圈。
而无向图的圈,要求这个边是互异的。
(4)如果一个无向图中从每一个顶点出发,都有到其他每个定点的路径,则这个图是联通的。具有这种性质的有向图被称为强连通图。
(5)如果把一个有向图的所有有向边去掉方向形成的无向图被称为这个有向图的基础图,也叫基图。
(6)有向图不是强连通,但是他的基图是连通的,则被称为弱连通。
完全图是其每一对顶点之间都存在一条边的图。
图的存储方式一般有四种:(1)邻接矩阵法
(2)邻接表法
(3)十字链表法
(4)邻接多重表法
这里介绍两种存储结构法 邻接矩阵法、邻接表法
邻接矩阵法的实现(针对无向不带权的图)
代码的实现可以对照下图的图来观察
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
#define Default_Size 8 //默认的最大顶点数
#define T char
//enum bool {false,true};
typedef struct Graphmatrix //针对无向不带权的图,用邻接矩阵法存储
{
int Maxvertices;//最大容量
int Numvertices;//定点数
int Numedgs;//边数
T *Verticeslist;//存储定点的结构
int **VEdges;//存储边界
}Graphmatrix;
void Init(Graphmatrix *g)//矩阵的初始化
{
int i,j;
g->Maxvertices = Default_Size;
g->Numvertices = g->Numedgs = 0;
g->Verticeslist = (T*)malloc(sizeof(T)* (g->Maxvertices));
assert(NULL != g->Verticeslist);
g->VEdges = (int**)malloc(sizeof(int *)*(g->Maxvertices));//初始边界数内存最大开辟
assert(NULL != g->VEdges);//判断边界内存是否开辟成功
for(i = 0;i<g->Maxvertices;++i) //初始化二维边界值
{
g->VEdges[i] = (int*)malloc(sizeof(int)*(g->Maxvertices));
}
for(i = 0;i<g->Maxvertices;++i)
{
for(j = 0;j<g->Maxvertices;++j )
{
g->VEdges[i][j] = 0;
}
}
}
bool Insert(Graphmatrix *g ,T x) //顶点的插入
{
if(g->Numvertices >= g->Maxvertices)
return false;
g->Verticeslist[g->Numvertices++] = x;
}
void Show(Graphmatrix *g) //邻接矩阵法的显示
{
printf(" ");
for(int i = 0;i<g->Numvertices;++i)
{
printf("%c ",g->Verticeslist[i]);
}
printf("\n");
for(int i = 0;i<g->Numvertices;++i)
{
printf("%c ",g->Verticeslist[i]);//纵列输入顶点
for(int j = 0;j<g->Numvertices;++j)
{
printf("%d ",g->VEdges[i][j]);
}
printf("\n");
}
printf("\n");
}
int GetPos(Graphmatrix *g,T v) //获取v定点的位置
{
for(int i = 0;i<g->Numvertices;++i)
{
if(g->Verticeslist[i] == v)
return i;
}
return -1;
}
void InsertEdge(Graphmatrix *gg,T x,T y) //插入边
{
int p1 = GetPos(gg,x); //获得x顶点的位置
int p2 = GetPos(gg,y);
if(p1==-1 || p2==-1)//顶点位置不合法
return;
gg->VEdges[p1][p2] = gg->VEdges[p2][p1] = 1;//两点之间的边以1填充
gg->Numedgs++;
}
void RemoveEdge(Graphmatrix *gg,T x,T y) //删除边
{
int p1 = GetPos(gg,x); //获得x顶点的位置
int p2 = GetPos(gg,y);
if(p1==-1 || p2==-1)//顶点位置不合法
return;
if(gg->VEdges[p1][p2] == 0) //x,y之间本没有边,直接返回,否则重新赋值0
return;
gg->VEdges[p1][p2] = gg->VEdges[p2][p1] = 0;//两点之间的边以1填充
gg->Numedgs--;
}
void RemoveVertex(Graphmatrix *gg,T x) //删除顶点
{
int p = GetPos(gg,x); //x的位置不合法
if(p == -1)
return;
int i,j,numedges = 0;//用来确定删除点所含的边数
for(i=p;i<gg->Numvertices-1;++i)
{
gg->Verticeslist[i] = gg->Verticeslist[i+1];
}
for(i=0;i<gg->Numvertices;++i)
{
if(gg->VEdges[p][i] != 0)
numedges++;
}
for(i=p;i<gg->Numvertices-1;++i)
{
for(j=0;j<gg->Numvertices;++j)
{
gg->VEdges[i][j] = gg->VEdges[i+1][j]; //删除x所在的行,将x所在的后一行依次给前行赋值
}
}
for(i=p;i<gg->Numvertices;++i)
{
for(j=0;j<gg->Numvertices;++j)
{
gg->VEdges[j][i] = gg->VEdges[j][i+1]; //删除x所在的列,将x所在的后一列依次给前列赋值
}
}
gg->Numvertices--;
gg->Numedgs -= numedges;
}
void Destroy(Graphmatrix *gg)//释放链表空间和释放动态申请的二维空间
{
free(gg->Verticeslist);//释放顶点
gg->Verticeslist = NULL;
for(int i=0;i<gg->Numvertices;++i)//释放边
{
free(gg->VEdges[i]);
}
free(gg->VEdges);
gg->Numedgs = gg->Numvertices = gg->Maxvertices = 0;
printf("Matrix freed\n");
}
测试文件
#include"Graph_head.h"
void main()
{
Graphmatrix gg;
Init(&gg); //矩阵的初始化
Insert(&gg,'A');//矩阵的顶点插入
Insert(&gg,'B');
Insert(&gg,'C');
Insert(&gg,'D');
Insert(&gg,'E');
Show(&gg);
InsertEdge(&gg,'A','B');//矩阵的边插入,以1代表两者之间的边
InsertEdge(&gg,'A','D');
InsertEdge(&gg,'B','C');
InsertEdge(&gg,'B','E');
InsertEdge(&gg,'E','C');
InsertEdge(&gg,'C','D');
Show(&gg);
RemoveEdge(&gg,'C','E'); //摧毁边
Show(&gg);
RemoveVertex(&gg,'C');//摧毁顶点
Show(&gg);
Destroy(&gg);//摧毁矩阵
Show(&gg);
}
测试文件的输出结果
邻接表的实现(对于无向无权图 )
#pragma once //无向无权图
#include<stdio.h>
#include<malloc.h>
#include<assert.h>
#define Max_size 10
#define T char
typedef struct Edges
{
int dex;//顶点的下标
struct Edges *link;//指向下一条边的指针
}Edges;
typedef struct Vertex
{
T data;//顶点数据
Edges *adj;//顶点指向边的指针
}Vertex;
typedef struct Graph_table
{
int MaxVertices;//最大容量
int NmuVertices; //顶点数
int NumEdges; //边数
Vertex *NnodeTable;//顶点的结构的表,
}Graph_table;
void InitGraph(Graph_table *gt)
{
gt->MaxVertices = Max_size;
gt->NmuVertices = gt->NumEdges = 0;
gt->NnodeTable = (Vertex*)malloc(sizeof(Vertex)*(gt->MaxVertices));
assert(NULL != gt->NnodeTable);
for(int i= 0;i<gt->MaxVertices;++i)
{
gt->NnodeTable[i].adj = NULL;
}
}
void InsertVertex(Graph_table *gt,T x)//
{
if(gt->NmuVertices >= gt->MaxVertices)
return;
gt->NnodeTable[gt->NmuVertices++].data = x;
}
void Show(Graph_table *gt)
{
Edges *p;
for(int i=0;i<gt->NmuVertices;++i)
{
printf("%d , %c : ",i,gt->NnodeTable[i].data);
p = gt->NnodeTable[i].adj;
while(NULL != p)
{
printf("%d --> ",p->dex);
p = p->link;
}
printf("NULL.\n");
}
printf("\n");
}
int Getpos(Graph_table *gt,T x) //获取顶点的位置
{
for(int i= 0;i<gt->NmuVertices;++i)
{
if(gt->NnodeTable[i].data == x)
return i;
}
return -1; //顶点在表里不存在
}
void InsertEdge(Graph_table *gt,T x,T y) //插入边
{
int p1 = Getpos(gt,x);
int p2 = Getpos(gt,y);
if(p1==-1 || p2==-1)
return;
Edges *s;
s = (Edges *)malloc(sizeof(Edges));//开辟边结点
assert(s != NULL);
s->dex = p2; //x-->y x到y的边
s->link = gt->NnodeTable[p1].adj;
gt->NnodeTable[p1].adj = s;
s = (Edges *)malloc(sizeof(Edges));//开辟边结点
assert(s != NULL);
s->dex = p1; //y-->x y到 x的边
s->link = gt->NnodeTable[p2].adj;
gt->NnodeTable[p2].adj = s;
gt->NumEdges++;
}
测试文件
#include"Graph_table.h"
void main()
{
Graph_table gt; //邻接表的对象
InitGraph(>); //邻接表的初始化
InsertVertex(>,'A');//邻接顶点的插入
InsertVertex(>,'B');
InsertVertex(>,'C');
InsertVertex(>,'D');
InsertVertex(>,'E');
Show(>);
InsertEdge(>,'A','B');//邻接边的插入
InsertEdge(>,'A','D');
InsertEdge(>,'B','C');
InsertEdge(>,'B','E');
InsertEdge(>,'C','D');
InsertEdge(>,'C','E');
Show(>);
}
邻接表的测试结果