系统的学过编程的人应该都知道,有一门基础课:《数据结构与算法》,这门课很重要,但是许多人却不怎么重视,导致后来算法学习频频碰壁。我不会给大家系统的讲数据结构,但是我会给大家讲一些很有趣的结构,下来的学习还是得靠大家自己努力啦。
这次讲的是模糊查询,在模糊查询之前,我们先看一下如何精确查询。
比如我想要在一堆(n个)字符串里查找我的名字‘liuruiyang’,那么我需要和这堆(n个)字符串里的每个字符串作比较,而每次比较时间复杂度都是O(len),所以总的时间复杂度就是O(n*len);
既然精确查询如此,模糊查询也不会差太多,模糊查询,当你查询‘liu’时,会出现所有开头为‘liu’的字符串,既然这样,我依然要跟每个字符串作比较,总的时间复杂度依然是O(n*len);
我们知道,英文字母有26个,不考虑大小写和符号的话,我们使用26个字母就可以表示所有的单词,那么对于一个单词而言,这个单词由字母组成,那么每个单词就由两部分组成:
①:单词的长度;
②:每个位置的字母。
单词的长度很容易,一个计数器就可以搞定了,而每个位置的字母却比较麻烦,因为每个字母都要保存下来,这样会大大增加操作的复杂程度,其实我们可以注意到,一共有26个字母,并且这些字母是连续的,这样我们就可以创建一个长度为26的数组,用这个数组的下标可以和字母形成一一对应的关系,0~25分别对应a~z。

这张图里的树结构,每条边代表一个字母,而一个节点对应的就是该节点的路径,而我们将数组下标和字母组成了一一对应的关系,字母对应的下标是该字母的字符值减去字母a的字符值,例:t对应的下标是’t’-‘a’;
上面的图里,有一点还没有表示清楚,那就是如何判断一个单词是否存在,其实很简单,我们只需要增加一个标记位,对单词的存在与否进行标记就好了。那么来看结构体吧:

typedef struct TrieTree
{int isStr;    //标记这个单词是否存在struct TrieTree * next[N];//N为26
}Trie;

举个例子吧,比如我已经在树中插入了”liuruiyang”,”liuyufu”,”lijing”这三条记录,由于字母表示边单词本身表示路径,那么可以肯定,这三条记录的公共边一定有”l”,”i”两个字母,既然我们知道它们的公共边,那么就可以轻松的得到它们的最近公共祖先,也就是”li”这个节点,注意:这个节点不一定是单词,它有可能不存在。
既然我们在树中得到了它们的最近公共祖先,那么它们就都可以通过这个公共祖先的子树进行遍历得到。反过来说,开头为”li”的单词,一定是”li”节点的子孙,它们都可以通过遍历”li”的子孙得到。这样,我们查找开头为”li”的节点,就不需要遍历整个树了,只需要遍历”li”节点的子树就好了。

这样,模糊查询就分析完了。下面开始编码实现吧。
当然,我们只有模糊查询算法是不行的,至少,你要能给树里加入单词啊。

首先是增:我们只需要将字符串按字符读出来,每个字符都代表一个边,如果边不存在,则创建这个边,当字符串读完时,将最后的那个节点的标记置为1,表示这个字符串已经存储在树里。
思想很简单,下面看代码吧:

int Insert(char * str, Trie * root)
{int len = strlen(str);Trie * temp = root;for (int i = 0; i < len; i++){int index = str[i]-'a';if (temp->next[index] == NULL){temp->next[index] = (Trie *)malloc(sizeof(Trie));temp = temp->next[index];temp->isStr = 0;for (int i = 0; i < N; i++){temp->next[i] = NULL;}}else{temp = temp->next[index];}}if (temp->isStr == 1)return 0;temp->isStr = 1;return 1;
}

函数返回0时说明这个单词已经在树中,不需要添加,返回1时说明添加成功。

下来是删除:
当然,删除有两种删法:
①:找到字符串对应的节点,直接将该节点对应的标记值改为0,表示该字符串不在树中。
②:找到字符串对应的节点,检查该节点是否有子孙节点,如果有则将该节点对应的标记值改为0,如果没有,就得删除该节点,并且如果该节点的祖先节点对其余节点不产生影响的话,要递归删除该节点的祖先节点。
两种方法各有利弊,第一种的好处就是删除时速度快,并且下次增加时也会更方便,坏处就是大量删除后会产生大量冗余节点,冗余节点会使查询时的效率变低,第二种方法的好处与坏处正好和第一种相反。
当然,字符串特别多时,使用第一种方法删除的话产生的冗余节点与原有的节点相比不值一提,(其实我是为了图方便),所以简单的使用了第一种方法。

int Delete(char * str, Trie * root)
{int len = strlen(str);Trie * temp = root;for (int i = 0; i < len; i++){int index = str[i]-'a';if (temp->next[index])temp = temp->next[index];elsereturn 0;}if (temp->isStr == 0)   return 0;temp->isStr = 0;return 1;
}

返回1表示成功删除,返回0表示不存在这个字符串。

下面就是模糊查询了:
按照先前说的思想,模糊查询实际上是首先精确查询目标字符串,得到该节点后,只需要递归遍历它的子树就好了。

int Fuzzys(char * str, Trie * root)
{int len = strlen(str);Trie * temp = root;for (int i = 0; i < len; i++){int index = str[i]-'a';if (temp->next[index])temp = temp->next[index];elsereturn 0;}Trie * newr = temp;int count = 0;Visit(str,newr,N,count);return 0;
}int Visit(char * str, Trie * root, int n, int count)
{if (root->isStr == 1){printf("%s",str);for (int i = 0; i < count; i++)printf("%c",a[i]+'a');printf("\n");}for (int i = 0; i < n; i++){if (root->next[i] != NULL){a[count] = i;Visit(str,root->next[i],n,count+1);}}return 0;
}

上面的代码由两个函数组成,Fuzzys()函数就是模糊查询的主函数,通过这个函数找到目标节点,然后Fuzzys()函数里面调用了Visit()函数,用来访问该节点的子孙。在整个过程中使用了一个全局不定长数组记录了节点的路径,输出时使用这个数组里的值进行输出。
我没有上精确查询的代码,其实认真看的同学应该已经发现了,精确查询的代码跟模糊查询应该差不太多。的确,基本没什么差别,只需要在Fuzzys()函数上稍微改动几行就行了。怎么改大家下去改吧,很简单,我就不多说了。

数据结构与算法本来就是一家,是密不可分的。许多算法之所以快,是因为它们使用了合理的数据结构。
我是算法吹,以后会给大家带来更多精彩的算法。

结构的艺术:模糊查询相关推荐

  1. like模糊查询是否走索引

    1.模糊查询 后通配 走索引 前通配 走全表 2.where条件用in或or 不会走索引索引的本质是平衡b+数,是为了方便查询的平衡多路查找树 B-Tree相比,B+Tree有以下不同点: 每个节点的 ...

  2. MySQL模糊查询再也用不着 like+% 了!

    欢迎关注方志朋的博客,回复"666"获面试宝典 前言 我们都知道 InnoDB 在模糊查询数据时使用 "%xx" 会导致索引失效,但有时需求就是如此,类似这样的 ...

  3. vue 带全选和多选的表格怎么写_vue实现下拉列表多选全选以及模糊查询的vue组件...

    vue实现下拉列表多选全选以及模糊查询的vue组件 发布时间:2018-09-12 17:41, 浏览次数:3776 , 标签: vue <>前端,有时有这样的需求,需要一个下拉列表,还要 ...

  4. c3p0 参数 模糊查询_mybatis之动态sql,模糊查询,结果集处理,mybatis分页及特殊字符处理...

    目标及项目目录结构 目标 1.mybatis动态sql 2.模糊查询 3.查询返回结果集的处理 4.分页查询 5.特殊字符处理 项目的目录结构 1.mybatis动态sql If.trim.forea ...

  5. oracle遍历表做查询,oracle 语句之对数据库的表名就行模糊查询,对查询结果进行遍历,依次获取每个表名结果中的每个字段(存储过程)...

    语句的执行环境是plsql的sql窗口, 语句的目的是从整个数据库中的所有表判断 不等于某个字段的记录数 . 代码如下: declare s_sql clob:=''; -- 声明一个变量,该变量用于 ...

  6. Mybatis-增删改查模糊查询分页注解(普通类型参数、引用类型参数、Map类型参数)

    代码地址https://www.lanzouw.com/ihOmnwdtbrc 请先看项目目录结构,然后再写代码.target是编译后的输出目录,只需要保证src目录目录一致就行. 1.创建数据库my ...

  7. cad模糊查询符号_万能模糊查询SQL

    ****************************************************************** * 功能:万能模糊查询SQL * 时间:2015/1/30 16: ...

  8. 关系数据库SQL之基本数据查询:子查询、分组查询、模糊查询

    前言 上一篇关系数据库常用SQL语句语法大全主要是关系型数据库大体结构,本文细说一下关系型数据库查询的SQL语法. 语法回顾 SELECT [ALL|DISTINCT] <目标列表达式>[ ...

  9. 第二章mapper接口 和模糊查询

    mybatis进行CURD 方式一:基于映射文件+SqlSession 特点:依赖SqlSession对象方法,实现CURD.比如selectList(),selectOne(),insert()- ...

最新文章

  1. aws rds mysql 连接_AWS Lambda RDS MySQL数据库连接接口
  2. mongodb默认的用户名密码_MongoDB 设置账号和密码
  3. Java 虚拟机诊断利器
  4. 【数据结构与算法】常用算法
  5. HDU 2298 Toxophily 【三分算法 or 直接推导物理公式】
  6. 商业价值:谷歌眼中的搜索未来
  7. 服务器安装python虚拟环境
  8. putty 使用perm密钥文件登陆堡垒机
  9. PHP验证时有用的几段代码
  10. JAVA获取word书签内容_Java 操作Word书签(一):添加、删除、读取书签
  11. 多显示器屏幕枚举方法
  12. Verilog学习之路(7)— 数字加法器
  13. pix2pixhd_基于pix2pixHD的行人图像生成
  14. 一行代码完成Java的Excel读写 侵立删
  15. 无我编程的10条诫律
  16. Xtrabackup备份与恢复+异机远程流式备份
  17. java毕业设计基于spring框架的论坛网站项目设计和源码
  18. 我的QQ密保卡,不许偷看哦
  19. 联想笔记本暗屏几乎看不见_联想笔记本屏幕突然变得很暗,基本看不见,怎么办,十万火急,是不是屏幕坏了???...
  20. div的display属性和visibility属性

热门文章

  1. 电脑桌面两个计算机图标怎么删除,电脑桌面上出现好几个音量图标,还是重叠的,删也删不掉,怎么办...
  2. SuperMap iServer发布ArcGIS瓦片
  3. 发邮件(通过发邮件 激活用户/激活链接)
  4. 解决 pyecharts 地图不显示的问题
  5. 测试小白的心酸路之测试初体验
  6. 国家培训python
  7. java 中 u表示啥意思_“U”到底是什么意思呢?
  8. 【学习笔记】专业术语
  9. 渣科的第一次蓝桥杯2017
  10. 微信朋友圈如何自动点赞