【字典树应用】联想用户最想输入的词汇

第一章:抛砖引玉

字典树是一种基于链表的数据结构,以统计词频并返回用户最想输入的词汇为例,分享一下字典树的应用心得。

刚建立的用户词库,用户输入两次“hilili”, 输入一次“hilucy”,此时用户再次输入“hi”,我们应该联想到用户可能要输入的单词是“hilili”,以下为统计示例图。

       【字典树应用】联想用户最想输入的词汇

字典树是一种兼顾空间和时间的数据结构,利用公共前缀节约空间,减少比较次数以提高查询和插入效率。

字典树的常见用途:保存大量字符串并进行统计(静态字典树,猜测金山词霸或xxx单词王都有利用到字典树) 、统计用户输入词频和联想用户想要输入的词汇(动态字典树,即用户词库)、字符串排序(域名排序等)。

 第二章:小试牛刀

1.定义一个字典树:

        

1  struct TrieNode
2 {
3      struct TrieNode* next[ 26];
4     unsigned  int     count;
5 };
View Code

 

2.创建一个字典树:

        

1 TrieNode* CreateTrieNode()
2 {
3     TrieNode* head = (TrieNode*)malloc( sizeof(TrieNode));
4     memset(head,  0sizeof(TrieNode));
5      return head;
6 }
View Code

3.查找字典树:

        

 1  // 遍历字符串,如果字符串未全部写入路径则查找失败,否则返回词频
 2  unsigned  int FindTrieNode(TrieNode* head,  char* str)
 3 {
 4      if(NULL == head)
 5          return  0;
 6     TrieNode* tmphead = head;
 7      int i =  0, cnt =  0;
 8      while(str[i])
 9     {
10         cnt = str[i] -  ' a ';
11          if(NULL != tmphead->next[cnt])
12         {
13             tmphead = tmphead->next[cnt];
14             i++;
15         }
16          else
17         {
18              return  0;
19         }
20     }
21      return tmphead->count;
22 }
View Code

4.插入字典树:

       

 1  // 遍历整个字符串,创建不存在的字典树路径,并将整个路径的词频++
 2  // 如果malloc失败,表示进程内存不足,插入字符串失败
 3  BOOL InsertTrieNode(TrieNode* head,  char* str)
 4 {
 5      if(NULL == head)
 6     {
 7         head = (TrieNode*)malloc( sizeof(TrieNode));
 8         memset(head,  0sizeof(TrieNode));
 9     }
10      int i =  0, cnt =  0;
11     TrieNode* tmphead = head;
12      while(str[i])
13     {
14         cnt = str[i] -  ' a ';
15          if(NULL == tmphead->next[cnt])
16         {
17              if(NULL != (tmphead->next[cnt] = (TrieNode*)malloc( sizeof(TrieNode))))
18                 memset(tmphead->next[cnt],  0sizeof(TrieNode));
19              else
20                  return FALSE;
21         }
22         tmphead = tmphead->next[cnt];
23         i++;
24         tmphead->count++;
25     }
26      return TRUE;
27 }
View Code

 

5.统计用户输入频率

        

 1  // 更新用户输入频率
 2  void UpdateFrequence(TrieNode* head,  char* str)
 3 {
 4      int i =  0, cnt =  0;
 5      while(str[i])
 6     {
 7         cnt = str[i] -  ' a ';
 8         head->next[cnt]->count++;
 9         head = head->next[cnt];
10         i++;
11     }
12 }
View Code

  6.联想最可能的词汇

        

 1  // 根据已输入的字符串联想出词频最高的一个字符串,即最有可能是用户想要输入的完整字符串。
 2  // 已输入的字符串必须已经插入字典树
 3  // 如果存在路径包含的情况,则总是返回最长路径:比如先插入pretty,再插入prettygirl,输入pr则联想词汇为prettygirl
 4  char* GetWantedWord(TrieNode* head,  char* szSrc,  char* szDes, size_t stDesLen)
 5 {
 6      if(NULL == head || NULL == szSrc || stDesLen <= strlen(szSrc))
 7          return szSrc;
 8     TrieNode* tmphead = head;
 9      int i =  0, cnt =  0;
10     unsigned  int uiMax =  0, len =  0;
11      char         cMax =  ' a ';
12      while(szSrc[i])
13     {
14         cnt = szSrc[i] -  ' a ';
15         tmphead = tmphead->next[cnt];
16         szDes[i] = szSrc[i];
17         len++;
18         i++;
19     }
20      while( 1)
21     {
22         uiMax =  0;
23         cMax =  ' a ';
24          for( int j =  0; j <  26; j++)
25         {
26              if(NULL != tmphead->next[j] && tmphead->next[j]->count > uiMax)
27             {
28                 uiMax = tmphead->next[j]->count;
29                 cMax = j +  ' a ';
30             }
31         }
32          if(len < stDesLen && uiMax >  0)
33             szDes[i] = cMax;
34          else
35         {
36             szDes[i] =  ' \0 ';
37              return szDes;
38         }
39         cnt = cMax -  ' a ';
40         tmphead = tmphead->next[cnt];
41         len++;
42         i++;
43     }
44     szDes[i] =  ' \0 ';
45      return szDes;
46 }
View Code

 

  7.只是功能测试,不涉及性能,测试代码略。

第三章:写在结束

程序还有待完善,随着程序的运行时间,用户输入越来越多,也出现了不少手误,怎么剔除掉次数较少的手误统计,如果两到三个用户在轮流使用该程序,怎么在用户切换时迅速反应过来?

字典树和hash表都能显著提高程序设计和code的效率,可以说是程序员手中的利器,值得善加利用。

多扯一句吧:怎样才能天然拥有贝克汉姆的经典发型,梳子往右梳,睡觉右躺,左右头发聚一线,这就有了。

你可能感兴趣的:(字典树)