图论(1) 图的基本数据结构和算法

图里面的东西太多,先写一个概要。在后面的文章中将继续逐个探讨和实现。

1,一些概念

顶点

边(无向图) 或 弧(有向图)

完全图,子图,连通图

路径,简单路径(顶点不重复)

生成树(无向图),关键路径,拓扑排序

2,存储结构

非常重要!!!
2种存储方式,邻接矩阵和邻接表

目前我写的都是用邻接表写的,但后来发现大家还是用邻接矩阵比较多,后来问了下baidu的阿海,他说一般都是用邻接矩阵,简单一些,但用过邻接表话,肯定可以秒杀邻接矩阵。恩 ,有时间我也写一下邻接矩阵的实现,,,,

无论哪种方式,顶点一般来说都放在数组里。(当然你也可以放在链表里,这样似乎很麻烦,很少见到这样做的),两种方式的区别在于顶点之间的关系的实现用什么

1)邻接表

这里的表其实是存储边得链表

将顶点放在一个Node[]数组里,每一个顶点维护一个它的边的链表(想想怎么去实现?),下面是顶点类的定义:

  
   package
    Graph;

public class VNode {

private Object element; // 节点自身信息
private Edge firstEdge; // 节点的第一条边
private boolean visited = false ; // 在遍历时标识是否被访问过

// get,set方法省略
}

每次写一个类似于C里面的结构体类,都把成员变量写为private,结果就需要写很多的get和set方法来访问和设置,这样做符合面向对象封装的要求,但算法的代码就显得比较麻烦,为了简化代码,侧重算法,以后我就都把它们写成public了。这里还是写成了private,get和set方法省略了。

这样Node[]数组里存放的就是上述顶点类型,每一个顶点包含顶点自身存放元素的信息(必备),是否被访问过的信息(遍历时用到),指向第一条边的Edge指针,

要维护顶点的边链表,还需要一个边类:

  
   package
    Graph;

public class Edge {

private int start,end; // 边指向两个节点的位置(如果用数组存放节点,就是下标,如果用链存放下标,就是位置索引)
private Edge next; // 边指向的下一条边

private int len; // 边的信息(长度)


// 省略读写方法
}

我用边得首尾顶点在顶点数组里的下标来标识一条边(这样有向无向图可以一起写),在边里还要维护一个指向下一个边的Edge指针(维护边链表),还有一个边的长度信息(写最小生成树的时候用到,或者其他的加权图)

上述2个顶点和边的定义就可以实现邻接表的存储方式。

2)邻接矩阵

现在顶点的定义只需要放顶点的元素和是否被访问即可,不需要指向第一个边的指针。

我们还是用Node[]放顶点,用一个关系矩阵来存放顶点之间的关系(是否有边连接)
关系矩阵用一个二维数组实现,

M[n][n]—假设有n个顶点的关系矩阵,M[i][j]的值表示顶点i和顶点j的关系

3,基本的算法

1)广度优先搜索
队列实现

2)深度优先搜索
栈实现

3)无向图的最小生成树
2种贪心算法

4)拓扑排序

5)带权有向无环图的关键路径

6)最短路径

总结:

图的存储方式很重要,弄清楚怎么去标表示图,才能做接下来的工作—

邻接表是指每个顶点维护一个跟它相关联的边得链表,而不是跟它相关联的顶点的链表,也就是说需要有2个类,顶点类(要包含指向第一个关联边的引用),边类(要包含指向下一条关联边得指针)。如果只有顶点类的话,那不行,边的长度信息没有地方存储

这是用邻接表的时候的一个小思考和误区,如果实现成相关联的顶点的链表,那么只能做广搜和深搜,对于加权图有关的算法就无能为力了,这样实现只能表示顶点之间的是否有连接的关系,边的信息不存在。

    原文作者:jinmengzhe
    原文地址: https://www.cnblogs.com/jmzz/archive/2011/05/21/2052803.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞