图的表示
2016-12-10 15:23
281 查看
1 邻接矩阵 ,对于N个点的图,需要N×N的矩阵表示点与点之间是否有边的存在。这种表示法的缺点是浪费空间,尤其是对于N×N的矩阵是稀疏矩阵,即边的数目远远小于N×N的时候,浪费了巨大的存储空间
2 邻接链表,对于任何一个node A,外挂一个邻接链表,如果存在 A->X这样的边,就将X链入链表。 这种表示方法的优点是节省空间,缺点是所有链表都存在的缺点,地址空间的不连续造成缓存命中降低,性能有不如临界矩阵这样的数组。
#ifndef __GRAPH_H__
#define __GRAPH_H__
typedef struct graph *Graph;
Graph graph_create(int n);
void graph_destroy(Graph);
void graph_add_edge(Graph, int source, int sink);
int graph_vertex_count(Graph);
int graph_edge_count(Graph);
int graph_out_degree(Graph, int source);
int graph_has_edge(Graph, int source, int sink);
void graph_foreach(Graph g, int source,
void (*f)(Graph g, int source, int sink, void *data),
void *data);
#endif
#include <stdlib.h>
#include <assert.h>
#include "graph.h"
/* basic directed graph type */
/* the implementation uses adjacency lists
* represented as variable-length arrays */
/* these arrays may or may not be sorted: if one gets long enough
* and you call graph_has_edge on its source, it will be */
struct graph {
int n; /* number of vertices */
int m; /* number of edges */
struct successors {
int d; /* number of successors */
int len; /* number of slots in array */
char is_sorted; /* true if list is already sorted */
int list[1];
/* actual list of successors */
} *alist[1];
};
/* create a new graph with n vertices labeled 0..n-1 and no edges */
Graph graph_create(int n)
{
Graph g;
int i;
g = malloc(sizeof(struct graph) + sizeof(struct successors *) * (n-1));
assert(g);
g->n = n;
g->m = 0;
for(i = 0; i < n; i++) {
g->alist[i] = malloc(sizeof(struct successors));
assert(g->alist[i]);
g->alist[i]->d = 0;
g->alist[i]->len = 1;
g->alist[i]->is_sorted= 1;
}
return g;
}
/* free all space used by graph */
void graph_destroy(Graph g)
{
int i;
for(i = 0; i < g->n; i++) free(g->alist[i]);
free(g);
}
/* add an edge to an existing graph */
void graph_add_edge(Graph g, int u, int v)
{
assert(u >= 0);
assert(u < g->n);
assert(v >= 0);
assert(v < g->n);
/* do we need to grow the list? */
while(g->alist[u]->d >= g->alist[u]->len) {
g->alist[u]->len *= 2;
g->alist[u] =
realloc(g->alist[u],
sizeof(struct successors) + sizeof(int) * (g->alist[u]->len - 1));
}
/* now add the new sink */
g->alist[u]->list[g->alist[u]->d++] = v;
g->alist[u]->is_sorted = 0;
/* bump edge count */
g->m++;
}
/* return the number of vertices in the graph */
int graph_vertex_count(Graph g)
{
return g->n;
}
/* return the number of vertices in the graph */
int graph_edge_count(Graph g)
{
return g->m;
}
/* return the out-degree of a vertex */
int graph_out_degree(Graph g, int source)
{
assert(source >= 0);
assert(source < g->n);
return g->alist[source]->d;
}
/* when we are willing to call bsearch */
#define BSEARCH_THRESHOLD (10)
static int
intcmp(const void *a, const void *b)
{
return *((const int *) a) - *((const int *) b);
}
/* return 1 if edge (source, sink) exists), 0 otherwise */
int graph_has_edge(Graph g, int source, int sink)
{
int i;
assert(source >= 0);
assert(source < g->n);
assert(sink >= 0);
assert(sink < g->n);
if(graph_out_degree(g, source) >= BSEARCH_THRESHOLD) {
/* make sure it is sorted */
if(! g->alist[source]->is_sorted) {
qsort(g->alist[source]->list,
g->alist[source]->d,
sizeof(int),
intcmp);
}
/* call bsearch to do binary search for us */
return
bsearch(&sink,
g->alist[source]->list,
g->alist[source]->d,
sizeof(int),
intcmp)
!= 0;
} else {
/* just do a simple linear search */
/* we could call lfind for this, but why bother? */
for(i = 0; i < g->alist[source]->d; i++) {
if(g->alist[source]->list[i] == sink) return 1;
}
/* else */
return 0;
}
}
/* invoke f on all edges (u,v) with source u */
/* supplying data as final parameter to f */
void graph_foreach(Graph g, int source,
void (*f)(Graph g, int source, int sink, void *data),
void *data)
{
int i;
assert(source >= 0);
assert(source < g->n);
for(i = 0; i < g->alist[source]->d; i++) {
f(g, source, g->alist[source]->list[i], data);
}
}
除此外,graph_foreach这种思想也很不错啊。Lisp这种传递函数作用到每一个元素上的方法,相当于Lisp中的mapcar,让人不仅拍案叫绝,很容易就能扩展出很好的功能。(当然也不是完全没瑕疵,比如realloc没有判断失败的情景,白璧微瑕)
既然是图的表示,我们当然很希望能够看到可视化的图。Land of Lisp一书中,学到了Linux下的neato 命令。 Linux下有工具帮助生成图的图片,可以满足我们可视化的需求。
测试代码
当然了,也可以用 dot命令绘制出PNG格式的图片来:
我们可以看到在当前目录下产生了一个文件名为graph_test.png的PNG格式的图片。打开看下和上面的图是一致的。我就不贴图了。
对dot感兴趣的筒子,可以继续阅读『dot 学习笔记』二. 记录 Shell 的变迁
参考文献:
1 Land of Lisp
2 PineWiki
3 算法:C语言实现。
2 邻接链表,对于任何一个node A,外挂一个邻接链表,如果存在 A->X这样的边,就将X链入链表。 这种表示方法的优点是节省空间,缺点是所有链表都存在的缺点,地址空间的不连续造成缓存命中降低,性能有不如临界矩阵这样的数组。
#ifndef __GRAPH_H__
#define __GRAPH_H__
typedef struct graph *Graph;
Graph graph_create(int n);
void graph_destroy(Graph);
void graph_add_edge(Graph, int source, int sink);
int graph_vertex_count(Graph);
int graph_edge_count(Graph);
int graph_out_degree(Graph, int source);
int graph_has_edge(Graph, int source, int sink);
void graph_foreach(Graph g, int source,
void (*f)(Graph g, int source, int sink, void *data),
void *data);
#endif
#include <stdlib.h>
#include <assert.h>
#include "graph.h"
/* basic directed graph type */
/* the implementation uses adjacency lists
* represented as variable-length arrays */
/* these arrays may or may not be sorted: if one gets long enough
* and you call graph_has_edge on its source, it will be */
struct graph {
int n; /* number of vertices */
int m; /* number of edges */
struct successors {
int d; /* number of successors */
int len; /* number of slots in array */
char is_sorted; /* true if list is already sorted */
int list[1];
/* actual list of successors */
} *alist[1];
};
/* create a new graph with n vertices labeled 0..n-1 and no edges */
Graph graph_create(int n)
{
Graph g;
int i;
g = malloc(sizeof(struct graph) + sizeof(struct successors *) * (n-1));
assert(g);
g->n = n;
g->m = 0;
for(i = 0; i < n; i++) {
g->alist[i] = malloc(sizeof(struct successors));
assert(g->alist[i]);
g->alist[i]->d = 0;
g->alist[i]->len = 1;
g->alist[i]->is_sorted= 1;
}
return g;
}
/* free all space used by graph */
void graph_destroy(Graph g)
{
int i;
for(i = 0; i < g->n; i++) free(g->alist[i]);
free(g);
}
/* add an edge to an existing graph */
void graph_add_edge(Graph g, int u, int v)
{
assert(u >= 0);
assert(u < g->n);
assert(v >= 0);
assert(v < g->n);
/* do we need to grow the list? */
while(g->alist[u]->d >= g->alist[u]->len) {
g->alist[u]->len *= 2;
g->alist[u] =
realloc(g->alist[u],
sizeof(struct successors) + sizeof(int) * (g->alist[u]->len - 1));
}
/* now add the new sink */
g->alist[u]->list[g->alist[u]->d++] = v;
g->alist[u]->is_sorted = 0;
/* bump edge count */
g->m++;
}
/* return the number of vertices in the graph */
int graph_vertex_count(Graph g)
{
return g->n;
}
/* return the number of vertices in the graph */
int graph_edge_count(Graph g)
{
return g->m;
}
/* return the out-degree of a vertex */
int graph_out_degree(Graph g, int source)
{
assert(source >= 0);
assert(source < g->n);
return g->alist[source]->d;
}
/* when we are willing to call bsearch */
#define BSEARCH_THRESHOLD (10)
static int
intcmp(const void *a, const void *b)
{
return *((const int *) a) - *((const int *) b);
}
/* return 1 if edge (source, sink) exists), 0 otherwise */
int graph_has_edge(Graph g, int source, int sink)
{
int i;
assert(source >= 0);
assert(source < g->n);
assert(sink >= 0);
assert(sink < g->n);
if(graph_out_degree(g, source) >= BSEARCH_THRESHOLD) {
/* make sure it is sorted */
if(! g->alist[source]->is_sorted) {
qsort(g->alist[source]->list,
g->alist[source]->d,
sizeof(int),
intcmp);
}
/* call bsearch to do binary search for us */
return
bsearch(&sink,
g->alist[source]->list,
g->alist[source]->d,
sizeof(int),
intcmp)
!= 0;
} else {
/* just do a simple linear search */
/* we could call lfind for this, but why bother? */
for(i = 0; i < g->alist[source]->d; i++) {
if(g->alist[source]->list[i] == sink) return 1;
}
/* else */
return 0;
}
}
/* invoke f on all edges (u,v) with source u */
/* supplying data as final parameter to f */
void graph_foreach(Graph g, int source,
void (*f)(Graph g, int source, int sink, void *data),
void *data)
{
int i;
assert(source >= 0);
assert(source < g->n);
for(i = 0; i < g->alist[source]->d; i++) {
f(g, source, g->alist[source]->list[i], data);
}
}
除此外,graph_foreach这种思想也很不错啊。Lisp这种传递函数作用到每一个元素上的方法,相当于Lisp中的mapcar,让人不仅拍案叫绝,很容易就能扩展出很好的功能。(当然也不是完全没瑕疵,比如realloc没有判断失败的情景,白璧微瑕)
既然是图的表示,我们当然很希望能够看到可视化的图。Land of Lisp一书中,学到了Linux下的neato 命令。 Linux下有工具帮助生成图的图片,可以满足我们可视化的需求。
测试代码
#include <stdio.h> #include <assert.h> #include "graph.h" #define TEST_SIZE (6) static void match_sink(Graph g, int source, int sink, void *data) { assert(data && sink == *((int *) data)); } static int node2dot(Graph g) { assert(g != NULL); return 0; } static void print_edge2dot(Graph g,int source, int sink, void *data) { fprintf(stdout,"%d->%d;n",source,sink); } static int edge2dot(Graph g) { assert( NULL); int idx = 0; int node_cnt = graph_vertex_count(g); for(idx = 0;idx<node_cnt; idx++) { graph_foreach(g,idx,print_edge2dot,NULL); } return 0; } int graph2dot(Graph g) { fprintf(stdout,"digraph{"); node2dot(g); edge2dot(g); fprintf(stdout,"}n"); return 0; } int main(int argc, char **argv) { Graph g; int i; int j; g = graph_create(TEST_SIZE); assert(graph_vertex_count(g) == TEST_SIZE); /* check it's empty */ for(i = 0; i < TEST_SIZE; i++) { for(j = 0; j < TEST_SIZE; j++) { assert(graph_has_edge(g, i, j) == 0); } } /* check it's empty again */ for(i = 0; i < TEST_SIZE; i++) { assert(graph_out_degree(g, i) == 0); graph_foreach(g, i, match_sink, 0); } /* check edge count */ assert(graph_edge_count(g) == 0); for(i = 0; i < TEST_SIZE; i++) { for(j = 0; j < TEST_SIZE; j++) { if(i < j) graph_add_edge(g, i, j); } } for(i = 0; i < TEST_SIZE; i++) { for(j = 0; j < TEST_SIZE; j++) { assert(graph_has_edge(g, i, j) == (i < j)); } } assert(graph_edge_count(g) == (TEST_SIZE*(TEST_SIZE-1)/2)); graph2dot(g); /* free it * */ graph_destroy(g); return 0; }我们这个测试程序基本测试了graph的API,同时利用graph_foreach函数的高效扩展性,输出了dot文件格式的文件我们看下执行结果。
root@manu:~/code/c/self/graph_2# gcc -o test generate_graph.c graph.c root@manu:~/code/c/self/graph_2# ./test >test.dot在我的Ubuntu下面用XDot可以看到test.dot已经是个图形文件了。图形如下:
当然了,也可以用 dot命令绘制出PNG格式的图片来:
我们可以看到在当前目录下产生了一个文件名为graph_test.png的PNG格式的图片。打开看下和上面的图是一致的。我就不贴图了。
对dot感兴趣的筒子,可以继续阅读『dot 学习笔记』二. 记录 Shell 的变迁
参考文献:
1 Land of Lisp
2 PineWiki
3 算法:C语言实现。
相关文章推荐
- 图论——读书笔记(图的表示即广度优先搜索)
- 数据结构(5):循环队列——队列的顺序表示和实现
- 树的定义以及表示
- POJ 1509 Glass Beads(字符串的最小表示法)
- 稀疏表示
- 神经网络与深度学习笔记——3.2神经网络表示
- char能表示(-128~127)
- IP地址0.0.0.0表示什么
- 【备战蓝桥杯】【递归】【C语言】【ALGO-95算法训练 2的次幂表示】
- Ruby中的%表示法
- 数据结构 用单链表表示集合的并交差运算
- 容器的进阶用法 表示分支即为‘行列’ 和字典的用法
- IEC61850之TrgOps报告触发选项各bit位表示含义
- 线性表的链式表示和实现----循环(单)链表
- 无根树的同构:Hash最小表示法(bzoj 4337: BJOI2015 树的同构)
- hdu3374(最小表示法+KMP)
- 为什么Windows 系统中用反斜杠表示路径,而C语言教材中都是用斜杠表示头文件的路径?
- ps -aux命令显示的状态列中表示的意思
- __user表示是一个user mode的pointer,所以kernel不可能直接使用。
- 稀疏表示