图的高效数据结构表示

以下内容转自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;
}

测试代码中设计到dot工具的使用,转载博客中有相关帖子可以学习,使用dot工具后,测试代码中构造的图是这样的



                                   

你可能感兴趣的:(数据结构,图,动态分配数组)