ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Trie树学习及python实现

2021-04-06 15:02:16  阅读:225  来源: 互联网

标签:node cur sequence Trie self children 学习 python 节点


一、定义

  Eg.一个保存了8个单词的字典树的结构如下图所示,8个单词分别是:“A”,“to”,“tea”,“ted”,“ten”,“i” ,“in”,“inn”。

 

 

 

  1. 字典(Trie)树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。
  2. 应用:统计和排序大量的字符串(但不仅限于字符串),经常被搜索引擎系统用于文本词频统计、前缀匹配用来搜索提示,也常用于计算左右信息熵、计算点互信息等,进阶版可与默克尔树融合成Merkle Patricia Tree用于区块链。
  3. 优点:最大限度地减少无谓的字符串比较,利用字符串的公共前缀来降低查询时间,空间换时间,因而查询效率高。
  4. 缺点:如果大量字符串没有共同前缀时很耗内存。 
  5. 性质
    1.   根节点不包含字符,除根节点外每一个节点都只包含一个字符。
    2.   从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
    3.   每个节点的所有子节点包含的字符都不相同。
  6. 时间复杂度:创建时间复杂度为O(L),查询时间复杂度是O(logL),查询时间复杂度最坏情况下是O(L),L是字符串的长度。

二、基本操作(创建、插入和查找)

 1 class TrieNode:
 2     def __init__(self):
 3         # 创建字典树的节点
 4         self.nodes = dict()
 5         self.is_leaf = False
 6     
 7     def insert(self, word: str):
 8         """
 9         基本操作:插入
10         Parameters
11         ----------
12         word : str
13         Returns
14         -------
15         None.
16 
17         """
18         curr = self
19         for char in word:
20             if char not in curr.nodes:
21                 curr.nodes[char] = TrieNode()
22             curr = curr.nodes[char]
23         curr.is_leaf = True
24     def search(self, word: str):
25         """
26         基本操作:查找
27         Parameters
28         ----------
29         word : str
30 
31         Returns
32         -------
33 
34         """
35         curr = self
36         for char in word:
37             if char not in curr.nodes:
38                 return False
39             curr = curr.nodes[char]
40         return curr.is_leaf

三、应用实例

  1 class TrieNode(object):
  2     def __init__(self, value=None, count=0, parent=None):
  3         # 值
  4         self.value = value
  5         # 频数统计
  6         self.count = count
  7         # 父节点
  8         self.parent = parent
  9         # 子节点,{value: TrieNode}
 10         self.children = {}
 11 
 12 class Trie(object):
 13     def __init__(self):
 14         # 创建空的根节点
 15         self.root = TrieNode()
 16     
 17     def insert(self, sequence):
 18         """
 19         基本操作:插入一个单词序列
 20         :param sequence:列表
 21         :return:
 22         """
 23         cur_node = self.root  # 当前节点
 24         for item in sequence:  #遍历单词的每一个字符
 25             if item not in cur_node.children:  # 当前字符不在当前节点的子节点中
 26                 # 插入节点
 27                 child = TrieNode(value=item, count=1, parent=cur_node)  # 新建节点
 28                 cur_node.children[item] = child  # 将该节点接入当前节点的子节点
 29                 cur_node = child  # 当前节点更新为子节点
 30             else:
 31                 # 更新节点
 32                 cur_node = cur_node.children[item]  # 更新当前节点为已存在的子节点
 33                 cur_node.count += 1  # 当前节点的计数+1
 34     
 35     def search(self, sequence):
 36         """
 37         基本操作,查询是否存在完整序列
 38 
 39         Parameters
 40         ----------
 41         sequence : 列表
 42 
 43         Returns:
 44 
 45         """
 46         cur_node = self.root  # 根节点
 47         mark = True  # 标记是否找到
 48         for item in sequence:  # 遍历序列
 49             if item not in cur_node.children:  # 判断子结点中是否存在
 50                 mark = False
 51                 break
 52             else:  # 存在
 53                 cur_node = cur_node.children[item]  # 更新当前节点
 54         # 如果还有子节点,说明序列并非完整,则查找不成功【此处可视情况找到前缀即反馈成功】
 55         if cur_node.children:
 56             mark = False
 57         return mark
 58     
 59     def delete(self, sequence):
 60         """
 61         基本操作,删除序列,准确来说是减少计数
 62 
 63         Parameters
 64         ----------
 65         sequence : 列表
 66 
 67         Returns
 68 
 69         """
 70         mark = False
 71         if self.search(sequence):
 72             mark = True
 73             cur_node = self.root  # 获取当前节点
 74             for item in sequence:  # 遍历序列
 75                 cur_node.children[item].count -= 1  # 节点计数减1
 76                 if cur_node.children[item].count == 0:  # 如果节点计数为0
 77                     cur_node.children.pop(item)  # 删除该节点
 78                     break
 79                 else:
 80                     cur_node = cur_node.children[item] # 更新当前节点
 81         return mark
 82     
 83     def search_part(self, sequence, prefix, suffix, start_node=None):
 84         """
 85         递归查找子序列,返回前缀和后缀节点(此处仅返回前后缀的内容与频数)
 86 
 87         Parameters
 88         ----------
 89         sequence : 列表
 90             DESCRIPTION.
 91         prefix : TYPE
 92             前缀字典,初始传入空字典
 93         suffix : TYPE
 94             后缀字典,初始传入空字典
 95         start_node : TYPE, optional
 96             起始节点,用于子树的查询
 97 
 98         Returns
 99         -------
100         None.
101 
102         """
103         if start_node:  # 该节点不为空,获取第一个子结点
104             cur_node = start_node
105             prefix_node = start_node.parent
106         else:  # 定位到根节点
107             cur_node = self.root 
108             prefix_node = self.root
109         mark = True
110         # 必须从第一个结点开始对比
111         for i in range(len(sequence)):
112             if i == 0:
113                 if sequence[i] != cur_node.value:  # 第一个节点不相同,找到第一个相同值的节点
114                     for child_node in cur_node.children.values():  # 该节点的每一个分支递归查找
115                         self.search_part(sequence, prefix, suffix, child_node)
116                     mark = False
117                     break
118             else:
119                 if sequence[i] not in cur_node.children:  # 后序列中值不在当前节点孩子中
120                     for child_node in cur_node.children.values():
121                         self.search_part(sequence, prefix, suffix, child_node)
122                     mark = False
123                     break
124                 else:
125                     cur_node = cur_node.children[sequence[i]]  # 继续查找序列
126         if mark:  # 找到序列
127             if prefix_node.value in prefix:  # 当前节点加入序列中
128                 prefix[prefix_node.value] += cur_node.count
129             else:
130                 prefix[prefix_node.value] = cur_node.count
131             for suffix_node in cur_node.children.values():
132                 if suffix_node.value in suffix:
133                     suffix[suffix_node.value] += suffix_node.count
134                 else:
135                     suffix[suffix_node.value] = suffix_node.count
136             # 即使找到一部分还需继续查找子节点
137             for child_node in cur_node.children.values():
138                 self.search_part(sequence, prefix, suffix, child_node)
139 
140 if __name__ == "__main__":
141     trie = Trie()
142     texts = [["葬爱", "少年", "葬爱", "少年", "慕周力", "哈哈"], ["葬爱", "少年", "阿西吧"], ["烈", "烈", "风", "中"], ["忘记", "了", "爱"],
143              ["埋葬", "了", "爱"]]
144     
145     for text in texts:
146         trie.insert(text)
147     markx = trie.search(["忘记", "了", "爱"])
148     print(markx)
149     markx = trie.search(["忘记", "了"])
150     print(markx)
151     markx = trie.search(["忘记", "爱"])
152     print(markx)
153     markx = trie.delete(["葬爱", "少年", "王周力"])
154     print(markx)
155     prefixx = {}
156     suffixx = {}
157     trie.search_part(["葬爱", "少年"], prefixx, suffixx)
158     print(prefixx)
159     print(suffixx)

参考文献

[1]. 百度百科词条【字典树】:https://baike.baidu.com/item/字典树/9825209   

[2]. 看动画轻松理解「Trie树」,https://www.cxyxiaowu.com/1873.html

[3]. 字典树python实现,https://blog.csdn.net/danengbinggan33/article/details/82151220

[4]. 吐血攻略Trie,包教包会,https://leetcode-cn.com/problems/short-encoding-of-words/solution/99-java-trie-tu-xie-gong-lue-bao-jiao-bao-hui-by-s/

标签:node,cur,sequence,Trie,self,children,学习,python,节点
来源: https://www.cnblogs.com/caoer/p/14621859.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有