**权和网:**如果图中的顶点到另一个顶点需要代价(距离或耗费),那么在表示边或弧的时候需要附加数据,附加的数据就叫做权,带权的图通常被称为网,这也是互联网的由来。
**子图:**假定有两个图G1和G2,如果G1的顶点集合是G2的顶点集合的子集,且G1的边或弧集合是G2的边或弧集合的子集,则称G1是G2的子图。
**邻接点:**在无向图中如果有一条边(v,w),则v,w两个顶点互为邻接点,即v和w相邻接,边(v,w)依附于顶点v,w,或者说(v,w)和顶点v、w相关联。
**顶点v的度:**在无向图中与顶点v相关联的边的数量。
**顶点v的入度和出度:**在有向图中,以顶点v作为弧头的弧的数量称为顶点的入度,以顶点v作为弧尾的弧的数量称为顶点的出度。
**路径:**从顶点v到达顶点w所经历的顶点序叫做路径,路径的长度就是边或弧的数目。
**回路或环:**起始点和终点相同的路径称为回路或环。
**简单路径:**路径中顶点不重复出现的路径称为简单路径。
**简单回路或简单环:**起始点和终点相同且其余顶点不重复出现,被称为简单回路或简单环。
**连通图:**在无向图中,从顶点v到顶点w有路径,则称v和w是连通的,如果图中任意两个顶点都是连通的,则称图为连通图。
**连通分量:**G1和G2都是连通图,且G1是G2的子图,则称G1是G2的连通分量或极大连通子图。
强连通图:在有向图中,如果任意一对顶点都双向连通,则称图为强连通图。
强连通分量:G1和G2都是强连通图,且G1是G2的子图,则称G1是G2的强连通分量或极大强连通子图。
三、图的存储结构:
邻接矩阵:
使用两个数组来存储数据元素(顶点)和数据元素之间的关系(边或弧)的信息。
一维数组:用于存储顶点。
二维数组:用于存储弧或边。
邻接矩阵的优点:
访问速度快,方便计算结点的度、入度、出度。
邻接矩阵的缺点:
顶点的容量有限,扩容非常麻烦,只适合存储稠密图,存储稀疏图时会有大量的内存浪费。
#include
#include
#include
typedef struct Grahh
{
size_t cal;
size_t cnt;
char *vertex;
char **edge;
} Graph;
Graph *create_graph(size_t cal)
{
Graph *graph = malloc(sizeof(Graph));
graph->vertex = malloc(sizeof(char) * cal);
graph->cal = cal;
graph->cnt = 0;
graph->edge = malloc(sizeof(char *) * cal);
for (int i = 0; i < cal; i++)
{
graph->edge[i] = calloc(sizeof(char), cal);
}
return graph;
}
bool add_vertex_graph(Graph *graph, char vertex)
{
if (graph->cnt >= graph->cal)
return false;
for (int i = 0; i < graph->cnt; i++)
{
if (graph->vertex[i] == vertex)
return false;
}
graph->vertex[graph->cnt++] = vertex;
return true;
}
bool add_edge_graph(Graph *graph, char v1, char v2)
{
int v1_index = -1, v2_index = -1;
for (int i = 0; i < graph->cnt; i++)
{
if (graph->vertex[i] == v1)
v1_index = i;
if (graph->vertex[i] == v2)
v2_index = i;
}
if (-1 == v1_index || -1 == v2_index || v1_index == v2_index)
return false;
graph->edge[v1_index][v2_index] = 1;
graph->edge[v2_index][v1_index] = 1;
return true;
}
int query_vertex_graph(Graph *graph, char vertex)
{
for (int i = 0; i < graph->cnt; i++)
{
if (vertex == graph->vertex[i])
return i;
}
return -1;
}
char first_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return '\0';
for (int i = 0; i < graph->cnt; i++)
{
if (1 == graph->edge[index][i])
return graph->vertex[i];
}
return '\0';
}
bool del_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return false;
for (int i = 0; i < graph->cnt; i++)
{
graph->edge[index][i] = 0;
graph->edge[i][index] = 0;
}
return true;
}
void _dfs_graph(Graph *graph, int index, bool *flags)
{
if (!flags[index])
return;
printf("%c\n", graph->vertex[index]);
flags[index] = false;
for (int i = 0; i < graph->cnt; i++)
{
if (1 == graph->edge[index][i])
_dfs_graph(graph, i, flags);
}
}
void dfs_graph(Graph *graph)
{
bool flags[graph->cnt];
for (int i = 0; i < graph->cnt; i++)
flags[i] = true;
for (int i = 0; i < graph->cnt; i++)
_dfs_graph(graph, i, flags);
}
void bfs_graph(Graph *graph)
{
bool flags[graph->cnt];
for (int i = 0; i < graph->cnt; i++)
flags[i] = true;
int queue[graph->cnt], front = 0, rear = 0;
for (int i = 0; i < graph->cnt; i++)
{
if (flags[i])
{
flags[i] = false;
queue[rear++] = i;
}
while (front != rear)
{
int index = queue[front++];
printf("%c ", graph->vertex[index]);
for (int j = 0; j < graph->cnt; j++)
{
if (1 == graph->edge[index][j] && flags[j])
{
flags[j] = false;
queue[rear++] = j;
}
}
}
}
}
void destroy_graph(Graph *graph)
{
for (int i = 0; i < graph->cnt; i++)
{
free(graph->edge[i]);
}
free(graph->edge);
free(graph->vertex);
free(graph);
}
int main(int argc, const char *argv[])
{
Graph *graph = create_graph(5);
add_vertex_graph(graph, 'A');
add_vertex_graph(graph, 'B');
add_vertex_graph(graph, 'C');
add_vertex_graph(graph, 'D');
add_vertex_graph(graph, 'E');
add_edge_graph(graph, 'A', 'C');
add_edge_graph(graph, 'A', 'D');
add_edge_graph(graph, 'C', 'B');
add_edge_graph(graph, 'B', 'D');
printf("------------------------\n");
for (int i = 0; i < graph->cnt; i++)
{
for (int j = 0; j < graph->cnt; j++)
{
printf("%hhd ", graph->edge[i][j]);
}
printf("\n");
}
bfs_graph(graph);
return 0;
}
邻接表:
采用一维数组加单向链表的方式存储存储。
一维数组:用于存储顶点和单向链表的头指针。
单向链表:存储着从该顶点出发的弧或边。
邻接表优点:
与邻接矩阵相比,它可以节约内存,有多少条边或弧就创建多少个链表节点,比较适合存储稀疏图。
邻接表缺点:
计算入度、删除节点时比较麻烦。
#include
#include
#include
typedef struct Bow
{
int dest;
struct Bow *next;
} Bow;
typedef struct Vertex
{
char data;
Bow first;
} Vertex;
typedef struct Graph
{
Vertex *vertex;
size_t cal;
size_t cnt;
} Graph;
Graph *create_graph(size_t cal)
{
Graph *graph = malloc(sizeof(Graph));
graph->vertex = malloc(sizeof(Vertex) * cal);
graph->cal = cal;
graph->cnt = 0;
return graph;
}
bool add_vertex_graph(Graph *graph, char vertex)
{
if (graph->cnt >= graph->cal)
return false;
for (int i = 0; i < graph->cnt; i++)
if (vertex == graph->vertex[i].data)
return false;
graph->vertex[graph->cnt].data = vertex;
graph->vertex[graph->cnt++].first.next = NULL;
return true;
}
int query_vertex_graph(Graph *graph, char vertex)
{
for (int i = 0; i < graph->cnt; i++)
{
if (graph->vertex[i].data == vertex)
return i;
}
return -1;
}
bool add_bow_graph(Graph *graph, char bow_tail, char bow_head)
{
int index_tail = query_vertex_graph(graph, bow_tail);
if (-1 == index_tail)
return false;
int index_head = query_vertex_graph(graph, bow_head);
if (-1 == index_head)
return false;
Bow *bow = malloc(sizeof(Bow));
bow->dest = index_head;
bow->next = graph->vertex[index_tail].first.next;
graph->vertex[index_tail].first.next = bow;
return true;
}
bool del_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return false;
if (NULL != graph->vertex[index].first.next)
{
Bow *bow = graph->vertex[index].first.next;
graph->vertex[index].first.next = bow->next;
free(bow);
}
graph->vertex[index].data = '\0';
for (int i = 0; i < graph->cnt; i++)
{
for (Bow *bow = &graph->vertex[i].first; NULL != bow->next; bow = bow->next)
{
if (index == bow->next->dest)
{
Bow *tmp = bow->next;
bow->next = tmp->next;
free(tmp);
}
}
}
return true;
}
int indegree_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return -1;
int indegree = 0;
for (int i = 0; i < graph->cnt; i++)
{
Bow *bow = graph->vertex[i].first.next;
while (NULL != bow)
{
if (index == bow->dest)
indegree++;
bow = bow->next;
}
}
return indegree;
}
int outdegree_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return -1;
int outdegree = 0;
Bow *bow = graph->vertex[index].first.next;
while (NULL != bow)
{
outdegree++;
bow = bow->next;
}
return outdegree;
}
void _dfs_graph(Graph *graph, int index, bool *flags)
{
flags[index] = false;
printf("%c ", graph->vertex[index].data);
Bow *bow = graph->vertex[index].first.next;
while (NULL != bow)
{
if (flags[bow->dest])
_dfs_graph(graph, bow->dest, flags);
bow = bow->next;
}
}
void dfs_graph(Graph *graph)
{
bool flags[graph->cnt];
for (int i = 0; i < graph->cnt; i++)
flags[i] = true;
for (int i = 0; i < graph->cnt; i++)
if (flags[i])
_dfs_graph(graph, i, flags);
printf("\n");
}
void bfs_graph(Graph *graph)
{
bool flags[graph->cnt];
for (int i = 0; i < graph->cnt; i++)
flags[i] = true;
int queue[graph->cnt], front = 0, rear = 0;
for (int i = 0; i < graph->cnt; i++)
{
if (flags[i])
{
flags[i] = false;
queue[rear++] = i;
}
while (front != rear)
{
int index = queue[front++];
printf("%c ", graph->vertex[index].data);
Bow *bow = graph->vertex[index].first.next;
while (NULL != bow)
{
if (flags[bow->dest])
{
flags[bow->dest] = false;
queue[rear++] = bow->dest;
}
bow = bow->next;
}
}
}
printf("\n");
}
void destroy_graph(Graph *graph)
{
for (int i = 0; i < graph->cnt; i++)
{
Bow *bow = &graph->vertex[i].first;
while (NULL != bow->next)
{
Bow *tmp = bow->next;
bow->next = tmp->next;
free(tmp);
}
}
free(graph->vertex);
free(graph);
}
int main(int argc, const char *argv[])
{
Graph *graph = create_graph(10);
add_vertex_graph(graph, 'A');
add_vertex_graph(graph, 'B');
add_vertex_graph(graph, 'C');
add_vertex_graph(graph, 'D');
add_vertex_graph(graph, 'E');
add_bow_graph(graph, 'A', 'D');
add_bow_graph(graph, 'A', 'E');
add_bow_graph(graph, 'A', 'C');
add_bow_graph(graph, 'C', 'B');
add_bow_graph(graph, 'B', 'D');
dfs_graph(graph);
bfs_graph(graph);
destroy_graph(graph);
return 0;
}
十字链表:
采用一维数组加两个十字链表进行存储,之所以叫十字链表,是因为它的链表弧节点会被两个链表指向,同时弧节点有两个指针,指向后续的弧节点。
一维数组:存储了顶点数据,两个链表头指针。
出度链表:指向的是以该结点作为弧尾的弧节点。
入度链表:指向的是以该结点作为弧头的弧节点。
十字链表的优点:
与邻接矩阵相比,它更节约内存,与邻接表相比它更方便计算顶点的出度。
十字链表的缺点:
删除顶点和删除弧时比较麻烦,也可以表示与实现无向图,但更合适表示与实现有向图。
#include
#include
#include
typedef struct Bow
{
int tailvex;
int headvex;
struct Bow *tlink;
struct Bow *hlink;
} Bow;
typedef struct Vertex
{
char data;
Bow *inlink;
Bow *outlink;
} Vertex;
typedef struct Graph
{
Vertex *vertex;
size_t cal;
size_t cnt;
} Graph;
Graph *create_graph(size_t cal)
{
Graph *graph = malloc(sizeof(Graph));
graph->vertex = malloc(sizeof(Vertex) * cal);
graph->cal = cal;
graph->cnt = 0;
return graph;
}
int query_vertex_graph(Graph *graph, char vertex)
{
for (int i = 0; i < graph->cnt; i++)
if (vertex == graph->vertex[i].data)
return i;
return -1;
}
bool add_vertex_graph(Graph *graph, char vertex)
{
if (graph->cnt >= graph->cal || -1 != query_vertex_graph(graph, vertex))
return false;
graph->vertex[graph->cnt].data = vertex;
graph->vertex[graph->cnt].inlink = NULL;
graph->vertex[graph->cnt++].outlink = NULL;
return true;
}
bool add_bow_graph(Graph *graph, char tailvex, char headvex)
{
int tailindex = query_vertex_graph(graph, tailvex);
int headindex = query_vertex_graph(graph, headvex);
if (-1 == tailindex || -1 == headindex)
return false;
Bow *bow = malloc(sizeof(Bow));
bow->tailvex = tailindex;
bow->headvex = headindex;
bow->tlink = graph->vertex[tailindex].outlink;
graph->vertex[tailindex].outlink = bow;
bow->hlink = graph->vertex[headindex].inlink;
graph->vertex[headindex].inlink = bow;
return true;
}
bool del_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return false;
graph->vertex[index].inlink = NULL;
for (int i = 0; i < graph->cnt; i++)
{
Bow *out = graph->vertex[i].outlink;
while (NULL != out && NULL != out->tlink)
{
if (out->tlink->headvex == index)
{
Bow *tmp = out->tlink;
out->tlink = tmp->tlink;
free(tmp);
}
out = out->tlink;
}
if (NULL != graph->vertex[i].outlink &&
index == graph->vertex[i].outlink->headvex)
{
Bow *tmp = graph->vertex[i].outlink;
graph->vertex[i].outlink = tmp->tlink;
free(tmp);
}
}
return true;
}
bool del_bow_graph(Graph *graph, char tailvex, char headvex)
{
int tailindex = query_vertex_graph(graph, tailvex);
int headindex = query_vertex_graph(graph, headvex);
if (-1 == tailindex || -1 == headindex)
return false;
Bow *out = graph->vertex[tailindex].outlink;
while (NULL != out && NULL != out->tlink)
{
if (out->tlink->headvex == headindex)
{
out->tlink = out->tlink->tlink;
break;
}
out = out->tlink;
}
if (NULL != graph->vertex[tailindex].outlink &&
headindex == graph->vertex[tailindex].outlink->headvex)
{
graph->vertex[tailindex].outlink = graph->vertex[tailindex].outlink->tlink;
}
Bow *in = graph->vertex[headindex].inlink;
while (NULL != in && NULL != in->hlink)
{
if (in->hlink->tailvex == tailindex)
{
Bow *tmp = in->hlink;
in->hlink = tmp->hlink;
free(tmp);
break;
}
in = in->hlink;
}
if (NULL != graph->vertex[headindex].outlink &&
tailindex == graph->vertex[headindex].outlink->tailvex)
{
Bow *tmp = graph->vertex[headindex].inlink;
graph->vertex[headindex].inlink = tmp->hlink;
free(tmp);
}
return true;
}
int indegree_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return -1;
int indegree = 0;
Bow *in = graph->vertex[index].inlink;
while (NULL != in)
{
indegree++;
in = in->hlink;
}
return indegree;
}
int outdegree_vertex_graph(Graph *graph, char vertex)
{
int index = query_vertex_graph(graph, vertex);
if (-1 == index)
return -1;
int outdegree = 0;
Bow *out = graph->vertex[index].outlink;
while (NULL != out)
{
outdegree++;
out = out->tlink;
}
return outdegree;
}
void _dfs_graph(Graph *graph, int index, bool *flags)
{
printf("%c ", graph->vertex[index].data);
flags[index] = false;
Bow *out = graph->vertex[index].outlink;
while (NULL != out)
{
if (flags[out->headvex])
_dfs_graph(graph, out->headvex, flags);
out = out->tlink;
}
}
void dfs_graph(Graph *graph)
{
bool flags[graph->cnt];
for (int i = 0; i < graph->cnt; i++)
flags[i] = true;
for (int i = 0; i < graph->cnt; i++)
if (flags[i])
_dfs_graph(graph, i, flags);
printf("\n");
}
void bfs_graph(Graph *graph)
{
bool flags[graph->cnt];
for (int i = 0; i < graph->cnt; i++)
flags[i] = true;
int queue[graph->cnt], front = 0, rear = 0;
for (int i = 0; i < graph->cnt; i++)
{
if (flags[i])
{
queue[rear++] = i;
flags[i] = false;
}
while (front != rear)
{
int index = queue[front++];
printf("%c ", graph->vertex[index].data);
Bow *out = graph->vertex[index].outlink;
while (NULL != out)
{
if (flags[out->headvex])
{
queue[rear++] = out->headvex;
flags[out->headvex] = false;
}
out = out->tlink;
}
}
}
printf("\n");
}
void destroy_graph(Graph *graph)
{
for (int i = 0; i < graph->cnt; i++)
del_vertex_graph(graph, graph->vertex[i].data);
free(graph->vertex);
free(graph);
}
int main(int argc, const char *argv[])
{
Graph *graph = create_graph(10);
add_vertex_graph(graph, 'A');
add_vertex_graph(graph, 'B');
add_vertex_graph(graph, 'C');
add_vertex_graph(graph, 'D');
add_vertex_graph(graph, 'E');
add_bow_graph(graph, 'A', 'D');
add_bow_graph(graph, 'A', 'E');
add_bow_graph(graph, 'A', 'C');
add_bow_graph(graph, 'C', 'B');
add_bow_graph(graph, 'B', 'D');
add_bow_graph(graph, 'E', 'D');
dfs_graph(graph);
bfs_graph(graph);
destroy_graph(graph);
return 0;
}
邻接多重表:
是对邻接表一种扩展,在邻接表边的基础上,增加了访问标记、组成边的两个顶点的下标、指向依附这两个顶点的下一条边的指针。
使用邻接表时,它的一条边的两个节点,分别在第i和第j的两个链表中,这给图的操作带来不便,而邻接多重表的链表节点增加各项成员,方便对图的操作。