【每日算法Day 84】面试必考题:Trie(字典树/前缀树)的实现

简介: 实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。

题目链接


LeetCode 208. 实现 Trie (前缀树)[1]

题目描述


实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。

示例1

Trietrie=newTrie();
trie.insert("apple");trie.search("apple");   //返回truetrie.search("app");    
//返回falsetrie.startsWith("app"); 
//返回truetrie.insert("app");   trie.search("app");   
//返回true

说明:


题解


字典树主要支持插入字符串、查询字符串是否在字典树中、查询字典树中是否存在某个前缀等操作,我这里还额外实现了一下 c++ 版本的删除字符串操作。

初始化字典树


初始化的时候,根结点为空,不用来放任何字符,所有字符串都是从下一层子结点开始存储。

每个结点有 26 个指针,指向下一层子结点,每个指针代表着下一个不同的字母。

每个结点还保存了一个变量 isEnd ,用来表示该结点是不是某个字符串结束的位置。

插入字符串


从根结点往下递归,如果字符串中下一个字母对应的子结点为空,那就新建一个结点再递归,否则的话就直接递归下去。

最后把最后一个结点的 isEnd 设置为 1,表示这个结点是字符串的结束位置。

查询字符串


从根结点往下递归查找,如果字符串还没遍历结束,但是结点已经空了,说明字符串不在字典树中。否则的话一直查找到最后一个字符,然后看对应结点的 isEnd 是 1 还是 0,如果是 1 ,就存在字符串,否则不存在。

查询字符串前缀


和查询字符串过程一模一样,唯一的区别就是最后不用看最后一个结点的 isEnd 了,直接返回 true 。因为既然都查询到了最后一个字符了,说明这个前缀一定存在。

删除字符串


这个是我自己实现的,一般来说字典树很少用到删除操作。

首先整体框架是和查询字符串类似的,从根结点往下递归查询,然后用一个栈保存查询到的结点。

如果查询过程中直接遇到了空结点,就直接返回,因为都不存在字符串,就不用删除了。然后判断最后一个结点的类型。

如果它的 isEnd 是 0,说明字符串不存在,那就直接返回不用删了。

如果它不是叶子结点,说明后面还接着字符串呢,那也不用删了,只要把该结点的 isEnd 设置为 0 就行了。

否则的话它就是叶子结点,那么就直接删除这个结点,并且从栈里出栈。

然后从栈里最后一个结点开始删除,直到栈顶的结点不是叶子结点(表示字典树中存在删除字符串的相同前缀字符串)或者 isEnd 是 1(表示字典树中存在删除字符串的前缀子串)。

代码


具体实现上面,c++ 我采用的结构体指针来构建出了一颗树。而 python 我直接用的嵌套的字典,并没有真正的构建出树,只有一个类,这样还挺方便的,但是删除操作有点麻烦,暂时就不写了。

c++

classTrie {
public:    
boolisEnd; 
vector<Trie*>next;
Trie() {    
isEnd=false;  
next=vector<Trie*>(26, 0);    }
~Trie() {    
for (autop : next) deletep;    }   
voidinsert(stringword) {    
Trie*node=this;   
for (autoc : word) {      
if (node->next[c-'a'] ==NULL) {       
node->next[c-'a'] =newTrie(); 
            }            node=node->next[c-'a'];   
        }     
node->isEnd=true;  
    }
voiddel(stringword) {   
stack<Trie*>st;    
Trie*node=this;     
for (autoc : word) {   
node=node->next[c-'a'];     
st.push(node);      
if (node==NULL) return;   
        }     
if (!(node->isEnd)) return;  
if (!isLeaf(node)) {   
node->isEnd=false;  
return;   
        }        deletenode; 
st.pop();      
while (!st.empty()) {   
node=st.top();     
st.pop();      
if (isLeaf(node) &&!(node->isEnd)) deletenode;   
elsebreak;      
        }  
    }        boolsearch(stringword) {    
Trie*node=this;     
for (autoc : word) {       
node=node->next[c-'a'];   
if (node==NULL) returnfalse;
        }      
returnnode->isEnd; 
    }      
boolstartsWith(stringprefix) {    
Trie*node=this;    
for (autoc : prefix) {  
node=node->next[c-'a'];
if (node==NULL) returnfalse; 
        }        returntrue;  
    }
boolisLeaf(Trie*node) {  
for (autop : next) {   
if (p) returnfalse; 
        }       
returntrue; 
    }
};

python

classTrie: 
def__init__(self):    
self.nxt= {}
definsert(self, word: str) ->None:    
node=self.nxtforcinword:     
ifcnotinnode:    
node[c] = {}   
node=node[c]  
node['#'] =Truedefsearch(self, word: str) ->bool:   
node=self.nxtforcinword:   
ifcnotinnode:   
returnFalsenode=node[c]    
return'#'innodedefstartsWith(self, prefix: str) ->bool:  
node=self.nxtforcinprefix:    
ifcnotinnode: 
returnFalsenode=node[c]   
returnTrue

参考资料

[1]

LeetCode 208. 实现 Trie (前缀树): https://leetcode-cn.com/problems/implement-trie-prefix-tree/

image.png


作者简介:godweiyang知乎同名华东师范大学计算机系硕士在读,方向自然语言处理与深度学习喜欢与人分享技术与知识,期待与你的进一步交流~

相关文章
|
机器学习/深度学习 人工智能 自然语言处理
【每日算法Day 100】字节跳动 AI Lab 面试编程题(三道)
我当时太紧张了,真是脑抽了,还想着弄个优先队列,划分最大的,然后丢进去,再划分最大的,但是是错的。 正确解法小姐姐走了我才想起来,二分答案 m ,然后扫描一遍判断将每一段划分成小于等于 m 的一共需要多少次。如果次数大于 k ,说明 m 太短了,否则说明 m 太长了。
378 0
【每日算法Day 100】字节跳动 AI Lab 面试编程题(三道)
|
算法 C++ Python
【每日算法Day 107】面试必考:良心推荐,一题三解,不看后悔一辈子
【每日算法Day 107】面试必考:良心推荐,一题三解,不看后悔一辈子
125 0
|
人工智能 算法
【每日算法Day 102】美团 AI 平台算法工程师面试编程题
【每日算法Day 102】美团 AI 平台算法工程师面试编程题
153 0
|
人工智能 算法
【每日算法Day 101】字节跳动 AI Lab 精选面试编程题
【每日算法Day 101】字节跳动 AI Lab 精选面试编程题
223 0
|
人工智能 算法
【每日算法Day 100】字节跳动 AI Lab 面试编程题(三道)
【每日算法Day 100】字节跳动 AI Lab 面试编程题(三道)
292 0
|
算法 C++ Python
【每日算法Day 86】面试经典题:把数字翻译成字符串
【每日算法Day 86】面试经典题:把数字翻译成字符串
|
存储 算法 C++
【每日算法Day 84】面试必考题:Trie(字典树/前缀树)的实现
【每日算法Day 84】面试必考题:Trie(字典树/前缀树)的实现
|
算法 C++ Python
【每日算法Day 82】面试经典题:求第K大数,我写了11种实现,不来看看吗?
【每日算法Day 82】面试经典题:求第K大数,我写了11种实现,不来看看吗?
107 0
|
存储 算法 C++
【每日算法Day 81】面试经典题:关于丑数,你真的理解为什么这么算吗?
【每日算法Day 81】面试经典题:关于丑数,你真的理解为什么这么算吗?
|
算法 C++
【每日算法Day 78】面试经典题:能说出全部四种方法,不录用你都不可能!
【每日算法Day 78】面试经典题:能说出全部四种方法,不录用你都不可能!
下一篇
无影云桌面