边关系表
边编号 | 起始节点编号 | 终止节点编号 | 权重 |
1 |
1 |
3 |
5 |
2 |
1 |
5 |
4 |
3 |
1 |
6 |
2 |
4 |
2 |
3 |
1 |
5 |
2 |
5 |
6 |
6 |
3 |
1 |
5 |
7 |
3 |
2 |
1 |
8 |
3 |
4 |
3 |
9 |
4 |
3 |
3 |
10 |
4 |
6 |
5 |
11 |
5 |
1 |
4 |
12 |
5 |
2 |
6 |
13 |
6 |
1 |
2 |
14 |
6 |
4 |
5 |
由于上面给出的图是无向图,所以两个节点之间的边的关系是相互的,如V1——>V3那么V3——>V1。所以边的关系表中有14条记录。上面列举出了节点的存储结构和边的存储结构,那么如何根据这些信息还原图的结构呢?其实要还原图,只需关注边的关系表就够了,而节点关系表只是存储节点详细信息的,而图的搜索和数据挖掘在开始时是不需要这些节点的详细信息,而是在挖掘到了有用的信息时才从关系表中获取每个节点的详细信息。所以在进行搜索或者其他的图挖掘的时候完全不需要知道节点详细信息,而是只要拿到每个节点的唯一编号就行了,当需要时再根据这个编号到节点关系表中获取节点的信息,这样做可以减少图所占用的内存,因为此时每个节点只是一个数字,而不是那些复杂的信息。废话不多说,那么如何根据边关系表得到图呢?还原图其实只需选择一个节点出发,对图进行探索就行了。如此处选择编号为1的节点(此后将以节点编号表示一个节点)对图进行探索:可以根据上面的表知道和1相连的节点有3、5和6,那么就存在1——>3,1——>5和1——>6(此时是找以1为起始节点的边),可以知道现在图的结构已经探索到了3、5和6节点,那么接下来将对3、5和6进行探索,即获取以3为起始节点、5为起始节点和6为起始节点的边,依次类推,将构造出一个图的结构。大家可能都看得到,其实上面的3、5和6都是1的邻接节点,所以可以推出:一个邻接节点将产生一条边(此处的邻接节点是和节点直接相连的节点,并且可达,如是有向边,这就要考虑方向性,反向是不可达的,所以此时不存在相互性),所以图的边可以直接更具节点的邻接节点来进行存储,这样可以简化图的还原。大家可能发现这样通过数据库来对图进行扩展是非常漫长的,如果图有上百万个节点,那么下个要通过这样的数据库来进行还原图那是非常艰苦的。下面介绍一种相对比较好的存储方式。
2)关系数据库+文本
此种方法就是将节点的详细信息存储到数据库中,所以还使用上面的节点关系表,而此处的边就不能存储在数据库中了,而是存储在文本中,此处就不对节点存储进行详细的介绍了。上面最后说过一个节点和其邻接节点就会构成一条边,所以此处就是通过存储邻接节点来对边进行存储了。我们是将每个节点的邻接节点通过数组写到文本中,可知一个节点的邻接节点不止一个,所以每个节点的邻接节点是存储在文本中的某一块区域,所以此处需要引入偏移量的概念,此处每个节点对应了一个偏移量,这个偏移量是做什么用的呢?就是指出这个节点的邻接节点信息是存储在文本的那个位置开始。同时可以知道一个节点的邻接节点个数是和该节点的度有关的,例如上面的1节点有3个度,那么1将有3个邻接节点,所以将产生3条边,这是一个重要的概念,因为度的大小和一个节点的邻接节点个数相同,同时也影响该节点在存储邻接节点文本所占据的大小。说了这么多肯定会晕的,下面还是举个例子说明吧:
邻接节点文本
位置 | 邻接节点 |
0 |
3 |
1 |
5 |
2 |
6 |
3 |
3 |
4 |
5 |
5 |
1 |
6 |
2 |
7 |
4 |
8 |
3 |
9 |
6 |
10 |
1 |
11 |
2 |
12 |
1 |
13 |
4 |
节点度文本
节点编号 | 度 |
1 |
3 |
2 |
2 |
3 |
3 |
4 |
2 |
5 |
2 |
6 |
2 |
|
|
邻接节点偏移量
节点编号 | 偏移量 |
1 |
0 |
2 |
3 |
3 |
5 |
4 |
8 |
5 |
10 |
6 |
12 |
此处可以看出,1的邻接节点是存储在邻接节点文本的0位置到2的位置,2的邻接节点是存储在3位置到4的位置.....其他的节点也可以以此退出,其实在文本中存储是以数组存储的,并没有上面那样的结构,如邻接节点文本:[3,5,6,3,5,1,2,4,3,6,1,2,1,4],在文本中每个数字一行,而偏移量就是某个节点的邻接节点存储位置的起始位置,而长度为该节点的度的大小,同样节点的度和偏移量也是这样存储的,也是每个数组一行,同时行数和节点的编号要对应,如1节点是第一行,2节点是第二行....因为这些值是和节点一一对应的。在我们的程序中其实是将这些信息通过IO读到数组中,如我们定义一个adjancent[]数组用于存放邻接节点的,adjancentoffset[]存储偏移量的,degree[]是存储节点度的。如1节点的度是degree[0],它的邻接节点偏移量是为:adjancentoffset[0],那么1节点的邻接节点是存储在adjancent[adjancentoffset[0]]到adjancent[adjancentoffset[0]+degree[0]-1]中,那么就会产生相应的边,那么边的编号就是adjancentoffset[0]~adjancentoffset[0]+degree[0]-1,可以看到边的编号和邻接节点的存储位置是对应的,因为一个邻接节点对应了一条边,上面已经讲过,此处就不在赘述。我们就可以将这些数组存放在程序中,并利用这些数组还原图结构,例如:我们还是以1节点开始扩展图:1的邻接节点有3、5和6,此时就扩展了三个节点,在通过这三个节点扩展它们各自的邻接节点,依此类推,边还原了图的结构,该种方法,避免了大量的数据库的访问,而是通过把图存储在文本中,并一次性的读入到内存中,并通过迭代还原图结构。但这种操作容易产生几种缺陷,第一、图的信息更新不方便,应为向图中添加了一些节点,那么就要重新生成这些文本,第二、这些图的信息全部存储在内存中,使得图的大小收到内存的限制。我做试验的时候,存储2百万个节点2百多万条边的图占内存20多M。所以多余图的节点个数不怎么更改,图的节点个数不不亿量级的可以采取这样的方式存储。关于该种方法的使用详细见
《一种图存储结构【看了之后你会对图的结构有新的认识》。
此文到此已差不多结束,希望如有什么不对的地方洗完指出!欢迎评论该文!
谢谢浏览!此文完!