图的基本概念
定义: 图(Graph)是一种非线性数据结构,形式化描述为:
Graph = (V, R)
其中,
V={Vi | Vi属于datatype, i = 0,1,2,…n-1}是图中元素Vi(称为顶点,Vertex)的集合,
n=0时,V为空集。
R={<Vi, Vj> | Vi, Vj属于V, 且p(Vi, Vj)存在}是图中顶点之间的关系集,P(Vi, Vj)
为顶点Vi到Vj之间是否存在路径的判定条件,即若Vi到Vj之间的路径存在,则关系<Vi, Vj>
属于R.
分类:
有向图 弧
无向图 边
网 :
若在图的关系<Vi, Vj>或<Vj, Vi>附加一个权值w
w
Vi ------> Vj
w称为弧或边上的权。带权的图称为网。权w的具体含义 图在不同的应用领域来确定。
如:顶点表示城市,权w可以视为两个城市之间距离等等。
顶点的度:
顶点边或弧的条数。
连通图 和 非连通图
路径:
从某个顶点到其他顶点是否存在路径
图的存储结构
“数组表示法”
“邻接表”
“十字链表”
“邻接多重表”
数组表示法 又称为 “邻接矩阵”
G = (V, R)
可以用两个数组来存储图G:
一个一维数组存储图G中的各个顶点。
V 用另外一个二维数组A来存储图G中顶点之间的关系R.
第i行,表示 以顶点V[i]为起点
第j列, 表示 以顶点V[j]为终点
图中的数据类型:
图中顶点类型:
typedef char Vtype;
权值的类型 :
typedef int Adjtype;
//图中顶点的最大数目
#define MAXN 100
图的类型:
typedef struct graph
{
Vtype V[MAXN]; //一维数组来存储顶点
Adjtype A[MAXN][MAXN]; //二维数组来存储顶点与顶点之间的关系
"邻接矩阵"
int vexnum; //图中有效的顶点个数
int arcnum; //图中有效边的条数
//......
}Graph;
创建邻接矩阵
需要写一个查找顶点在一维数组中的下标
//找到某一个顶点对应的下标
int Find_V_index(Graph* g, Vtype vex)
{
int i;
for (i = 0; i < g->vexnum; i++)
{
if (g->V[i] == vex)
{
//找到了
return i;
}
}
return -1;//表示没有找到
}
Graph* Create_Graph(void)
{
Graph* g = malloc(sizeof(Graph));
g->arcnum = 0;
g->vexnum = 0;
Vtype d;
while (1)
{
//约定当用户输入为‘#’表示结束
scanf("%c", &d);
if (d == '#')
{
break;
}
g->V[g->vexnum] = d;
g->vexnum++;
}
//过滤掉'\n'
getchar();
//将二维数组中,有效区域数值全部初始化为无穷大 ‘
int i, j;
for (i = 0; i < g->vexnum; i++)
{
for (j = 0; j < g->vexnum; j++)
{
g->A[i][j] = Very_Big;
}
}
Vtype s, e;
int s_i, e_i; //s和e分别对应的下标
Adjtype w;
while (1)
{
scanf("%c%c%d", &s, &e, &w);
if (s == '#' && e == '#' && w == 0)
{
break;
}
//先找到s对应在邻接矩阵中下标
s_i = Find_V_index(g, s);
//再找到e对应在邻接矩阵中下标
e_i = Find_V_index(g, e);
g->A[s_i][e_i] = w;
//过滤掉'\n'
getchar();
}
return g;
}
邻接表
链式结构来存储图
所谓邻接表,是将图中每一个顶点v和由v发出的弧
或边构成单链表。邻接表是图的一种链式存储结构。
数据类型:
typedef char Vtype; //顶点的类型
typedef int Adjtype; //权值的类型
typedef struct bian
{
int stop_index; //终点的下标
Adjtype w; //权值
struct bian * next; //下一条边的指针
}Bian;
//顶点元素的结构体
typedef struct vertex
{
Vtype data; //顶点的数据域
Bian * first; //指向由该顶点为起点发出的第一条边
}Vertex;
//图中顶点元素的最大数目
#define MAXN 100
//图的类型
Vertex Graph[MAXN];
创建邻接表
与创建邻接矩阵思路一样,只是插入数据时用的是链表的思想
//找起点元素下标
static int Find_index(Graph* G, Vtype s)
{
int i;
for (i = 0; i < G->vexn; i++)
{
if (G->g[i].data == s)
{
return i;
}
}
return -1;
}
//图初始化
Graph* Init_Graph(void)
{
Graph* G = malloc(sizeof(*G));
G->arcn = G->vexn = 0;
Vtype d;
while (1) //键盘输入数组
{
scanf("%c", &d);
if (d == '#')
{
break;
}
G->g[G->vexn].data = d;
G->g[G->vexn].first = NULL;
G->vexn++;
}
getchar(); //过滤\n
//构造顶点发出的边组成的各个单链表
Vtype s, e;
Adjtype w;
while (1)
{
scanf("%c%c%d", &s, &e, &w);
if (s == '#')
{
break;
}
Bian* p = malloc(sizeof(*p));
p->next = NULL;
p->stop_index = Find_index(G, e); //终点元素下标
p->w = w;
int s_i= Find_index(G, s); //起点元素下标
if (G->g[s_i].first == NULL) //初始为空
{
G->g[s_i].first = p;
}
else
{
//找到插入位置进行插入操作
Bian* pk = G->g[s_i].first;
Bian* pr = NULL;
while (pk)
{
if (pk->stop_index > p->stop_index)
{
break;
}
pr = pk;
pk = pk->next;
}
if (pk == NULL) //尾插
{
pr->next = p;
}
else if (pk == G->g[s_i].first) //头插
{
p->next = pk;
G->g[s_i].first = p;
}
else
{
pr->next = p;
p->next = pk;
}
}
//过滤掉\n
getchar();
}
return G;
}