跳至主要內容

208. 实现 Trie (前缀树)


208. 实现 Trie (前缀树)open in new window

🟠   🔖  设计 字典树 哈希表 字符串  🔗 LeetCodeopen in new window

题目

A trieopen in new window (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker.

Implement the Trie class:

  • Trie() Initializes the trie object.
  • void insert(String word) Inserts the string word into the trie.
  • boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise.
  • boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.

Example 1:

Input

["Trie", "insert", "search", "search", "startsWith", "insert", "search"]

[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]

Output

[null, null, true, false, true, null, true]

Explanation

Trie trie = new Trie();

trie.insert("apple");

trie.search("apple"); // return True

trie.search("app");> // return False

trie.startsWith("app"); // return True

trie.insert("app");

trie.search("app");> // return True

Constraints:

  • 1 <= word.length, prefix.length <= 2000
  • word and prefix consist only of lowercase English letters.
  • At most 3 * 10^4 calls in total will be made to insert, search, and startsWith.

题目大意

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 是一种树形数据结构,专门用于存储字符串集。每个节点表示字符串的一个字符,子节点对应该字符后续的字符序列。Trie 的最大优点是可以在 O(m) 时间内完成查找或插入操作,其中 m 是要查找或插入的字符串的长度。

Trie 的结构:

  • 每个节点可以有多个子节点,每个子节点代表一个字母。
  • 一个标志位 isEnd 用于表示是否存在一个以当前节点为结尾的完整单词。
  1. 插入单词 (insert)
    • 从根节点开始,对于单词中的每个字符,依次创建子节点。如果字符的子节点不存在,则创建一个新的子节点。
    • 最后,标记该节点为单词的结尾(即 isEnd = true)。
  2. 查找单词 (search)
    • 从根节点开始,依次沿着给定单词的字符查找对应的子节点。
    • 如果找到所有字符且最后一个节点标记为单词的结尾,返回 true;否则,返回 false
  3. 查找前缀 (startsWith)
    • search 类似,只是不要求节点是单词的结尾,只要能找到所有前缀字符对应的节点,就返回 true

复杂度分析

  • 时间复杂度
    • 插入O(m)m 是要插入的单词长度。
    • 查找O(m)m 是要查找的单词长度。
    • 前缀查找O(m)m 是前缀的长度。

代码

var Trie = function () {
	this.root = {}; // 初始化根节点
};

// 插入一个单词到 Trie
/**
 * @param {string} word
 * @return {void}
 */
Trie.prototype.insert = function (word) {
	let node = this.root;
	for (let char of word) {
		// 如果子节点不存在,则创建新的子节点
		if (!node[char]) {
			node[char] = {};
		}
		// 继续遍历下一个字符
		node = node[char];
	}
	// 单词结束标记
	node.isEnd = true;
};

// 搜索单词,要求完全匹配
/**
 * @param {string} word
 * @return {boolean}
 */
Trie.prototype.search = function (word) {
	let node = this.root;
	for (let char of word) {
		// 如果找不到字符,返回 false
		if (!node[char]) return false;
		node = node[char];
	}
	// 只有到达的节点是单词结尾时,才返回 true
	return node.isEnd == true;
};

// 检查是否存在以某个前缀开头的单词
/**
 * @param {string} prefix
 * @return {boolean}
 */
Trie.prototype.startsWith = function (prefix) {
	let node = this.root;
	for (let char of prefix) {
		// 如果前缀字符不存在,返回 false
		if (!node[char]) return false;
		node = node[char];
	}
	// 只要能匹配到前缀,返回 true
	return true;
};

/**
 * 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)
 */

相关题目

题号标题题解标签难度
211添加与搜索单词 - 数据结构设计open in new window[✓]open in new window深度优先搜索 设计 字典树 1+
642设计搜索自动补全系统open in new window深度优先搜索 设计 字典树 4+
648单词替换open in new window字典树 数组 哈希表 1+
676实现一个魔法字典open in new window深度优先搜索 设计 字典树 2+
1804实现 Trie (前缀树) IIopen in new window设计 字典树 哈希表 1+
2227加密解密字符串open in new window设计 字典树 数组 2+
3042统计前后缀下标对 Iopen in new window字典树 数组 字符串 3+
3045统计前后缀下标对 IIopen in new window字典树 数组 字符串 3+