Trie树遍历的加速

Trie树用来给字符串排序的时候有一个好处:边读边排序,但是读完之后要输出的时候麻烦来了。经过测试,用26W个word建立的Trie中,空白位是使用位的20倍左右,那么在Trie比较大的时候当然也就比较慢了。这篇文章讨论的优化主要是去避免访问这些空白位,实现方式无关(数组或指针?)。

  首先想到的一个方法是:在insert的时候顺便标记这个节点有哪些子节点。因为总共只有26种可能性,那么自然也就想到了用一个int作为flag,如果0位置1则表示有‘a’这个子节点。

  第一步(记录)完成了,下面我们来看如何来使用该记录?熟悉位移的同学可能已经想到:可以使用x&-x来计算出最低位为1的数。但是遗憾的是这个并不是我们想要的结果,因为得到的是2^N,我们想要的是这个N。好的,最笨的一种方法出来了:

  int t[] = new int[1<<26];

  t[1<<0] = 0.....

非常遗憾的是我们申请不了这么大的空间。那么,问题就是将一些离散的数通过数组建立映射关系,很自然地就会联想到Hash。怎么使用Hash呢?我尝试了一下,从2^0到2^26对29取余数为:

  1.2.4.8.16.3.6.12.24.19.9.18.7.14.28.27.25.21.13.26.23.17.5.10.20.11

没有看错,没有一个是相同的!这样的话就可以仅仅使用一个大小不到30的数组就可以搞定。同样遗憾的时候,这里用到了取余数的操作,要知道这个还算比较大的,可能一不小心就把我们辛辛苦苦省下来的时间又葬送掉了。那么有没有其他的方法呢?还是回到最笨的方法的思路上:

  如果我们申请不了int[1<<26],那申请int[1<<13]总是可以的吧?

为什么这么做呢?因为申请不了这么大的空间!这时候就把这个问题换成两半了,比如在节点I出的标志位flag:

  int lowHalf = flag&((1<<14)-1);

  int highHalf = (flag>>13)&((1<<14)-1);

  while(lowHalf > 0){

    int i = lowHalf&-lowHalf;

    int j = t[i];

    // do someting

  }

  while(highHalf > 0){

    int i = highHalf&-highHalf;

    int j = t[i]+13;

    // do someting

  }

和最笨的那种方法相比,只是多了一次操作而已。

  好了现在来看最后一种方法,就不多写了:

  switch(flag&-flag){

  case 1<<0:

  case 1<<1:

  case 1<<2:

  .....

  }

你可能感兴趣的:(trie)