原文链接:http://blog.csdn.net/shaoyunzhe/article/details/24712967
1.位置
使用时包含头文件ptree.h 还要编译ptree.c文件。
2.数据结构
2.1struct ptree
- /* Patricia tree top structure. */
- struct ptree
- {
- /* Top node. */
- struct ptree_node *top;
- /* Maximum key size allowed (in bits). */
- u_int32_t max_key_len;
- /* Identifier of the FIB the tree is for - default is 0 */
- u_int32_t id;
- };
- /* Patricia tree node structure. */
- struct ptree_node
- {
- struct ptree_node *link[2];
- #define p_left link[0]
- #define p_right link[1]
- /* Tree link. */
- struct ptree *tree;
- struct ptree_node *parent;
- /* Lock of this radix. */
- u_int32_t lock;
- /* Each node of route. */
- void *info;
-
- /* Key len (in bits). */
- u_int32_t key_len;
- /* Key begins here. */
- u_int8_t key [1];
- };
2.2struct ptree_node
- /* Patricia tree top structure. */
- struct ptree
- {
- /* Top node. */
- struct ptree_node *top;
- /* Maximum key size allowed (in bits). */
- u_int32_t max_key_len;
- /* Identifier of the FIB the tree is for - default is 0 */
- u_int32_t id;
- };
- /* Patricia tree node structure. */
- struct ptree_node
- {
- struct ptree_node *link[2];
- #define p_left link[0]
- #define p_right link[1]
- /* Tree link. */
- struct ptree *tree;
- struct ptree_node *parent;
- /* Lock of this radix. */
- u_int32_t lock;
- /* Each node of route. */
- void *info;
- /* Key len (in bits). */
- u_int32_t key_len;
- /* Key begins here. */
- u_int8_t key [1];
- };
3.1. PAT tree介绍
3.1.1. 应用
- PAT tree是目前信息检索领域应用十分成功的索引方法。主要在字符串子串匹配上有着非常优异的表现,采用半无限长字串作为字符串的查找结构。当有了一个文档的所有半无限长字串的集合,该文档的任何子串都可以定位为某一半无限长字串的前缀。
- 半无限长字串:就是一串字符的顺序子串(从一给定点到串的结尾)。
比如一个字符串CUHK,它的半无限长字串:CUHK、UHK、HK和K。
3.1.2. 基本定义
- PAT tree是由所有可能的无限长字串所构成的一棵树。在PAT tree中通过逐个鉴别半无限长字串的二进制位来判断树中的分支,如果二进制位是0则产生左分支,是1则产生右分支,因此PAT tree是一棵二叉树,树的结点分为外部结点和内部结点。
- 外部结点:也就是树的叶子结点,用来记录半无限长字串的首字符在完整半无限长字串中的开始位置(字符索引)和半无限长字串出现的频次。
- 内部结点:除了叶子结点的所有结点,这个结点是自动添加的,用来记录索引的路径,它有三个基本的数据项:左指针、右指针、比较位。
- 左指针:指向该结点的左子树。
- 右指针:指向该结点的右子树。
- 比较位:记录的是从根结点到达该结点的所有位串最后一个不相同位的位置。用来指示下一步搜索的方向:当位串的比较位为0 时,搜索转向左子树,当该比较位为1时,搜索转向右子树。
3.1.3. 原理说明
- 将所有半无限长字串,用二进制位来表示。它们之间的比较也是以bit位为单位来进行比较。
- 规定所有半无限长字串的长度要一致,结尾没有的空字符用0来填充。
3.1.4. 举例说明
- CUHK 01000011 01010101 01001000 01001011
- UHK0 01010101 01001000 01001011 00000000
- HK00 01001000 01001011 00000000 00000000
- K000 01001011 00000000 00000000 00000000
图 1 PAT tree示例图
3.1.5. 检索过程
- 检索过程就是根据查询字串在PAT tree中从根结点寻找路径的过程。当比较完查询字串所有位置后,搜索路径达到PAT tree的某一结点。
- 若该结点为叶子结点,则判断查询字串是否为叶子结点所指的半无限长字串的前缀,如果判断为真,则查询字串出现的频次即为叶子结点中记录的频次;否则,该查询字串不存在。
- 若该结点为内部结点,则判断查询字串是否为该结点所辖子树中任一叶子结点所指的半无限长字串的前缀。如果判断为真,该子树中所有叶子结点记录的频次之和即为查询字串的出现频次。否则,查询字串不存在。
- 这样,通过PAT tree可以检索原文中任意长度的字串及其出现频次。
3.2. ptree.c文件中PAT tree与一般意义上PAT tree的差异
- 数据结构不同。
- ptree.c文件中存储的关键信息长度可以不同。
- 对于ptree.c文件中的PAT tree内部结点有可能是我们手动添加的结点。
- 利用ptree.c文件中的代码CUHK、UHK、HK和K的存储效果为2图。
图2 ptree.c示例图
3.3. ptree.c文件中PAT tree的介绍
3.3.1. 数据结构
- PAT tree头:即struct ptree,用来管理PAT tree。
- 数据结点:即struct ptree_node。分为通配虚结点和信息结点。
- 通配虚结点:为了使整棵树链接起来,自动添加的结点,结点中的关键字为它左、右子树关键字的公共部分。
- 信息结点:用户手动添加的结点。
图 3 ptree.h数据结构图
3.3.2. 设计原理
- 查找最长匹配的结点:
a. 从头结点开始依次向下比较,取两个key_len中较小值为关键字的比较长度;
b. 如果,比较到某结点不匹配,那么它的父结点为最长匹配结点(父结点为NULL表示没有匹配到结点),直到把待匹配结点的key_len匹配完。 - 添加结点:
a. 从头结点开始,查看是否有完全匹配的结点,如果有,不添加新结点。
b. 如果没有,获取最长匹配的结点。
如果该结点是第一个添加的结点,把该结点定义为头结点。
如果最长匹配的结点为叶子结点,把待匹配结点添加为该叶子结点的孩子结点。
如果最长匹配的结点的孩子结点为叶子结点,把待匹配的结点插入到最长匹配结点和它的孩子结点之间。
否则,需要添加通配结点。 - 删除结点:
待删除结点的lock属性应等于0,info为NULL且左、右子树不能都存在,才可进行删除操作。 - 结点的lock属性:
每个结点都包含一个lock成员,称为结点的访问计数。
新创建的信息结点没有进行任何操作时,它的lock值等于1。
自动添加的通配结点没有进行任何操作时,它的lock值等于0。
添加新结点时,如果存在完全匹配的结点,不添加结点,但原结点的lock值会+1。
每次访问(遍历、查找、添加)该结点时调用ptree_lock_node()函数使lock值+1,访问结束后调用ptree_unlock_node()函数lock值-1,使之恢复原值。
3.3.3. 举例说明
-
有三个结点,假设key_len都为3(bit),关键字的前三个bit位分别是:010、100和101,其中结点1和结点2均为通配结点。
结点1:key_len == 0(bit);
结点2:key_len == 1(bit)。
-
向PAT tree中增加第一个结点010......,如右图4:
图 4
-
向PAT tree中增加第二个结点100......,如右图5:
图 5
- 说 明:当向PAT tree中增加结点100......时,由于和当前存在结点的第一个bit位就不匹配,所以在增加结点的同时,会自动产生一个key_len = 0(bit)的通配虚结点,逻辑图如上图所示,通配虚结点的p_left指针,指向不匹配的bit位为0的结点,p_right指针指向不匹配的bit位 为1的结点。
-
向PAT tree中增加第三个结点101......,如右图6:
图 6
说明:向PAT tree中增加结点101......时,当匹配到第二bit位时,没有结点可以继续匹配,在增加结点的同时,会自动产生一个key_len = 2(bit),key值为10(含有2个bit位)的通配虚结点,逻辑图如上图所示,通配结点的p_left指针,指向不匹配的bit位为0的结点100......,p_right指针指向不匹配的bit位为1的结点101......。
4. 接口
4.1 ptree_init
- 原型
struct ptree *ptree_init (u_int16_t max_key_len) - 功能
初始化树结构 - 参数
max_key_len:树节点key的最大比特位长度 - 返回值
成功返回初始化好的树结构体指针,失败返回NULL
4.2 ptree_top
- 原型
struct ptree_node *ptree_top (struct ptree *tree) - 功能
获得树的根节点并加锁 - 说明
主要用于遍历整个树的节点 - 参数
tree:树结构指针 - 返回值
成功返回加锁了的树根节点,失败返回NULL
4.3 ptree_next
- 原型
struct ptree_node *ptree_next (struct ptree_node *node) - 功能
返回当前节点的下一个节点 - 参数
node:树节点指针 - 返回值
成功返回加锁了的当前节点的下一节点,失败返回NULL
4.4 ptree_next_until
- 原型
struct ptree_node *ptree_next_until (struct ptree_node *node1, struct ptree_node *node2) - 功能
返回当前节点的下一个节点 - 说明
若找到下一节点前遇到node2,返回NULL
- 参数
node1:树节点指针1; node2:树节点指针2 - 返回值
成功返回加锁了的当前节点的下一节点,失败返回NULL
4.5 ptree_node_get
- 原型
struct ptree_node *ptree_node_get (struct ptree *tree, u_char *key, u_int16_t key_len) - 功能
增加树节点到树 - 说明
若key被包含在已有树节点中,则返回相应节点
- 参数
tree:树结构指针; key:键值; key_len:key比特位长度 - 返回值
成功返回加了锁的树节点指针,失败返回NULL
4.6 ptree_node_lookup
- 原型
struct ptree_node *ptree_node_lookup (struct ptree *tree, u_char *key, u_int16_t key_len) - 功能
查找节点 - 说明
根据key在树中查找相应节点,找到包含key值且info非空,key_len相等的则加锁并返回相应节点,找不到则返回NULL
- 参数
tree:树结构指针; key:键值; key_len:key比特位长度(key_len >= 树节点key长度) - 返回值
成功返回加了锁的树节点指针,失败返回NULL
4.7 ptree_lock_node
- 原型
struct ptree_node *ptree_lock_node (struct ptree_node *node) - 功能
对node加锁 - 参数
node:树节点指针 - 返回值
成功返回加了锁的树节点指针
4.8 ptree_node_match1
- 原型
struct ptree_node *ptree_node_match1 (struct ptree *tree, u_char *key, u_int16_t key_len) - 功能
查找节点 - 说明
根据key在树中查找相应节点,返回树节点中的key值匹配查找key值的树节点(不关心info),否则返回NULL
- 参数
tree:树结构指针; key:键值; key_len:key比特位长度 - 返回值
成功返回加了锁的匹配树节点指针(该节点key_len 不小于 搜索key_len),否则返回NULL
4.9 ptree_node_match
- 原型
struct ptree_node *ptree_node_match (struct ptree *tree, u_char *key, u_int16_t key_len) - 功能
查找节点
- 说明
根据key在树中查找相应节点(查找key的值必须大于等于树节点的值,否则直接返回NULL),返回树节点中的key值匹配查找key值且数据节点(info指针)非空的树节点,否则返回NULL
- 参数
tree:树结构指针; key:键值; key_len:key比特位长度 - 返回值
成功返回加了锁的匹配树节点指针(该节点key_len 不大于 搜索key_len),否则返回NULL
4.10 ptree_node_free
- 原型
void ptree_node_free (struct ptree_node *node) - 功能
释放树节点空间 - 参数
node:树节点指针 - 返回值
无
4.11 ptree_finish
- 原型
void ptree_finish (struct ptree *tree) - 功能
释放树 - 参数
tree:树结构指针 - 返回值
无
4.11 ptree_unlock_node
- 原型
void ptree_unlock_node (struct ptree_node *node) - 功能
释放树节点锁,如果锁值为0,删除此节点 - 参数
node:树节点指针 - 返回值
无
4.12 ptree_node_delete
- 原型
void ptree_node_delete (struct ptree_node *node) - 功能
从树中删除节点 - 参数
node:树节点指针 - 返回值
无
4.13 ptree_node_delete_all
- 原型
void ptree_node_delete_all (struct ptree *tree) - 功能
删除树中所有节点 - 参数
tree:树结构指针 - 返回值
无
4.14 ptree_has_info
- 原型
int ptree_has_info (struct ptree *tree) - 功能
检查树中节点 info是否设置 - 参数
tree:树结构指针 - 返回值
1:info设置 0:树空或info未设置
4.15 ptree_key_copy
- 原型
void ptree_key_copy (struct ptree_node *node, u_char *key, u_int16_t key_len) - 功能
为node设置key值 - 参数
node:树节点指针 key:键值 key_len:键值比特位长度 - 返回值
无
4.16 ptree_bit_to_octets
- 原型
int ptree_bit_to_octets (u_int16_t key_len) - 功能
将比特位长度转换成字节长度 - 参数
key_len:比特位长度 - 返回值
字节长度
4.17 ptree_key_match
- 原型
int ptree_key_match (u_char *np, u_int16_t n_len, u_char *pp, u_int16_t p_len) - 功能
key值匹配 - 说明
匹配key(n_len 不大于p_len),如果np的前n_len位与pp的前n_len位相等返回true,否则返回false - 参数
np:key1 n_len; key1比特位长度 pp:key2; p_len:key2比特位长度 - 返回值
如果np包含pp返回true,否则返回false
4.18 ptree_check_bit
- 原型
int ptree_check_bit (struct ptree *tree, u_char *p, u_int16_t key_len) - 功能
根据键值返回0或1 - 说明
根据key值返回0,1决定搜索或添加方向(左孩子或右孩子) - 参数
tree:树结构指针; p:键值指针; ken_len:键值长度; - 返回值
0, 1 (用于判断左孩子还是右孩子)
4.19 ptree_node_match_exclude
- 原型
struct ptree_node * ptree_node_match_exclude (struct ptree *pt, u_char *key1, u_int16_t key_len1, u_char *key2, u_int16_t key_len2) - 功能
保留未用 - 参数
保留
使用说明
1.首先调用ptree_init初始化tree结构
2.然后调用ptree_node_get添加节点
3.通过调用ptree_node_lookup查找匹配key的节点
3调用ptree_node_delete删除指定节点
4.最后调用ptree_finish释放节点
备注:lock索引值在每次有返回节点时都会加一,在处理完该节点后记得ptree_unlock_node该节点!
示例
1.程序添加节点示意图如下:
2.运行结果
3.代码
- #include <stdio.h>
- #include "ptree.h"
- #define BUF_SIZE 3
- /*封装的信息打印函数*/
- void my_printf(struct ptree_node *node);
- /*封装的打印所有树节点信息函数*/
- void show_all_node(struct ptree *tree);
-
- int main(void)
- {
- struct ptree * tree;
- int i;
- char *str = "****str******";
- u_char buf[BUF_SIZE][3] = {{192, 168, 1},{20, 16, 5}, {10, 8, 26}} ;
- u_char buf_1[] = {10, 8};
- u_char buf_2[] = {16};
- struct ptree_node *node = NULL, *next = NULL;
- i= 0;
-
- /* 初始化树 */
- tree = ptree_init (32);
- if (NULL == tree)
- printf("tree is null \n");
-
-
- while(i < BUF_SIZE)
- {
- /* 将节点加入到树*/
- node = ptree_node_get (tree, buf[i], 24);
- if(NULL == node)
- goto finish;
- node->info = str;
- i++;
- show_all_node(tree);
- }
-
- /* 将节点加入到树*/
- ptree_node_get (tree, buf_1, 16);
- show_all_node(tree);
- /* 将节点加入到树*/
- ptree_node_get (tree, buf_2, 8);
- show_all_node(tree);
- /*查找节点 */
- node = ptree_node_lookup(tree, (u_char *)buf[1], 24);
- if (NULL != node)
- {
- printf("-----------------del--------------\n");
- ptree_unlock_node(node);
- my_printf(node);
- printf("-----------------del--------------\n");
- }
-
- /* 从树中删除节点 */
- node->info = NULL;/*info为空才能删除*/
- ptree_unlock_node(node);
-
- /*遍历树节点*/
- show_all_node(tree);
-
- finish:
- /* 释放树 */
- ptree_finish (tree);
- return 0;
- }
- void show_all_node(struct ptree *tree)
- {
- /*遍历树节点*/
- int num;
- struct ptree_node *node = NULL, *next = NULL;
- num = 0;
-
- printf("-----------------traverse--------------\n");
- node = ptree_top (tree);
-
- while (NULL != node)
- { /* 获取下一个节点 */
- next = ptree_next (node);
- my_printf(node);
- node = next;
- num++;
- }
- printf("num = %d\n", num);
- }
-
- void my_printf(struct ptree_node *node)
- {
- int i, j;
- i = 0;
- j = ptree_bit_to_octets(node->key_len);
- while(i < j)
- {
- printf("%d.", node->key[i]);
- i++;
- }
- printf("\tlock = %d\tkey_len = %d\n", node->lock, node->key_len);
-
- }
- #include <stdio.h>
- #include "ptree.h"
- #define BUF_SIZE 3
- /*封装的信息打印函数*/
- void my_printf(struct ptree_node *node);
- /*封装的打印所有树节点信息函数*/
- void show_all_node(struct ptree *tree);
- int main(void)
- {
- struct ptree * tree;
- int i;
- char *str = "****str******";
- u_char buf[BUF_SIZE][3] = {{192, 168, 1},{20, 16, 5}, {10, 8, 26}} ;
- u_char buf_1[] = {10, 8};
- u_char buf_2[] = {16};
- struct ptree_node *node = NULL, *next = NULL;
- i= 0;
- /* 初始化树 */
- tree = ptree_init (32);
- if (NULL == tree)
- printf("tree is null \n");
- while(i < BUF_SIZE)
- {
- /* 将节点加入到树*/
- node = ptree_node_get (tree, buf[i], 24);
- if(NULL == node)
- goto finish;
- node->info = str;
- i++;
- show_all_node(tree);
- }
- /* 将节点加入到树*/
- ptree_node_get (tree, buf_1, 16);
- show_all_node(tree);
- /* 将节点加入到树*/
- ptree_node_get (tree, buf_2, 8);
- show_all_node(tree);
- /*查找节点 */
- node = ptree_node_lookup(tree, (u_char *)buf[1], 24);
- if (NULL != node)
- {
- printf("-----------------del--------------\n");
- ptree_unlock_node(node);
- my_printf(node);
- printf("-----------------del--------------\n");
- }
- /* 从树中删除节点 */
- node->info = NULL;/*info为空才能删除*/
- ptree_unlock_node(node);
- /*遍历树节点*/
- show_all_node(tree);
- finish:
- /* 释放树 */
- ptree_finish (tree);
- return 0;
- }
- void show_all_node(struct ptree *tree)
- {
- /*遍历树节点*/
- int num;
- struct ptree_node *node = NULL, *next = NULL;
- num = 0;
- printf("-----------------traverse--------------\n");
- node = ptree_top (tree);
- while (NULL != node)
- { /* 获取下一个节点 */
- next = ptree_next (node);
- my_printf(node);
- node = next;
- num++;
- }
- printf("num = %d\n", num);
- }
- void my_printf(struct ptree_node *node)
- {
- int i, j;
- i = 0;
- j = ptree_bit_to_octets(node->key_len);
- while(i < j)
- {
- printf("%d.", node->key[i]);
- i++;
- }
- printf("\tlock = %d\tkey_len = %d\n", node->lock, node->key_len);
- }