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


 
逆邻接表逆邻接表与邻接表结构类似,只不过图的顶点链接着能够到达该顶点的相邻顶点 。也就是说,邻接表时顺着图中的箭头寻找相邻顶点,而逆邻接表时逆着图中的箭头寻找相邻顶点 。

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

文章插图
邻接表和逆邻接表的共同使用下,就能够把一个完整的有向图结构进行表示 。可以发现,邻接表和逆邻接表实际上有一部分数据时重合的,因此可以将两个表合二为一,从而得到了所谓的十字链表 。
 
十字链表十字链表似乎很简单,只需要通过相同的顶点分别链向以该顶点为终点和起点的相邻顶点即可 。
24 张图彻底弄懂九大常见数据结构

文章插图
但这并不是最优的表示方式 。虽然这样的方式共用了中间的顶点存储空间,但是邻接表和逆邻接表的链表节点中重复出现的顶点并没有得到重复利用,反而是进行了再次存储 。因此,上图的表示方式还可以进行进一步优化 。
十字链表优化后,可通过扩展的顶点结构和边结构来进行正逆邻接表的存储:(下面的弧头可看作是边的箭头那端,弧尾可看作是边的圆点那端)
data:用于存储该顶点中的数据;
firstin指针:用于连接以当前顶点为弧头的其他顶点构成的链表,即从别的顶点指进来的顶点;
firstout指针:用于连接以当前顶点为弧尾的其他顶点构成的链表,即从该顶点指出去的顶点;
边结构通过存储两个顶点来确定一条边,同时通过分别代表这两个顶点的指针来与相邻顶点进行链接:
tailvex:用于存储作为弧尾的顶点的编号;
headvex:用于存储作为弧头的顶点的编号;
headlink 指针:用于链接下一个存储作为弧头的顶点的节点;
taillink 指针:用于链接下一个存储作为弧尾的顶点的节点;

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

文章插图
以上图为例子,对于顶点A而言,其作为起点能够到达顶点E 。因此在邻接表中顶点A要通过边AE(即边04)指向顶点E,顶点A的firstout指针需要指向边04的tailvex 。同时,从B出发能够到达A,所以在逆邻接表中顶点A要通过边AB(即边10)指向B,顶点A的firstin指针需要指向边10的弧头,即headlink指针 。以此类推 。
十字链表采用了一种看起来比较繁乱的方式对边的方向性进行了表示,能够在尽可能降低存储空间的情况下增加指针保留顶点之间的方向性 。具体的操作可能一时间不好弄懂,建议多看几次上图,弄清指针指向的意义,明白正向和逆向邻接表的表示 。
  
总结数据结构博大精深,没有高等数学的讳莫如深,也没有量子力学的玄乎其神,但是其在计算机科学的各个领域都具有强大的力量 。本文试图采用图解的方式对九种数据结构进行理论上的介绍,但是其实这都是不够的 。
即便是简单的数组、栈、队列等结构,在实际使用以及底层实现上都会有许多优化设计以及使用技巧,这意味着还需要真正把它们灵活的用起来,才能够算是真正意义上的熟悉和精通 。但是本文可以作为常见数据结构的一个总结,当你对某些结构有些淡忘的时候,不妨重新回来看看 。

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


推荐阅读