zoukankan      html  css  js  c++  java
  • 【字典树应用】联想用户最想输入的词汇

    第一章:抛砖引玉

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

    刚建立的用户词库,用户输入两次“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] = '';
    37             return szDes;
    38         }
    39         cnt = cMax - 'a';
    40         tmphead = tmphead->next[cnt];
    41         len++;
    42         i++;
    43     }
    44     szDes[i] = '';
    45     return szDes;
    46 }
    View Code

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

    第三章:写在结束

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

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

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

  • 相关阅读:
    记录一次线上优化流程
    php ignite 使用问题记录
    invalid contrller specified 错误分析及解决
    koa 2 的 async 和 await 语法
    koa 2 的安装
    vue 自定义组件 v-model双向绑定、 父子组件同步通信的多种写法
    VS2019专业版和企业版激活密钥
    RE:ゼロから始める PKU 生活 episode 2
    CSP-S 2020 游记
    ioi2021集训队作业
  • 原文地址:https://www.cnblogs.com/learn-my-life/p/3746200.html
Copyright © 2011-2022 走看看