Graph Representation
Graph Interface
Graph是Undirected的(15122定义),也就是说对于vertex A和B,BA和AB是一条edge。我们将graph表示成一个data structure,其中vertex定义为无符号整数。那么一个基本的graph interface可以如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| typedef unsigned int vertex; typedef struct graph_header *graph_t;
graph_t graph_new(unsigned int numvert); //@ensures \result != NULL;
void graph_free(graph_t G); //@requires G != NULL;
unsigned int graph_size(graph_t G); //@requires G != NULL;
bool graph_hasedge(graph_t G, vertex v, vertex w); //@requires G != NULL; //@requires v < graph_size(G) && w < graph_size(G);
void graph_addedge(graph_t G, vertex v, vertex w); //@requires G != NULL; //@requires v < graph_size(G) && w < graph_size(G); //@requires v != w && !graph_hasedge(G, v, w);
typedef struct neighbor_header *neighbors_t;
neightbors_t graph_get_neighbors(graph_t G, vertex v); //@requires G != NULL && v < graph_size(G); //@ensures \result != NULL;
bool graph_hasmore_neighbors(neighbors_t nbors); //@requires nbors != NULL;
vertex graph_next_neighbor(neighbors_t nbors); //@requires nbors != NULL; //@requires graph_hasmore_neighbors(nbors);
void graph_free_neighbors(neighbors_t nbors); //@requires nbors != NULL;
|
我们通过graph_hasmore_neighbors和graph_next_neighbors来检查或者获取某个vertex的相邻vertex。我们可以通过多种数据结构实现上面的interface,一个有e条边的graph可以被链表或者edge的数组来表示。在链表实现中,添加一个edge的时间复杂度是O(1),因为可以直接在链表前面接上。但是检查某一个edge是否存在需要O(e)的复杂度因为要遍历整个graph。获取某个节点的相邻节点同样需要O(e)的复杂度(Worst case)。使用哈希表和自平衡二分树也可以实现graph,但是这里只涉及到使用adjacency matrices和adjacency lists的graph实现。
Adjacency Matrices
我们可以使用一个二维数组储存两个节点之间是否存在edge,这样的表示方法在这里被称为Adjacency Matrices。假设我们有节点B(=1)和节点D(=3),我们检查两个节点之间是否存在edge的方法就是看二维数组的row 1,column 3。在一个不区分方向的图中,数组的右上半边是左下的镜像。因为edge的关系是对称的。因为我们不允许一个node使用edge链接自己,所以这个matrix的对角线是空的。
Adjacency Matrices的实现需要很多空间。对于一个有v个节点的graph我们需要O(v2)的复杂度来分配内存。但是,使用改方法的好处是添加edge和检查edge的操作都是O(1)复杂度。
假如一个graph有0到2v(v−1)个节点数v,并且多数edge存在,那么总edge数和v2成比例关系,我们说这样的graph是dense的。对于dense graph,O(e)=O(v2),因此adjacency matrices是表示其的较好办法。因为存储其的空间并不比linked list多,但是操作更快。
Adjacency Lists
我们将dense graph的对立称作sparse graph。Adjacency lists适合作为sparse graph的实现。在这个实现当中,我们有一个类似于哈希表的一维数组,每一个vertex都在数组中有一个位置,并且数组中的每一个元素包含所有其他和该vertex相连的链表。
这个实现需要O(v + e)的空间来存储有v个vertex和e个edge的graph。也可以被写作O(max(v, e))。添加一个edge需要的时间是constant,但是lookup某一个edge需要的时间是O(min(v, e)),因为min(v-1, e)是每一个adjacency list的最大长度。在adjacency list实现里面寻找一个neighbor的时间是O(1)。
Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| typedef struct adjlist_node adjlist struct adjlist_node{ vertex vert; adjlist *next; };
typedef struct graph_header graph; struct graph_header{ unsigned int size; adjlist **adj; };
bool is_vertex(graph *G, vertex v){ REQUIRES(G != NULL); return v < G->size; }
bool is_in(adjlist *p, vertex v){ while(p != NULL){ if(p->vert == v) return true; p = p->next; } return false; }
bool is_graph(graph *G){ if(G == NULL) return false; if(G->adj == NULL) return false; for(unsigned int i = 0; i < G->size; i++){ if(!is_acyclic(G->adj[i])) return false; for(adjlist *p = G->adj[i]; p != NULL; p = p->next){ if(p->vert == i || !(is_vertex(G, p->vert))) return false; if(!is_in(G->adj[p->vert], i)) return false; if(is_in(p->next, p->vert)) return false; } } return true; }
graph *graph_new(unsigned int size){ graph *G = xmalloc(sizeof(graph)); G->size = size; G->adj = xcalloc(size, sizeof(adjlist*)); ENSURES(is_graph(G)); return G; }
bool graph_hasedge(graph *G, vertex v, vertex w){ REQUIRES(is_graph(G) && is_vertex(G, v) && is_vertex(G, w)); for(adjlist *L = G->adj[v]; L != NULL; L = L->next){ if(L->vert = w) return true; } return false; }
void graph_addedge(graph *G, vertex v, vertex w){ REQUIRES(is_graph(G) && is_vertex(G, v) && is_vertex(G, w)); REQUIRES(v != w && !graph_hasedge(G, v, w));
adjlist *L; L = xmalloc(sizeof(adjlist)); L->vert = w; L->next = G->adj[v]; G->adj[v] = L;
L = xmalloc(sizeof(adjlist)); L->vert = v; L->next = G->adj[w]; G->adj[w] = L;
ENSURES(is_graph(G)); ENSURES(graph_hasedge(G, v, w)); }
struct neighbor_header{ adjlist *next_neighbor; } typedef struct neighbor_header neighbors;
neighbors *graph_get_neighbors(graph *G, vertex v){ REQUIRES(is_graph(G) && is_vertex(G, v)); neighbors *nbors = xmalloc(sizeof(neighbors)); nbors->next_neighbor = G->adj[v]; ENSURES(is_neighbors(nbors)); return nbors; }
bool graph_hasmore_neighbors(neighbors *nbors){ REQUIRES(is_neighbors(nbors)); return nbors->next_neighbor != NULL; }
vertex graph_next_neighbor(neighbors *nbors){ REQUIRES(is_neighbors(nbors)); REQUIRES(graph_hasmore_neighbors(nbors));
vertex v = nbors->next_neighbor->vert; nbors->next_neighbor = nbors->next_neighbor->next; return v; }
void graph_free_neighbors(neighbors *nbors){ free(nbors); }
|