以下内容转自http://blog.chinaunix.net/uid-24774106-id-3505579.html
其中添加了一些个人学习过程中的注释和绘图,会特别标出
下述代码均在vs2010中测试通过,成功运行
任何一本讲到图算法的算法书,都会讲到图的表示方法有两种
1 邻接矩阵 ,对于N个点的图,需要N×N的矩阵表示点与点之间是否有边的存在。这种表示法的缺点是浪费空间,尤其是对于N×N的矩阵是稀疏矩阵,即边的数目远远小于N×N的时候,浪费了巨大的存储空间。
2 邻接链表,对于任何一个node A,外挂一个邻接链表,如果存在 A-<X这样的边,就将X链入链表。 这种表示方法的优点是节省空间,缺点是所有链表都存在的缺点,地址空间的不连续造成缓存命中降低,性能有不如临界矩阵这样的数组。
一直以来,我也是觉得,鱼和熊掌不可兼得,这是无可奈何的事情。直到我看到了一份比较完美的code。他有动态分配的数组来存放邻接节点。一起欣赏下这份代码吧。年前我第一次看到这份代码的时候,激动的我晚上半天睡不着觉。平时自己写的代码,一板一眼,虽说功能无误,总少了那么几分灵气。看了C算法,也算对图的表示方法知道一些,却写不出这么优美的代码:
我以前觉得,自己大量练习联系写代码是学习编程的最好的方法,是最开但是看了这份代码后,觉得,学习前辈高人优秀的代码,是提高自己的一条捷径,对我们这些普通的coder而言,我们看代码的时间是超过写代码的时间的。阅读前辈优秀代码,会更快的提升自己的编程能力。对于初学者尤其是这样,这也是进入一个优秀的开发team的重要性,能更快的成长。
下图是自己画的这份代码的数据结构图,可以辅助理解
#ifndef __GRAPH_H__ #define __GRAPH_H__ typedef struct graph *Graph; //这里定义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
// Graph.cpp : Defines the entry point for the console application. // #include "stdafx.h" #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 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 */ }; struct graph { int n; /* number of vertices */ int m; /* number of edges */ 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 =(graph*) malloc(sizeof(struct graph) + sizeof(struct successors *) * (n-1)); //这里是n-1而不是n的原因是graph内部已经包含了一个 //successors *alist[1] assert(g); g->n = n; g->m = 0; for(i = 0; i < n; i++) { g->alist[i] =(successors*) 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) //添加一个节点u--->节点v 的边 { assert(u >= 0); assert(u < g->n); assert(v >= 0); assert(v < g->n); //u,v的必须在图的结点编号范围内 /* do we need to grow the list? */ while(g->alist[u]->d >= g->alist[u]->len) { g->alist[u]->len *= 2; g->alist[u] =(successors*) realloc(g->alist[u], sizeof(struct successors) + sizeof(struct successors) * (g->alist[u]->len - 1)); //这里原博客的语句是 //sizeof(struct successors) + sizeof(struct 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); } }
#include "stdafx.h" #include <assert.h> #include <stdlib.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(g != 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); system("pause"); return 0; }