24 张图彻底弄懂九大常见数据结构( 四 )


再哈希法:在产生冲突之后,使用关键字的其他部分继续计算地址,如果还是有冲突,则继续使用其他部分再计算地址 。这种方式的缺点是时间增加了 。
链地址法:链地址法其实就是对Key通过哈希之后落在同一个地址上的值,做一个链表 。其实在很多高级语言的实现当中,也是使用这种方式处理冲突的 。
公共溢出区:这种方式是建立一个公共溢出区,当地址存在冲突时,把新的地址放在公共溢出区里 。
目前比较常用的冲突解决方法是链地址法,一般可以通过数组和链表的结合达到冲突数据缓存的目的 。

24 张图彻底弄懂九大常见数据结构

文章插图
左侧数组的每个成员包括一个指针,指向一个链表的头 。每发生一个冲突的数据,就将该数据作为链表的节点链接到链表尾部 。这样一来,就可以保证冲突的数据能够区分并顺利访问 。
考虑到链表过长造成的问题,还可以使用红黑树替换链表进行冲突数据的处理操作,来提高散列表的查询稳定性 。
 图图相较于上文的几个结构可能接触的不多,但是在实际的应用场景中却经常出现 。比方说交通中的线路图,常见的思维导图都可以看作是图的具体表现形式 。
图结构一般包括顶点和边,顶点通常用圆圈来表示,边就是这些圆圈之间的连线 。边还可以根据顶点之间的关系设置不同的权重,默认权重相同皆为1 。此外根据边的方向性,还可将图分为有向图和无向图 。
24 张图彻底弄懂九大常见数据结构

文章插图
图结构用抽象的图线来表示十分简单,顶点和边之间的关系非常清晰明了 。但是在具体的代码实现中,为了将各个顶点和边的关系存储下来,却不是一件易事 。
 
邻接矩阵目前常用的图存储方式为邻接矩阵,通过所有顶点的二维矩阵来存储两个顶点之间是否相连,或者存储两顶点间的边权重 。
24 张图彻底弄懂九大常见数据结构

文章插图
无向图的邻接矩阵是一个对称矩阵,是因为边不具有方向性,若能从此顶点能够到达彼顶点,那么彼顶点自然也能够达到此顶点 。此外,由于顶点本身与本身相连没有意义,所以在邻接矩阵中对角线上皆为0 。
24 张图彻底弄懂九大常见数据结构

文章插图
有向图由于边具有方向性,因此彼此顶点之间并不能相互达到,所以其邻接矩阵的对称性不再 。
用邻接矩阵可以直接从二维关系中获得任意两个顶点的关系,可直接判断是否相连 。但是在对矩阵进行存储时,却需要完整的一个二维数组 。若图中顶点数过多,会导致二维数组的大小剧增,从而占用大量的内存空间 。
而根据实际情况可以分析得,图中的顶点并不是任意两个顶点间都会相连,不是都需要对其边上权重进行存储 。那么存储的邻接矩阵实际上会存在大量的0 。虽然可以通过稀疏表示等方式对稀疏性高的矩阵进行关键信息的存储,但是却增加了图存储的复杂性 。
因此,为了解决上述问题,一种可以只存储相连顶点关系的邻接表应运而生 。
 
邻接表在邻接表中,图的每一个顶点都是一个链表的头节点,其后连接着该顶点能够直接达到的相邻顶点 。相较于无向图,有向图的情况更为复杂,因此这里采用有向图进行实例分析 。
24 张图彻底弄懂九大常见数据结构

文章插图
在邻接表中,每一个顶点都对应着一条链表,链表中存储的是顶点能够达到的相邻顶点 。存储的顺序可以按照顶点的编号顺序进行 。比如上图中对于顶点B来说,其通过有向边可以到达顶点A和顶点E,那么其对应的邻接表中的顺序即B->A->E,其它顶点亦如此 。
通过邻接表可以获得从某个顶点出发能够到达的顶点,从而省去了对不相连顶点的存储空间 。然而,这还不够 。对于有向图而言,图中有效信息除了从顶点“指出去”的信息,还包括从别的顶点“指进来”的信息 。这里的“指出去”和“指进来”可以用出度和入度来表示 。
入度:有向图的某个顶点作为终点的次数和 。
出度:有向图的某个顶点作为起点的次数和 。
由此看出,在对有向图进行表示时,邻接表只能求出图的出度,而无法求出入度 。这个问题很好解决,那就是增加一个表用来存储能够到达某个顶点的相邻顶点 。这个表称作逆邻接表 。


推荐阅读