LeetCode-题目整理【9】:Trie树

最长公共前缀可以使用字典树来解答,在解答中,需要查找单词,如果有精确需要查找的单词word,那么可以使用代码:

func (this *Trie) Search(word string) bool {
   
	for _, v := range word {
   
		if this.next[v-'a'] == nil {
   
			return false
		}
		this = this.next[v-'a']
	}
	if this.isEnd == false {
   
		return false
	}
	return true
}

但是如果查找的单词不明确,那么可以使用代码(以公共前缀为例):

func (node *TrieNode) search() string {
   
	prefix := ""
	for node.countChildren() == 1 && !node.isEnd {
   
	
	//就是这个位置,可以遍历字典树中的字母,如果当前字母不为空,可以继续往下遍历
		for i := 0; i < 26; i++ {
   
			if node.next[i] != nil {
   
				prefix = prefix + string('a'+ i)
				node = node.next[i]
				break
			}
		}
	}
	
	return prefix
}

    // 计算当前节点子节点的数量
func (node *TrieNode) countChildren() int {
   
	......
}
  1. 最长公共前缀
    简单
    编写一个函数来查找字符串数组中的最长公共前缀。
    如果不存在公共前缀,返回空字符串 “”。
    示例 1:
    输入:strs = [“flower”,“flow”,“flight”]
    输出:“fl”
    示例 2:
    输入:strs = [“dog”,“racecar”,“car”]
    输出:“”
    解释:输入不存在公共前缀。
//字符串
// 函数首先找到长度最短的字符串,然后逐个比较字符,如果遇到不相同的字符,则返回当前索引之前的字符串作为结果。
//这是一个更简洁的实现,它的时间复杂度是O(n*m),其中n是字符串数组的长度,m是字符串的平均长度。

func longestCommonPrefix(strs []string) string {
   
	if len(strs) == 0 {
   
		return ""
	}

	minstr := strs[0]

	for _, str := range strs {
   
		if len(str) < len(minstr) {
   
			minstr = str
		}
	}

	for i := 0; i < len(minstr); i++ {
   
		curStr := strs[0][i]
		for _, str := range strs {
   
			if str[i] != curStr {
   
				return strs[0][:i]
			}
		}
	}

	return strs[0][:len(minstr)]
}
//使用字典树
type TrieNode struct {
   
	next  [26]*TrieNode
	isEnd bool
}

func longestCommonPrefix(strs []string) string {
   
	root := &TrieNode{
   }

	//构建字典树
	for _, str := range strs {
   
		root.Insert(str)
	}

	//查找最长公共前缀
	return root.search()
}

// 插入字符
func (node *TrieNode) Insert(word string) {
   
	for _, v := range word {
   
		if node.next[v-'a'] == nil {
   
			node.next[v-'a'] = &TrieNode{
   }
		}
		node = node.next[v-'a']
	}
	node.isEnd = true
}

    //查找字符和公共前缀
func (node *TrieNode) search() string {
   
	prefix := ""

	//当前节点的子节点数量为1(如果大于1,表示节点之后会有分叉,就不是公共节点)
	//且当前节点不是一个单词的结束节点,就继续循环。
	for node.countChildren() == 1 && !node.isEnd {
   
		for i := 0; i < 26; i++ {
   
			if node.next[i] != nil {
   
				prefix = prefix + string('a'+ i)
				node = node.next[i]
				break
			}
		}
	}

	return prefix
}

    // 计算当前节点子节点的数量
func (node *TrieNode) countChildren() int {
   
	count := 0
	for i := 0; i < 26; i++ {
   
		if node.next[i] != nil {
   
			count++
		}
	}
	return count
}

  1. 实现 Trie (前缀树)
    中等
    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
type Trie struct {
   
	next  [26]*Trie
	isEnd bool
}

func Constructor() Trie {
   
	return Trie{
   }
}

func (this *Trie) Insert(word string) {
   
	for _, v := range word {
   
		if this.next[v-'a'] == nil {
   
			this.next[v-'a'] = new(Trie)
		}
		this = this.next[v-'a']
	}
	this.isEnd = true
}

func (this *Trie) Search(word string) bool {
   
	for _, v := range word {
   
		if this.next[v-'a'] == nil {
   
			return false
		}
		this = this.next[v-'a']
	}
	if this.isEnd == false {
   
		return false
	}
	return true
}

//前提是已经知道 prefix 这个单词,因此和前面“查找公共前缀”的解答不一样,遍历过程不一样,(和开头中提到的精确到具体单词类似)
func (this *Trie) StartsWith(prefix string) bool {
   
	for _, v := range prefix {
   
		if this.next[v-'a'] == nil {
   
			return false
		}
		this = this.next[v-'a']
	}
	return true
}

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

下面这道题解答过程和 “实现 Trie (前缀树)” 的insert和search基本一样,主要区别在于查找单词时,多了条件:word 中可能包含一些 ‘.’ ,每个 . 都可以表示任何一个字母。
多添加的代码是判断"."的情况:

if char == '.' {
   
			for _, v := range this.next {
   
				if v != nil && v.search(word, i+1) {
   
					return true
				}
			}
			return false
		}
  1. 添加与搜索单词 - 数据结构设计
    中等
    请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。
    实现词典类 WordDictionary :
    WordDictionary() 初始化词典对象
    void addWord(word) 将 word 添加到数据结构中,之后可以对它进行匹配
    bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回 false 。word 中可能包含一些 ‘.’ ,每个 . 都可以表示任何一个字母。
    示例:
    输入:
    [“WordDictionary”,“addWord”,“addWord”,“addWord”,“search”,“search”,“search”,“search”]
    [[],[“bad”],[“dad”],[“mad”],[“pad”],[“bad”],[“.ad”],[“b…”]]
    输出:
    [null,null,null,null,false,true,true,true]
    解释:
    WordDictionary wordDictionary = new WordDictionary();
    wordDictionary.addWord(“bad”);
    wordDictionary.addWord(“dad”);
    wordDictionary.addWord(“mad”);
    wordDictionary.search(“pad”); // 返回 False
    wordDictionary.search(“bad”); // 返回 True
    wordDictionary.search(“.ad”); // 返回 True
    wordDictionary.search(“b…”); // 返回 True
type WordDictionary struct {
   
	next  [26]*WordDictionary
	isEnd bool
}

func Constructor() WordDictionary {
   
	return WordDictionary{
   }
}

func (this *WordDictionary) AddWord(word string) {
   
	for _, v := range word {
   
		if this.next[v-'a'] == nil {
   
			this.next[v-'a'] = new(WordDictionary)
		}
		this = this.next[v-'a']
	}
	this.isEnd = true
}

func (this *WordDictionary) Search(word string) bool {
   
	return this.search(word, 0)
}

func (this *WordDictionary) search(word string, index int) bool {
   
	for i := index; i < len(word); i++ {
   
		char := word[i]
		//多了判断"."的情况,其他基本一致
		if char == '.' {
   
			for _, v := range this.next {
   
				if v != nil && v.search(word, i+1) {
   
					return true
				}
			}
			return false
		}
		if this.next[char-'a'] == nil {
   
			return false
		}
		this = this.next[char-'a']
	}
	if this.isEnd == false {
   
		return false
	}
	return true
}

/**
 * Your WordDictionary object will be instantiated and called as such:
 * obj := Constructor();
 * obj.AddWord(word);
 * param_2 := obj.Search(word);
 */

相关推荐

  1. LeetCode-题目整理9】:Trie

    2024-01-26 10:22:02       52 阅读
  2. 【字典TrieLeetCode-208. 实现 Trie (前缀)

    2024-01-26 10:22:02       59 阅读
  3. leetcode 208. 实现 Trie (前缀)

    2024-01-26 10:22:02       55 阅读
  4. LeetCode 算法:实现 Trie (前缀) c++

    2024-01-26 10:22:02       26 阅读
  5. LeetCode-题目整理【4】:跳跃游戏

    2024-01-26 10:22:02       50 阅读
  6. LeetCode-题目整理【10】:单词搜索

    2024-01-26 10:22:02       55 阅读
  7. LeetCode-题目整理【11】:回溯算法

    2024-01-26 10:22:02       37 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-26 10:22:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-26 10:22:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-01-26 10:22:02       87 阅读
  4. Python语言-面向对象

    2024-01-26 10:22:02       96 阅读

热门阅读

  1. 五大自然语言处理技术里程碑浅析

    2024-01-26 10:22:02       50 阅读
  2. 记录CentOS8安装docker全过程

    2024-01-26 10:22:02       57 阅读
  3. centos系统安装指定版本的gcc

    2024-01-26 10:22:02       53 阅读
  4. 《动手学深度学习(PyTorch版)》笔记3.5

    2024-01-26 10:22:02       51 阅读
  5. 2024 axios封装 包括请求拦截、错误码等

    2024-01-26 10:22:02       46 阅读
  6. [深度学习]PaddleClas:统计模型Flops

    2024-01-26 10:22:02       49 阅读
  7. LeetCode 刷题总结 【未完待续】

    2024-01-26 10:22:02       64 阅读