1、图作为一种模型来定义对象之间的关系。对象由顶点表示,而对象之间的关系则通过顶点之间的边来表示。
2、图分为有向图和无向图,有向图的边叫做弧。
3、图的表达式:G = (V, E),E包含有序对(u,v),对于无向图无所谓(u,v)和(v,u)。
如上图,有向图:V={v0, v1, v2, v3}. E={(v0,v1), (v2, v0), (v2, v3), (v3, v0)}.
无向图:V={v0, v1, v2, v3,v4}.E={(v0, v1), (v0, v3),(v1, v2), (v1, v4), (v2, v3), (v2, v4)}
4、图中两个重要关系:邻接、关联
邻接:有向图中(v0, v1)说明v1邻接v0。
完全图:一个图中每一个顶点都与其他顶点相邻接
关联:有向图中(v0, v1)说明边(v0, v1)从顶点v0关联到v1,顶点和边的关系。
5、顶点的入度:以该顶点为终点的边数。
顶点的出度:以该顶点为起点的边数。
简单路径:没有重复顶点的路径
环:路径包含相同的节点两次或以上,即从某点出发最后能返回到某点。
6、连通:每个顶点都能通过某条路径到达其他顶点。
关节点:移除某个顶点将使图或某分支失去连通性。
桥:移除某条边使得图失去连通性。
7、邻接表适用于稀疏图;邻接矩阵适用于稠密图
8、搜索方法:广度优先搜索、深度优先搜索
深度优先搜索
9、代码实现
(1)数据结构:每个顶点对应一个链表节点,节点中*data指向节点结构AdjList
//AdjList包含:顶点;与该顶点相连接的一个顶点集合。这是每个链表节点指向的数据data
typedef struct AdjList_{
void *vertex;
Set adjacent;
}AdjList;
//整个图的属性,包含:节点数、边数、所有节点组成的链表属性
typedef struct Graph_{
int vcount;
int ecount;
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
List adjlists;
}Graph;
typedef enum VertexColor_{
white, gray, black
}VertexColor;
(2)初始化
void graph_init(Graph *graph, int (*match)(const void *key1, const void *key2), void(*destroy)(void *data))
{
graph->vcount = 0;
graph->ecount = 0;
graph->match = match;
graph->destroy = destroy;
list_init(graph->adjlists, 0, NULL, NULL, NULL);
return;
}
(3)删除整个图
void graph_destroy(Graph *graph)
{
AdjList *adjlist;
while(list_size(&graph->adjlists) > 0)
{
if(list_rem_next(&graph->adjlists, NULL, (void **)&adjlist) == 0)
{
//删除每个顶点的邻节点集合
set_destroy(&adjlist->adjacent);
//删除该顶点,这两者就是链表节点指向的*data
if(graph->destroy != NULL)
graph->destroy(adjlist->vertex);
//释放*data指向结构自身的空间,上面为该空间指向的内容
free(adjlist);
}
}
}
(4)插入一个顶点:申请一个AdjList,其中成员vertex为data,其边集set初始化为NULL
//这里入口参数*data对应 *adjlist中vertex
int graph_ins_vertex(Graph *graph, const void *data)
{
LIST_ELEMENT *element;
AdjList *adjlist;
int retval;
for(element = list_tail(graph->adjlists); element != NULL; element = list_next(element))
{
if(graph->match(data, ((AdjList *)list_data(element))->vertex))
return 1;
}
if((adjlist = (AdjList *)malloc(sizeof(AdjList))) == NULL)
return -1;
adjlist->vertex = (void *)data;
set_init(&adjlist->adjacent, graph->match, NULL);
if((retval = list_ins_next(&graph->adjlists, list_tail(&graph->adjlists), adjlist)) != 0)
return retval;
graph->vcount++;
return 0;
}
(5)插入一条边
//在图中加入一条连线
//对于图中已存在顶点data1和data2,单向data1指向data2,就是在顶点data1的集合中加入data2
int graph_ins_edge(Graph *graph, const void *data1, const *data2)
{
LIST_ELEMENT *element;
int retval;
//先找到顶点data2和data1
for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
{
if(graph->match(data1, ((AdjList *)list_data(element))->vertex))
break;
}
if(element == NULL)
return -1;
//向data1的集合中插入data2
if((retval = set_insert( &( (AdjList *)(list_data(element))->adjacent ), data2)) != 0 )
{
return retval;
}
graph->ecount++;
}
(6)删除顶点,顶点不能和其他任何顶点相连,即不在其他节点的集合中,自己的集合为空。
int graph_rem_vertex(Graph *graph, void **data)
{
LIST_ELEMENT *element;
AdjList *adjlist;
int found;
prev = NULL;
found = 0;
for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
{
if(set_is_mumber(&( (AdjList *)(list_data(element))->adjacent ), *data))
return -1;
if(graph->match(*data,&( (AdjList *)(list_data(element))->vertex ))
{
temp = element;
fount =1;
}
//此处在for内部,如果没发现则prev一直在更新
if(!found)
prev = element;
}
if(!found)
return -1;
if(set_size( &((AdjList *)list_data(temp))->adjacent )> 0)
return -1;
*data = adjlist->vertex;
//此时节点集合为空可以直接释放
free(adjlist);
graph->vcount--;
return 0;
}
(7)删除边,即删除data1集合中的元素data2
int graph_rem_edge(Graph *graph, const void *data1, const *data2)
{
LIST_ELEMENT *element;
//先找到顶点data1
for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
{
if(graph->match(data1, ((AdjList *)list_data(element))->vertex))
break;
}
if(element == NULL)
return -1;
if(set_remove(&( (AdjList *)(list_data(element))->adjacent ), *data2)) != 0)
return -1;
graph->ecount--;
return 0;
}
(8)找到某个顶点对应的结构adjlist
//data对应的结构返回到adjlist
int graph_adjlist(const Graph *graph, const void *data, AdjList **adjlist)
{
LIST_ELEMENT *element, *prev;
prev = NULL;
for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
{
if(graph->match(*data,&( (AdjList *)(list_data(element))->vertex ))
{
break;
}
prev = element;
}
if(element == NULL)
return -1;
*adjlist = list_data(element);
return 0;
}
(9)判断顶点data1和data2是否有邻接关系,即判断data2是否存在于data1的集合中
int graph_is_adjacent(const Graph *graph, const void *data1, const void *data2)
{
LIST_ELEMENT *element, *prev;
prev = NULL;
for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
{
if(graph->match(*data,&( (AdjList *)(list_data(element))->vertex ))
{
break;
}
prev = element;
}
if(element == NULL)
return -1;
return set_is_mumber(&( (AdjList *)(list_data(element))->adjacent ), data2);
}