comments | difficulty | edit_url | tags | ||||
---|---|---|---|---|---|---|---|
true |
中等 |
|
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串word
。boolean search(String word)
如果字符串word
在前缀树中,返回true
(即,在检索之前已经插入);否则,返回false
。boolean startsWith(String prefix)
如果之前已经插入的字符串word
的前缀之一为prefix
,返回true
;否则,返回false
。
示例:
输入 ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] 输出 [null, null, true, false, true, null, true] 解释 Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // 返回 True trie.search("app"); // 返回 False trie.startsWith("app"); // 返回 True trie.insert("app"); trie.search("app"); // 返回 True
提示:
1 <= word.length, prefix.length <= 2000
word
和prefix
仅由小写英文字母组成insert
、search
和startsWith
调用次数 总计 不超过3 * 104
次
前缀树每个节点包括两部分:
- 指向子节点的指针数组
$children$ ,对于本题而言,数组长度为$26$ ,即小写英文字母的数量。$children[0]$ 对应小写字母$a$ ,...,$children[25]$ 对应小写字母$z$ 。 - 布尔字段
$isEnd$ ,表示该节点是否为字符串的结尾。
我们从字典树的根开始,插入字符串。对于当前字符对应的子节点,有两种情况:
- 子节点存在。沿着指针移动到子节点,继续处理下一个字符。
- 子节点不存在。创建一个新的子节点,记录在
$children$ 数组的对应位置上,然后沿着指针移动到子节点,继续搜索下一个字符。
重复以上步骤,直到处理字符串的最后一个字符,然后将当前节点标记为字符串的结尾。
我们从字典树的根开始,查找前缀。对于当前字符对应的子节点,有两种情况:
- 子节点存在。沿着指针移动到子节点,继续搜索下一个字符。
- 子节点不存在。说明字典树中不包含该前缀,返回空指针。
重复以上步骤,直到返回空指针或搜索完前缀的最后一个字符。
若搜索到了前缀的末尾,就说明字典树中存在该前缀。此外,若前缀末尾对应节点的
class Trie:
def __init__(self):
self.children = [None] * 26
self.is_end = False
def insert(self, word: str) -> None:
node = self
for c in word:
idx = ord(c) - ord('a')
if node.children[idx] is None:
node.children[idx] = Trie()
node = node.children[idx]
node.is_end = True
def search(self, word: str) -> bool:
node = self._search_prefix(word)
return node is not None and node.is_end
def startsWith(self, prefix: str) -> bool:
node = self._search_prefix(prefix)
return node is not None
def _search_prefix(self, prefix: str):
node = self
for c in prefix:
idx = ord(c) - ord('a')
if node.children[idx] is None:
return None
node = node.children[idx]
return node
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
class Trie {
private Trie[] children;
private boolean isEnd;
public Trie() {
children = new Trie[26];
}
public void insert(String word) {
Trie node = this;
for (char c : word.toCharArray()) {
int idx = c - 'a';
if (node.children[idx] == null) {
node.children[idx] = new Trie();
}
node = node.children[idx];
}
node.isEnd = true;
}
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
public boolean startsWith(String prefix) {
Trie node = searchPrefix(prefix);
return node != null;
}
private Trie searchPrefix(String s) {
Trie node = this;
for (char c : s.toCharArray()) {
int idx = c - 'a';
if (node.children[idx] == null) {
return null;
}
node = node.children[idx];
}
return node;
}
}
/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/
class Trie {
private:
vector<Trie*> children;
bool isEnd;
Trie* searchPrefix(string s) {
Trie* node = this;
for (char c : s) {
int idx = c - 'a';
if (!node->children[idx]) return nullptr;
node = node->children[idx];
}
return node;
}
public:
Trie()
: children(26)
, isEnd(false) {}
void insert(string word) {
Trie* node = this;
for (char c : word) {
int idx = c - 'a';
if (!node->children[idx]) node->children[idx] = new Trie();
node = node->children[idx];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = searchPrefix(word);
return node != nullptr && node->isEnd;
}
bool startsWith(string prefix) {
Trie* node = searchPrefix(prefix);
return node != nullptr;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
type Trie struct {
children [26]*Trie
isEnd bool
}
func Constructor() Trie {
return Trie{}
}
func (this *Trie) Insert(word string) {
node := this
for _, c := range word {
idx := c - 'a'
if node.children[idx] == nil {
node.children[idx] = &Trie{}
}
node = node.children[idx]
}
node.isEnd = true
}
func (this *Trie) Search(word string) bool {
node := this.SearchPrefix(word)
return node != nil && node.isEnd
}
func (this *Trie) StartsWith(prefix string) bool {
node := this.SearchPrefix(prefix)
return node != nil
}
func (this *Trie) SearchPrefix(s string) *Trie {
node := this
for _, c := range s {
idx := c - 'a'
if node.children[idx] == nil {
return nil
}
node = node.children[idx]
}
return node
}
/**
* Your Trie object will be instantiated and called as such:
* obj := Constructor();
* obj.Insert(word);
* param_2 := obj.Search(word);
* param_3 := obj.StartsWith(prefix);
*/
class TrieNode {
children;
isEnd;
constructor() {
this.children = new Array(26);
this.isEnd = false;
}
}
class Trie {
root;
constructor() {
this.root = new TrieNode();
}
insert(word: string): void {
let head = this.root;
for (let char of word) {
let index = char.charCodeAt(0) - 97;
if (!head.children[index]) {
head.children[index] = new TrieNode();
}
head = head.children[index];
}
head.isEnd = true;
}
search(word: string): boolean {
let head = this.searchPrefix(word);
return head != null && head.isEnd;
}
startsWith(prefix: string): boolean {
return this.searchPrefix(prefix) != null;
}
private searchPrefix(prefix: string) {
let head = this.root;
for (let char of prefix) {
let index = char.charCodeAt(0) - 97;
if (!head.children[index]) return null;
head = head.children[index];
}
return head;
}
}
use std::{cell::RefCell, collections::HashMap, rc::Rc};
struct TrieNode {
pub val: Option<char>,
pub flag: bool,
pub child: HashMap<char, Rc<RefCell<TrieNode>>>,
}
impl TrieNode {
fn new() -> Self {
Self {
val: None,
flag: false,
child: HashMap::new(),
}
}
fn new_with_val(val: char) -> Self {
Self {
val: Some(val),
flag: false,
child: HashMap::new(),
}
}
}
struct Trie {
root: Rc<RefCell<TrieNode>>,
}
/// Your Trie object will be instantiated and called as such:
/// let obj = Trie::new();
/// obj.insert(word);
/// let ret_2: bool = obj.search(word);
/// let ret_3: bool = obj.starts_with(prefix);
impl Trie {
fn new() -> Self {
Self {
root: Rc::new(RefCell::new(TrieNode::new())),
}
}
fn insert(&self, word: String) {
let char_vec: Vec<char> = word.chars().collect();
// Get the clone of current root node
let mut root = Rc::clone(&self.root);
for c in &char_vec {
if !root.borrow().child.contains_key(c) {
// We need to manually create the entry
root.borrow_mut()
.child
.insert(*c, Rc::new(RefCell::new(TrieNode::new())));
}
// Get the child node
let root_clone = Rc::clone(root.borrow().child.get(c).unwrap());
root = root_clone;
}
{
root.borrow_mut().flag = true;
}
}
fn search(&self, word: String) -> bool {
let char_vec: Vec<char> = word.chars().collect();
// Get the clone of current root node
let mut root = Rc::clone(&self.root);
for c in &char_vec {
if !root.borrow().child.contains_key(c) {
return false;
}
// Get the child node
let root_clone = Rc::clone(root.borrow().child.get(c).unwrap());
root = root_clone;
}
let flag = root.borrow().flag;
flag
}
fn starts_with(&self, prefix: String) -> bool {
let char_vec: Vec<char> = prefix.chars().collect();
// Get the clone of current root node
let mut root = Rc::clone(&self.root);
for c in &char_vec {
if !root.borrow().child.contains_key(c) {
return false;
}
// Get the child node
let root_clone = Rc::clone(root.borrow().child.get(c).unwrap());
root = root_clone;
}
true
}
}
/**
* Initialize your data structure here.
*/
var Trie = function () {
this.children = {};
};
/**
* Inserts a word into the trie.
* @param {string} word
* @return {void}
*/
Trie.prototype.insert = function (word) {
let node = this.children;
for (let char of word) {
if (!node[char]) {
node[char] = {};
}
node = node[char];
}
node.isEnd = true;
};
/**
* Returns if the word is in the trie.
* @param {string} word
* @return {boolean}
*/
Trie.prototype.search = function (word) {
let node = this.searchPrefix(word);
return node != undefined && node.isEnd != undefined;
};
Trie.prototype.searchPrefix = function (prefix) {
let node = this.children;
for (let char of prefix) {
if (!node[char]) return false;
node = node[char];
}
return node;
};
/**
* Returns if there is any word in the trie that starts with the given prefix.
* @param {string} prefix
* @return {boolean}
*/
Trie.prototype.startsWith = function (prefix) {
return this.searchPrefix(prefix);
};
/**
* Your Trie object will be instantiated and called as such:
* var obj = new Trie()
* obj.insert(word)
* var param_2 = obj.search(word)
* var param_3 = obj.startsWith(prefix)
*/
public class Trie {
bool isEnd;
Trie[] children = new Trie[26];
public Trie() {
}
public void Insert(string word) {
Trie node = this;
foreach (var c in word)
{
var idx = c - 'a';
node.children[idx] ??= new Trie();
node = node.children[idx];
}
node.isEnd = true;
}
public bool Search(string word) {
Trie node = SearchPrefix(word);
return node != null && node.isEnd;
}
public bool StartsWith(string prefix) {
Trie node = SearchPrefix(prefix);
return node != null;
}
private Trie SearchPrefix(string s) {
Trie node = this;
foreach (var c in s)
{
var idx = c - 'a';
if (node.children[idx] == null)
{
return null;
}
node = node.children[idx];
}
return node;
}
}
/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.Insert(word);
* bool param_2 = obj.Search(word);
* bool param_3 = obj.StartsWith(prefix);
*/