使用level-compression tire树来实现快速IP路由查找(http://www.csc.kth.se/~snilsson/fast-routing/#fig)

IP地址查找

IP协议以包的形式来发送数据。每个包头中除了其他东西外,都有一个目的地址。IPv4中该地址是一个32位的数字。而在IPv6中,位数将增加到128位。

当一个包通过Internet来发送时,它通常会遇到一些路由器。这些路由器的工作就是负责将这些包转发到正确的最终地址处。路由器中维护了一张用于指示每个可能地址的适合的转发方向。这张路由表中只是包含了一些前缀而不非全部地址。前缀相同的地址被路由到相同的地方。这是一个由15个实例的路由表:

image一个包的目的地址和路由表中的前缀进行比较。如果有多于一个匹配,则选择最长的匹配前缀。如果没有实例匹配,那么则使用默认的路由项。Internet中的核心路由器需要识别所有的地址,因此通常来说这种路由器维护的路由表都非常的庞大。

Tries

trie是用来存储字符串的通用数据结构。隐藏在其背后的思想非常简单,就是每个字符串代表树结构中的叶子节点。而字符串的值就是对应的从根到叶子节点的路径。路由表的二进制串对应于下图中的tire树。

image 举例来说,串010对应的就是从根节点到叶子节点3的路径:首先左转,然后右转,再左转。

     这个简单的结构效率不高。节点的数目可能非常的大同时平均深度也可能非常的深。克服这个问题的传统技术就是使用路径压缩:每个只有一个儿子的内部节点被一处。我们存储一个数据,skip,来标示该节点在路径中跳过了多少位。上图的路径压缩版如下图所示。在路径压缩二叉trie中节点总数为2n-1,其中n为trie中叶子节点的数目。对于大部分的分布来说,路径压缩trie的平均深度为log n。

image 路径压缩用于压缩trie中很少使用的部分。层次压缩,是用于压缩trie中经常被使用的部分。其思路是,通过使用一个节点来代替有2i个后代的正层。该替换不断的递归使用到各个子trie。上图的路径压缩图使用LC-trie算法得到下面的图。LC-trie的平均深度为log log n。

 

image 

如果我们想要得到理论的效率上界,有效的表示该trie当然是非常重要的。使用传统的方式,在每个内部节点都保存儿子的指针集合显然不是一个好的方法。一个好的空间有效的方法是将儿子节点保存在连续的内存位置。使用这种方法,只有最左边的儿子才是需要的。事实上,所有的节点可以保存在一个数组中,而每一个节点可以通过只包含三个数字的单个字来表示:

  • 分支因子
  • skip值
  • 最左儿子

上图的LC-trie可以通过下面的数组来表示:

image

注意图中的一些细节,该图中的节点标号与图三是不一致的,数组中的标号从根节点开始,为0,编号是按照层次遍历的方式来编的。branch的值为各个节点的分支数去log,如根节点的分支树为8,则branch为log 8

通过这种实现,没有很明显的用来动态更新,插入或者移除单个实例的方法。然而,对于路由表来说,可以在一个比较大的时间段内通过重建整个结构来实现。

搜索算法可以通过下面的伪码高效的实现:

image

其中s代表搜索的字符串,EXTRACT函数返回在字符串s中从位置p开始branch个位所代表的值。我们使用T来代表该trie树。trie的根节点保存在T[0]中。注意,返回的add只是表示可能命中,在搜索中跳过的bits可能不匹配。因此,我们必须单独地保存字符串的值,而后执行一次额外的比较来检查是否真正的进行了成功的搜索。

例如,我们搜索10110111.我们从root开始,node值为0,branch为3,skip值为0.因此,我们从搜索字符串中得到前3个bit。这3个bit的值为5,加到指针中,得到数组中的位置6.在该节点,branch值为2,skip值为0,得到另外的2个bit,值为2,将该值加到该位置,到达位置15,branch为0,即为叶子节点。指针值5指明了在基向量中的位置。注意,我们必须总要检查该命中是否为真。我们必须比较查找串中的前5bit和基向量中前5bit的值。事实上,该查找时成功的。

你可能感兴趣的:(使用level-compression tire树来实现快速IP路由查找(http://www.csc.kth.se/~snilsson/fast-routing/#fig))