纸上谈兵: 图 (graph)

  • 时间:
  • 浏览:1

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!

图(graph)是本身 比较松散的数据价值形式。它有要是 节点(vertice),在要是 节点之间,由(edge)相连。节点的概念在树中也出先过,亲戚亲戚朋友通常在节点中储存数据。边表示一一3个 多节点之间的趋于稳定关系。在树中,亲戚亲戚朋友用边来表示子节点和父节点的归属关系。树是本身 特殊的图,但限制性更强要是 。

另一一3个 多的本身 数据价值形式是很常见的。比如计算机网络,要是 我由要是 节点(计算机如果路由器)以及节点之间的边(网线)构成的。城市的道路系统,也是由节点(路口)和边(道路)构成的图。地铁系统也都需要理解为图,地铁站都需要认为是节点。基于图有要是 经典的算法,比如求图中一一3个 多节点的最短路径,求最小伸展树等。

图的经典研究是柯尼斯堡七桥间题(Seven Bridges of Königsberg)。柯尼斯堡是现今的加里宁格勒,城市带有根小绳子 河流过,河带有一一3个 多小岛。有七座桥桥连接河的两岸和一一3个 多小岛。送信员总想知道,有这麼 一一3个 多法律方式,能不重复的走过7个桥呢?

(本身 间题在要是 奥数教材中称为"一笔画"间题)

欧拉时代的柯尼斯堡地图

柯尼斯堡的都需要看作由7个边和一一3个 多节点构成的一一3个 多图:

本身 间题最终被欧拉巧妙的处里。七桥间题也启发了一门新的数人学科——图论(graph theory)的诞生。欧拉的基本思路是,如果某个节点有的是起点如果终点,这麼 连接它的边的数目需要为偶数个(从一一3个 多桥进入,再从另一一3个 多桥一蹶不振 )。对于柯尼斯堡的七桥,如果一一3个 多节点都为奇数个桥,而最多只有有一一3个 多节点为起点和终点,要是 不如果一次走完。

图的定义

严格的说,图[$G = (V, E)$]是由节点的集合V和边的集合E构成的。一一3个 多图的所有节点构成一一3个 多集合[$V$]。一一3个 多边都需要表示为[$(v_1, v_2)$],其中[$v_1, v_2 \in V$],即一一3个 多节点。如果[$(v_1, v_2)$]有序,即[$(v_1, v_2)$]与[$(v_2, v_1)$]不同,这麼 图是有向的(directed)。有序的边都需要理解为单行道,只有沿一一3个 多方向行进。如果[$(v_1, v_2)$]无序,这麼 图是无向的(undirected)。无序的边都需要理解成双向都都需要行进的道路。一一3个 多无序的边都需要看作连接相同节点的一一3个 多反向的有序边,要是 无向图都需要理解为有向图的本身 特殊情形。

(七桥间题中的图是无向的。城市中的公交线路都需要是 无向的,比如趋于稳定单向环线)

图的一一3个 多路径(path)是图的一系列节点[$w_1, w_2, ..., w_n$],且对于[$1 \le i < n $],有[$ (w_i, w_{i+1}) \in E$]。也要是 我说,路径是一系列的边连接而成,路径的两端为一一3个 多节点。路径上端的总数称为路径的长度。乘坐地铁时,亲戚亲戚朋友会在选者某个路径,来从A站到达B站。另一一3个 多的路径如果有不止根小绳子 ,亲戚亲戚朋友往往会根据路径的长度以及沿线的拥挤情形,来选者根小绳子 最佳的路线。如果趋于稳定根小绳子 长度大于0的路径,该路径的两端为同一节点,这麼 认为该图中趋于稳定环路(cycle)。很明显,上海的地铁系统中趋于稳定环路。

 

找到根小绳子 环路

如果从每个节点,到任意一一3个 多其它的节点,有的是根小绳子 路径说说,这麼 图是连通的(connected)。对于一一3个 多有向图来说,另一一3个 多的连通称为强连通(strongly connected)。如果一一3个 多有向图不满足强连通的条件,但将它的所有边都改为双向的,此时的无向图是连通的,这麼 认为该有向图是弱连通(weakly connected)。

如果将有火车站的城市认为是节点,铁路是连接城市的边,另一一3个 多的图如果是不连通的。比如北京和费城,北京有铁路通往上海,费城有铁路通往纽约,但北京和费城之间这麼 路径相连。

图的实现

本身 简单的实现图的法律方式是使用二维数组。让数组a的每一行为一一3个 多节点,该行的不同元素表示该节点与要是 节点的连接关系。如果[$(u, v) \in E$],这麼 a[u][v]记为1,或者为0。比如下面的一一3个 多带有一一3个 多节点的图:

 

都需要简单表示为

a 1 2 3
1 0 1 1
2 0 0 0
3 0 1 0

本身 实现法律方式所趋于稳定的空间为[$O(|V|^2)$],[$|V|$]为节点总数。所需内存随着节点增加而更慢增多。之上端有的是很密集,这麼 要是 数组元素记为0,只有稀疏的要是 数组元素记为1,要是 并有的是很经济。

更经济的实现法律方式是使用,即记录每个节点所有的相邻节点。对于节点m,亲戚亲戚朋友建立一一3个 多链表。对于任意节点k,如果有[$(m, k) \in E$],就将该节点放在到对应节点m的链表中。邻接表是实现图的标准法律方式。比如下面的图,

 

都需要用如下的数据价值形式实现:

 

左侧为一一3个 多数组,每个数组元素代表一一3个 多节点,且指向一一3个 多链表。该链表包带有该数组元素所有的相邻元素。

总体上看,邻接表都需要分为两部分。邻接表所趋于稳定的总空间为[$O(|V| + |E|)$]。数组部分储存节点信息,趋于稳定[$|V|$])的空间,即节点的总数。链表存储边的信息,趋于稳定[$|E|$]的空间,即边的总数。在要是 多样化的间题中,定点和边还如果有要是 的附加信息,亲戚亲戚朋友都需要将那些附加信息储趋于稳定相应的节点之上端的位置。

下面为具体的C代码:

/* By Vamei */
#include <stdio.h>
#include <stdlib.h>

#define NUM_V 5

typedef struct node *position;

/* node */
struct node {
    int element;
    position next;
};

/* 
 * operations (stereotype)
 */
void insert_edge(position, int, int);
void print_graph(position graph, int nv);

/* for testing purpose */
void main()
{
    struct node graph[NUM_V];
    int i;

    // initialize the vertices
    for(i=1; i<NUM_V; i++) {
        (graph+i)->element = i;
        (graph+i)->next    = NULL;
    }

    // insert edges
    insert_edge(graph,1,2);
    insert_edge(graph,1,4);
    insert_edge(graph,3,2);
    insert_edge(graph,4,2);
    insert_edge(graph,4,3);

    print_graph(graph,NUM_V);
}

/* print the graph */
void print_graph(position graph, int nv) {
    int i;
    position p;
    for(i=1; i<nv; i++) {
        p = (graph + i)->next;
        printf("From %3d: ", i);
        while(p != NULL) {
            printf("%d->%d; ", i, p->element);
            p = p->next;
        }
        printf("\n");
    }
}

/*
 * insert an edge
 */
void insert_edge(position graph,int from, int to)
{
    position np;
    position nodeAddr;

    np = graph + from;

    nodeAddr = (position) malloc(sizeof(struct node));
    nodeAddr->element = to;
    nodeAddr->next    = np->next;
    np->next = nodeAddr;
}

运行结果:

From   1: 1->4; 1->2;

From   2:

From   3: 3->2;

From   4: 4->3; 4->2;

上端的实现主要基于链表,可参考纸上谈兵: 表 (list) 。

总结

图是本身 很简单的数据价值形式。图的组织法律方式比较松散,自由度比较大,但也造成比较高的算法多样化度。我将在如果介绍要是 图的经典算法。

欢迎继续阅读“纸上谈兵: 算法与数据价值形式”系列