经常碰到要存一堆的string, 这个时候可以用hash tables, 虽然hash tables 查找很快,但是hash tables不能表现出字符串之间的联系.可以用binary search tree, 但是查询速度不是很理想. 可以用trie, 不过trie会浪费很多空间(当然你也可以用二个数组实现也比较省空间). 所以这里Ternary Search trees 有trie的查询速度快的优点,以及binary search tree省空间的优点.

实现一个12个单词的查找

这个是用二分查找树实现,n是单词个数,len是长度,复杂度是O(logn * n),空间是n*len

这个是用trie实现,复杂度O(n), 空间是 这里是18 * 26(假设只有26个小写字符),随着单词长度的增长等,需要的空间就更多

这个是Ternary search tree, 可以看出空间复杂度和binary search tree 一样, 复杂度近似O(n),常数上会比trie差点.

介绍

Ternary search tree 有binary search tree 省空间和trie 查询快的优点.
Ternary search tree 有三个只节点,在查找的时候,比较当前字符,如果查找的字符比较小,那么就跳到左节点.如果查找的字符比较大,那么就跳转到友节点.如果这个字符正好相等,那么就走向中间节点.这个时候比较下一个字符.
比如上面的例子,要查找”ax”, 先比较”a” 和 “i”, “a” < "i",跳转到"i"的左节点, 比较 "a" < "b", 跳转到"b"的左节点, "a" = "a", 跳转到 "a"的中间节点,并且比较下一个字符"x". "x" > “s” , 跳转到”s” 的右节点, 比较 “x” > “t” 发现”t” 没有右节点了.找出结果,不存在”ax”这个字符

构造方法

这里用c语言来实现
节点定义:

typedef struct tnode *Tptr;
typedef struct tnode {
char s;
Tptr lokid, eqkid, hikid;
} Tnode;

先介绍查找的方法:

int search(char *s) // s是想要查找的字符串
{
Tptr p;
p = t; //t 是已经构造好的Ternary search tree 的root 节点.
while (p) {
if (*s < p->s) { // 如果*s 比 p->s 小, 那么节点跳到p->lokid
p = p->lokid;
} else if (*s > p->s) {
p = p->hikid;
} else {
if (*(s) == '\0') { //当*s 是'\0'时候,则查找成功
return ;
} //如果*s == p->s,走向中间节点,并且s++
s++;
p = p->eqkid;
}
}
return ;
}

插入某一个字符串:

Tptr insert(Tptr p, char *s)
{
if (p == NULL) {
p = (Tptr)malloc(sizeof(Tnode));
p->s = *s;
p->lokid = p->eqkid = p->hikid = NULL;
}
if (*s < p->s) {
p->lokid = insert(p->lokid, s);
} else if (*s > p->s) {
p->hikid = insert(p->hikid, s);
} else {
if (*s != '\0') {
p->eqkid = insert(p->eqkid, ++s);
} else {
p->eqkid = (Tptr) insertstr; //insertstr 是要插入的字符串,方便遍历所有字符串等操作
}
}
return p;
}
}

同binary search tree 一样,插入的顺序也是讲究的,binary search tree 在最坏情况下顺序插入字符串会退化成一个链表.不过Ternary search Tree 最坏情况会比 binary search tree 好很多.

肯定得有一个遍历某一个树的操作

//这里以字典序输出所有的字符串
void traverse(Tptr p) //这里遍历某一个节点以下的所有节点,如果是非根节点,则是有同一个前缀的字符串
{
if (!p) return;
traverse(p->lokid);
if (p->s != '\0') {
traverse(p->eqkid);
} else {
printf("%s\n", (char *)p->eqkid);
}
traverse(p->hikid);
}

应用

这里先介绍两个应用,一个是模糊查询,一个是找出包含公共前缀的字符串, 一个是相邻查询(哈密顿距离小于某个范围)
模糊查询
psearch(“root”, “.a.a.a”) 应该能匹配出baxaca, cadakd 等字符串

void psearch1(Tptr p, char *s)
{
if (p == NULL) {
return ;
}
if (*s == '.' || *s < p->s) { //如果*s 是'.' 或者 *s < p->s 就查找左子树
psearch1(p->lokid, s);
}
if (*s == '.' || *s > p->s) { //同上
psearch1(p->hikid, s);
}
if (*s == '.' || *s == p->s) { // *s = '.' 或者 *s == p->s 则去查找下一个字符
if (*s && p->s && p->eqkid != NULL) {
psearch1(p->eqkid, s + );
}
}
if (*s == '\0' && p->s == '\0') {
printf("%s\n", (char *) p->eqkid);
}
}

解决在哈密顿距离内的匹配问题,比如hobby和dobbd,hocbe的哈密顿距离都是2

void nearsearch(Tptr p, char *s, int d) //s 是要查找的字符串, d是哈密顿距离
{
if (p == NULL || d < )
return ;
if (d > || *s < p->s) {
nearsearch(p->lokid, s, d);
}
if (d > || *s > p->s) {
nearsearch(p->hikid, s, d);
}
if (p->s == '\0') {
if ((int)strlen(s) <= d) {
printf("%s\n", (char *) p->eqkid);
}
} else {
nearsearch(p->eqkid, *s ? s + : s, (*s == p->s) ? d : d - );
}
}

搜索引擎输入bin, 然后相应的找出所有以bin开头的前缀匹配这样类似的结果.比如bing,binha,binb 就是找出所有前缀匹配的结果.

void presearch(Tptr p, char *s) //s 是想要找的前缀
{
if (p == NULL)
return;
if (*s < p->s) {
presearch(p->lokid, s);
} else if (*s > p->s) {
presearch(p->hikid, s);
} else {
if (*(s + ) == '\0') {
traverse(p->eqkid); // 遍历这个节点,也就是找出包含这个节点的所有字符
return ;
} else {
presearch(p->eqkid, s + );
}
}
}

总结

1.Ternary search tree 效率高而且容易实现
2.Ternary search tree 大体上效率比hash来的快,因为当数据量大的时候hash出现碰撞的几率也会大,而Ternary search tree 是指数增长
3.Ternary search tree 增长和收缩很方便,而 hash改变大小的话则需要拷贝内存重新hash等操作
4.Ternary search tree 支持模糊匹配,哈密顿距离查找,前缀查找等操作
5.Ternary search tree 支持许多其他操作,比如字典序输出所有字符串等,trie也能做,不过很费时.

参考:http://drdobbs.com/database/184410528?pgno=1

Ternary Search Trees 三分搜索树的更多相关文章

  1. [LeetCode] Unique Binary Search Trees 独一无二的二叉搜索树

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  2. [LeetCode] Unique Binary Search Trees II 独一无二的二叉搜索树之二

    Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. For e ...

  3. [Swift]LeetCode95. 不同的二叉搜索树 II | Unique Binary Search Trees II

    Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1 ...

  4. [Swift]LeetCode96. 不同的二叉搜索树 | Unique Binary Search Trees

    Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n? Example ...

  5. [LeetCode] 96. Unique Binary Search Trees(给定一个数字n,有多少个唯一二叉搜索树) ☆☆☆

    [Leetcode] Unique binary search trees 唯一二叉搜索树 Unique Binary Search Trees leetcode java 描述 Given n, h ...

  6. [Leetcode] Unique binary search trees 唯一二叉搜索树

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  7. leetCode 95.Unique Binary Search Trees II (唯一二叉搜索树) 解题思路和方法

    Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. For e ...

  8. 【LeetCode-面试算法经典-Java实现】【096-Unique Binary Search Trees(唯一二叉搜索树)】

    [096-Unique Binary Search Trees(唯一二叉搜索树)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given n, how many s ...

  9. [LeetCode] 96. Unique Binary Search Trees 独一无二的二叉搜索树

    Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n? Example ...

随机推荐

  1. 项目中Zookeeper配置参数笔记

    ZooKeeper是以Fast Paxos算法为基础的,Paxos 算法存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxo ...

  2. Python导入自定义包或模块

    一般我们会将自己写的 Python 模块与 Python 自带的模块分开存放以达到便于维护的目的. Python 运行环境在查找模块时是对 sys.path 列表进行遍历,如果我们想在运行环境中添加自 ...

  3. POJ 2352 Stars 线段树 数星星

    转载自 http://www.cnblogs.com/fenshen371/archive/2013/07/25/3214927.html 题意:已知n个星星的坐标.每个星星都有一个等级,数值等于坐标 ...

  4. 异步|同步&阻塞|非阻塞

    异步|同步:区别在于发出一个功能调用时,是否马上得到返回结果 阻塞|非阻塞:区别在于调用结果返回之前,当前线程是否挂起 node.js:单线程.异步非阻塞模型 单线程与异步不矛盾,与并发是矛盾的 ht ...

  5. 跳过 centos部署 webpy的各种坑

    用centos部署webpy发现的各种坑: 1.python 版本: 2.中文编码: 3.web模块路径: 在命令行里输入python,能import web,但是网站错误报告一直报告没有找到web模 ...

  6. Windows下Python连接数据库(mysql, mongodb)

    一 实验平台 1 os: win7 64位旗舰版sp1 2 python: 2.7.10 x64 二 连接数据库 1 连接 mysql数据库 (1)下载mysql(5.6.25-winx64) 建议下 ...

  7. Google Developing for Android 三 - Performance最佳实践

    Google Developing for Android 三 - Performance最佳实践 发表于 2015-06-07   |   分类于 Android最佳实践 原文 Developing ...

  8. canvas生成二维码,并下载保存为本地的图片

    function getTicket(id,salt){//qrcode生成canvas二维码 $(".zc-mask").show(); $(".edit-box&qu ...

  9. Android自学笔记:Git下载源代码

    Info:做J2ME几年了,现在基本没有公司用了,是时候向Android领域进军了. 自学中,难免会有疏漏,有问题请及时提出,共同学习共同进步. 2014-10-13:初版 2014-10-14:添加 ...

  10. 显示全部select change 异常

    异常信息(异常类型:Genersoft.Platform.Core.Error.GSPException)异常提示:调用方法SelectChange发生异常,详细请看内部异常信息!异常信息:调用方法S ...