Chinaunix首页 | 论坛 | 博客
  • 博客访问: 204544
  • 博文数量: 16
  • 博客积分: 2001
  • 博客等级: 大尉
  • 技术积分: 265
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-29 16:25
文章分类
文章存档

2011年(1)

2009年(1)

2008年(14)

我的朋友

分类: C/C++

2008-10-23 15:51:59

Trie.h  Trie.cc  LHashTrie.cc  SArrayTrie.cc
文档作者:rickjin
创立时间:08.08.24
--------------
1、基本类
--------------
    Trie.h Trie.cc 这两个文件主要以模板方式实现了一个常用Trie 结构, Trie    DataT> 可以当作是 Map 的扩展, 把一个 KeyT 的序列 (KeyT *) 映射到数据.
    该文件中同时提供了两个用于遍历 Trie 的 迭代器:
    TrieIter  用于遍历当前Trie 结点下的直接子结点
    TrieIter2 用于遍历当前Trie 结点下的所有可能路径中符合给定深度的子结点

    SArrayTrie.cc 同 LHashTrie.cc 两个文件很简单,  只是加载 Trie 的定义,
    但分别使用 SArray 和 LHash 存储内部数据.  Trie.h 中提供了一个宏
    USE_SARRAY_TRIE 作为选择开关 如果 USE_SARRAY_TRIE 被定义, 则选用 SArray
    存储数据, 否则使用 LHash存储数据. 
   
    #ifdef USE_SARRAY_TRIE
    # define TRIE_INDEX_T   SArray
    # define TRIE_ITER_T    SArrayIter
    #else /* ! USE_SARRAY_TRIE */
    # define TRIE_INDEX_T   LHash
    # define TRIE_ITER_T    LHashIter
    #endif /* USE_SARRAY_TRIE *
   

----------------
2、类接口说明
----------------
2.1) Trie 类主要接口

template
class Trie
{
    friend class TrieIter;
    friend class TrieIter2;
public:
    //查找操作
    Trie *findTrie(const KeyT *keys, Boolean &foundP);
    //插入操作
    Trie *insertTrie(const KeyT *keys, Boolean &foundP);
    //删除操作
    Trie *removeTrie(const KeyT *keys, Boolean &foundP);
    //返回当前结点数据
    DataT &value();
    //清空操作
    void clear();
    //数据大小
    unsigned int numEntries();
private:
    TRIE_INDEX_T< KeyT, Trie > sub
    DataT data;    
};

 
说明:
a. 私有成员 sub 可以是 SArray 或是 LHash,  是一种 Map 结构,
用于索引键值, 由此构造的 Trie 形成一个嵌套的链式结构, 可以通过 sub
找到所有的子结点. DataT 用于存储当前结点下的数据.
b. 类似于 C 语言中的字符串以 '\0' 结束, 键值序列 KeyT *keys 也以一个特殊键值
标志结束位置, 可以通过布尔函数 Map_noKeyP(key) 判定key 是否为结束标志.
c. 如果 keys == NULL  或是 keys 长度为0 (即 Map_noKeyP(Key[0]) == True), 则此
键值索引当前的 Trie 结点.
d. foundP 用于指示基于 keys 的检索是否成功, 如果成功则 foundP 被设置为 True.
   
2.2) TrieIter 类主要接口

template
class TrieIter
{
public:
    //构造函数
    TrieIter(const Trie &trie, int (*sort)(KeyT,KeyT) = 0);
 //重新初始化函数
    void init();
    //迭代函数
    Trie *next(KeyT &key);
private:
    TRIE_ITER_T< KeyT, Trie > myIter;
};
说明:
a. TrieIter  用于遍历当前 Trie 结点下的直接子 Trie 结点, 对应于 Trie::TRIE_INDEX_T
   为 SArray(或 LHash) 类型,  TrieIter::TRIE_ITER_T 将为 SArrayIter (或
   LHashIter) 类型, 所以直接使用 SAarrayIter (或 LHashIter) 完成遍历操作, 实
   现很简单。 
b. 构造函数中的 sort 函数用于键值排序。
c. 迭代函数 next(KeyT &key) 调用之后, 设置下一个 key 的值并返回和 key 值对应的
   Trie 结点指针。
d. 当你使用了一个迭代器 TrierIter, 想再次是用它的时候, 可以调用 init() 对它重新
  初始化
2.3) TrieIter2 类主要接口
template
class TrieIter2
{
public:
    //构造函数
    TrieIter2(const Trie &trie, KeyT *keys, unsigned int level, int (*sort)(KeyT,KeyT) = 0);
 //重新初始化函数
    void init();
    //迭代函数
    Trie *next();
private:
    const Trie &myTrie;                // 当前 Trie 结点
    KeyT *keys;                                    // 用于存储满足要求的结点的键值
    int level;                                     // 要求的结点深度
    int (*sort)(KeyT,KeyT);                        // 键值比较函数
    TRIE_ITER_T< KeyT, Trie > myIter;  // 用于遍历当前 Trie 结点下的所有子结点
    TrieIter2 *subIter;                // 递归地遍历子结点的子结点
    Boolean done;                                  // level=0 的时候指示迭代是否结束
};
说明:
a. TrieIter2 用于遍历当前 Trie 结点下的所有可能路径中深度为 level 的子 Trie 结点
b. 构造函数中传入的 KeyT *keys, 是用于迭代中存储键值用的。 每次调用迭代函数
next(), 函数返回下一个深度为 level 的 Trie 结点的指针, *keys 将被设置为对应于
该结点的键值,
d. 当你使用了一个迭代器 TrierIter2, 想再次是用它的时候, 可以调用 init() 对它重新
  初始化
 
-------------------
3、主要函数功能说明
-------------------
2.1) Trie 类主要接口
a) 析构函数

Trie::~Trie()
{
    TrieIter iter(*this);
    KeyT key;
    Trie *node;
    while (node = iter.next(key)) {
        node->~Trie();             //显示调用子结点的析构函数!!
    }
}
该析构函数通过递归显示调用当前 Trie 结点的子结点的析构函数销毁对象, 此处必须进
行显示递归调用, 否则会出现内存泄漏. 原因是 sub 属于 SArray/LHash 类型, 其中使用
placement new 对内部内存做初始化, 所以必须由用户自己显示调用析构函数, 否则析构
函数不会被执行。

b)查找操作
参数说明:
@param keys   要查找的键值
@param foundP 指示键值对应结点是否存在
@return       键值所对应的 Trie 结点

Trie*
Trie::findTrie(const KeyT *keys, Boolean &foundP)
{
    // 键值为空, 指示当前结点,
    // 设置 foundP 为 True, 返回当前的 Trie 结点
    if (keys == 0 || Map_noKeyP(keys[0])) {
        foundP = true;
        return (Trie *)this;
    } else {
        //非空, 查找索引 sub
        Trie *subtrie = sub.find(keys[0]);
        if (subtrie == 0) {  //查找失败, 设置foundP 为 false, 返回 NULL
            foundP = false;
            return 0;
        } else { // 继续递归查找
            return subtrie->findTrie(keys + 1, foundP);
        }
    }
}
查找函数使用递归函数调用实现, 如果修改为迭代算法能够提高一些性能。
c) 插入操作
插入和查找非常相似, 不再详细介绍

d) 删除操作
参数说明:
@param keys   要删除的结点的键值
@param foundP 指示键值对应结点是否存在
@return       被删除的 Trie 结点的指针

Trie *
Trie::removeTrie(const KeyT *keys, Boolean &foundP)
{
    //删除操作必须提供长度 >0 的 key 值, 否则不做删除操作
    if (keys == 0 || Map_noKeyP(keys[0]))
    {
        foundP = false;
        return 0;
    } else if (Map_noKeyP(keys[1])) //键长为 1, 查找到子结点并删除
    {
        Trie *subtrie = sub.remove(keys[0], foundP);
        if (foundP)
        {
            subtrie->~Trie();    //删除结点,显示调用析构,
            return subtrie;
        } else {
            return 0;
        }
    } else { // 键长 > 1, 递归调用删除操作
        Trie *subtrie = sub.find(keys[0], foundP);
        if (!foundP) {
            return 0;
        } else {
            return subtrie->removeTrie(keys + 1, foundP);
        }
    }
}

2.1) Trie 类主要接口
a) 构造函数

template
TrieIter2::TrieIter2(const Trie &trie,
                                 KeyT *keys, unsigned int level,
                                 int (*sort)(KeyT,KeyT) )
    : myTrie(trie),
      keys(keys),
      level(level),
      sort(sort),
      myIter(trie.sub, sort),
      subIter(0),
      done(false)
{
    if (level == 0)
    {
        Map_noKey(keys[0]); // 设置键值为空
    }
   
    else if (level == 1)
    {
        Map_noKey(keys[1]); // 设置键值长度为 1
    }
}

b) 迭代函数
参数说明:
@return 返回下一个满足深度要求的 Trie 结点, 同时把 keys 设置为该 Trie 结点对应
 的键值

template
Trie* TrieIter2::next()
{
    // level == 0 的时候, 直接返回当前 Trie 结点
    // 使用 done 指示迭代是否结束
 if (level == 0)
    {
  if (done)
   return 0;
        else  
        {
   done = true;
   return (Trie *)&myTrie; 
  }
 }
    else if (level == 1)  //当前结点下面的所有子结点都满足 level == 1
    {
  return myIter.next(keys[0]);
 }
    else // 结点深度要求 level > 1
    {
  while (1) {
   if (subIter == 0) {
                //获取当前结点的下一个子结点
    Trie *subTrie = myIter.next(keys[0]);
    if (subTrie == 0)  //当前结点已经没有其他子结点了
     return 0;
    else
                    // subTrie 为 Trie 结点的一个子结点
                    // 对 subTrie 子结点建立一个 迭代器 subIter, 用于遍历子结点的子结点
     subIter = new TrieIter2(*subTrie, keys + 1, level - 1, sort);
   }
            //对结点 subTrie, 递归调用迭代函数 next()
   Trie *next = subIter->next();
   if (next == 0)
            {
                //subTrie 的所有子结点遍历结束
    delete subIter;
    subIter = 0;
   } else
    return next;    //找到 subTrie 的一个子结点, 返回
  }
 }
}
 
 
/* vi: set ts=4 sw=4 et: */
阅读(1242) | 评论(0) | 转发(0) |
0

上一篇:srilm 阅读文档3

下一篇:srilm 阅读文档8

给主人留下些什么吧!~~