摸鱼:
难度 :中等
请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。
实现词典类 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"); // return False
wordDictionary.search("bad"); // return True
wordDictionary.search(".ad"); // return True
wordDictionary.search("b.."); // return True
提示:1 <= word.length <= 500
addWord 中的 word 由小写英文字母组成
search 中的 word 由 '.' 或小写英文字母组成
最多调用 50000 次 addWord 和 search
刚开始想用数组:(超时的解法)
List<String> lists;
public WordDictionary() {
this.lists = new ArrayList<>();
}
public void addWord(String word) {
lists.add(word);
}
public boolean search(String word) {
int i;
for (i = 0; i < lists.size(); i++) {
String str = lists.get(i);
if (str.length() != word.length()){
continue;
}
int j;
for ( j= 0; j < word.length(); j++) {
if (word.charAt(j)=='.'){
continue;
}else {
if (word.charAt(j)!=str.charAt(j)){
break;
}
}
}
if (j == word.length()){
break;
}
}
if (i == lists.size()){
return false;
}else {
return true;
}
}
正解是使用前缀树:
什么是前缀树(字典树):
就像字典一样,这样方便查找,降低查询时的时间复杂度(堆排序的时间复杂度为O(n*log n))前缀树的时间复杂度会更低,因为节点的孩子一般比二要大,其实前缀树可以看成一个多子树的(二叉树)。
likou211:前缀树解法代码:
public class WordDictionary {
Tree tree;
public WordDictionary() {
this.tree = new Tree();
}
public void addWord(String word) {
tree.insert(word);
}
public boolean search(String word) {
return BFS(word,0,tree);
}
private boolean BFS(String word, int i, Tree tree) {
if (word.length() == i){
return tree.inEnd();
}
char ch = word.charAt(i);
int index = ch - 'a';
if (Character.isLetter(ch)){
if (tree.getChildren()[index] !=null && BFS(word,i+1,tree.getChildren()[index])){
return true;
}
}else {
for (int j = 0; j < 26; j++) {
if (tree.getChildren()[j] !=null && BFS(word,j+1,tree.getChildren()[index])){
return true;
}
}
}
return false;
}
class Tree{
boolean isEnd = false;
Tree[] trees = new Tree[26];
public void insert(String word){
Tree tree = this;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
if (tree.trees[index] == null){
tree.trees[index] = new Tree();
}
tree = tree.trees[index];
}
tree.isEnd = true;
}
public boolean inEnd(){
return isEnd;
}
public Tree[] getChildren() {
return trees;
}
}
public static void main(String[] args) {
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("abc");
System.out.println(wordDictionary.search("abz"));
}
}
第一步创建Tree类,类里面有两个属性一个是
boolean isEnd = false; 标记是否是某一个单词的结尾,是某个单词的结尾为true
Tree[] trees = new Tree[26]; 这个可以装下二十六个字母,通过
if (tree.trees[index] == null){ tree.trees[index] = new Tree(); //insert时,为null插入这个字符(这个是通过指针) //通过覆盖数组上某个位置的地址,就和c语言指针一样 }
likou211;
代码大概意思是
1. 创建前缀树,前缀树事先定义好的
Tree tree; public WordDictionary() { this.tree = new Tree(); }
2. 添加单词 ,直接向前缀树的数据结构中添加
public void addWord(String word) { tree.insert(word); }
3. 查询单词是否在数据结构体中
public boolean search(String word) { return BFS(word,0,tree); }
BFS:深度遍历
当单词长度等于遍历的深度时,看是否有这个单词插入过,isEnd是否为true
if (Character.isLetter(ch)){ if (tree.getChildren()[index] !=null && BFS(word,i+1,tree.getChildren()[index])){ return true; } }
如果字符为字母,判断是否有对应的孩子,没有返回false,有孩子则单词向后移动一位,继续查找知道移动到单词最后一位,判断isEnd是否为true
else { for (int j = 0; j < 26; j++) { if (tree.getChildren()[j] !=null && BFS(word,j+1,tree.getChildren()[index])){ return true; } } }
如果为字符为'.'时,这时需要遍历a-z所有的单词,然后和前面的一样
我们就假设查找String a = "a" , String b = "a.p" ,String c = "ass";
递归图如下,递归就行胶带一样(浇地的管子一样)它会一层一层往里面递近(递归方程),并且一定存在一个终止条件,最后从内侧(有可能不到最内侧就退出了)往外出来,其实就是内层返回的是false的话,还会一直遍历,(因为if条件全部成立)如果返回为true,程序会结束for循环,然后返回到上一层,这里if条件也是成立的,所以一直成立,直到最外层return true
递归(回溯算法)
1.初始数据
2.递归方程
3.终止条件