说在前面:
Map
映射数据结构和Trie
的异同:
相同:都能够根据key
进行映射得到对应的值。
不同:
①Trie
中查询/添加的算法复杂度为O(len)
,即插入/查询的字符串的长度,而Map
查询/添加的算法复杂度为O(logn)
,n为输入的查询元素个数。
②Trie
是用多叉树实现的,而Map
一般是用二叉树实现的。
Trie
:专门为了处理(存储/查询)字符串而设计的
Trie
是以时间换空间的,提高了搜索效率但是却浪费了空间- 改良数据结构:① 压缩字典树 ② 三分搜索树
Trie
压缩字典树
三分字典树
:牺牲了一定的时间,但是节省了空间
1. 应用场景:
① 子串查询
② 文件压缩(文件实际上是由很长的字符串组成的,故文件压缩是对很长的字符串进行压缩的)
③ 模式匹配(eg:正则表达式的引擎)
④ DNA(DNA本身就是一个超长的字符串)
2. 设计Trie
由于
Trie
是一棵多叉树,则在其每个节点的设计中需要保存多个下一个结点的位置,故需要通过Map
,即映射的数据结构进行存储下一堆结点的位置,映射数据结构的每一个元素的key
即为下一个结点对应的标识,而value
则是下一个结点的位置
下面实现中Trie
中保存的是一个个单词,故在设计Trie
时需要给每个节点添加一个isWord
标识,来判断当前结点以及其祖父能否组成一个单词。
class NodeEle{
isWords:boolean = false;;
next = new Map();
}
class Trie{
root:NodeEle;
size:number;
constructor(){
this.root = new NodeEle();
this.size = 0;
}
}
3. Trie
的相关方法
① add
add(word:string):void{
let nowNode = this.root;
for(let i=0;i<word.length;i++){
let c = word.charAt(i);
let newNode = new NodeEle();
if(!nowNode.next.get(c)){
nowNode.next.set(c,newNode);
}
nowNode = nowNode.next.get(c);
}
if(!nowNode.isWords){
nowNode.isWords = true;
this.size++;
}
}
② find
find(word:string):boolean{
let newNode = this.root;
for(let i=0;i<word.length;i++){
let c = word.charAt(i);
if(!newNode.next.get(c)){
return false;
}
newNode = newNode.next.get(c);
}
return newNode.isWords;//有可能像panda这样的单词,如果查找单词为pan,而n未被标识为单词(即查找到的是panda的前缀),则返回false
}
③ isPrefix
isPrefix(prefix:string):boolean{
let newNode = this.root;
for(let i=0;i<prefix.length;i++){
let c = prefix.charAt(i);
if(!newNode.next.get(c)){
return false;
}
newNode = newNode.next.get(c);
}
return true;
}
④ match
match = function(nodeEle,matchExe,index){
if(matchExe.length-1==index){
return nodeEle.isWords;
}
let c = matchExe.charAt(index);
if(c!="."){
if(nodeEle.next.get(c)!=null){
return this.match(nodeEle.next.get(c),matchExe,index+1);
}
return false;
}
//如果为. 则要遍历当前结点的所有下一个结点
for(let val of nodeEle.next.values()){
if(this.match(val,matchExe,index+1)){
return true;
}
}
return false;
}
3. leetCode
相关题目
① 键值映射
实现一个 MapSum 类里的两个方法,insert 和 sum
- 对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。
- 对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。
示例
//示例
输入: insert("apple", 3), 输出: Null
输入: sum("ap"), 输出: 3
输入: insert("app", 2), 输出: Null
输入: sum("ap"), 输出: 5
实现
/**
* Initialize your data structure here.
*/
class NodeEle {
constructor(val) {
this.value=val?val:0;
this.next = new Map();
}
;
}
var MapSum = function() {
this.root = new NodeEle();
};
/**
* @param {string} key
* @param {number} val
* @return {void}
*/
MapSum.prototype.insert = function(word, val) {
let nowNode = this.root;
for (let i = 0; i < word.length; i++) {
let c = word.charAt(i);
if (!nowNode.next.get(c)) {
nowNode.next.set(c, new NodeEle());
}
nowNode = nowNode.next.get(c);
}
nowNode.value = val;
};
/**
* @param {string} prefix
* @return {number}
*/
MapSum.prototype.sum = function(prefix) {
let newNode = this.root;
for(let i=0;i<prefix.length;i++){
let c = prefix.charAt(i);
if(newNode.next.get(c)==null){
return 0;
}
newNode = newNode.next.get(c);
}
return this.mapSum(newNode.value,newNode);
};
MapSum.prototype.mapSum = function(sum,newNode){
for(let val of newNode.next.values()){
sum += val.value;
this.mapSum(sum,val);
}
return sum;
}
/**
* Your MapSum object will be instantiated and called as such:
* var obj = new MapSum()
* obj.insert(key,val)
* var param_2 = obj.sum(prefix)
*/
var obj = new MapSum()
obj.insert("apple",3)
obj.insert("app",5)
var param_2 = obj.sum("ap")
console.log(param_2)
② 添加与搜索单词 - 数据结构设计
设计一个支持
addWord(word)
和search(word)
操作的数据结构:
search(word)
可以搜索文字或正则表达式字符串,字符串只包含字母.
或a-z
。.
可以表示任何一个字母。
示例
addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true
实现
/**
* Initialize your data structure here.
*/
class NodeEle {
constructor() {
this.isWords = false;
this.next = new Map();
}
;
}
var WordDictionary = function() {
this.root = new NodeEle();
};
/**
* Adds a word into the data structure.
* @param {string} word
* @return {void}
*/
WordDictionary.prototype.addWord = function(word) {
let nowNode = this.root;
for (let i = 0; i < word.length; i++) {
let c = word.charAt(i);
let newNode = new NodeEle();
if (!nowNode.next.get(c)) {
nowNode.next.set(c, newNode);
}
nowNode = nowNode.next.get(c);
}
if (!nowNode.isWords) {
nowNode.isWords = true;
}
console.log(nowNode)
};
/**
* Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter.
* @param {string} word
* @return {boolean}
*/
WordDictionary.prototype.search = function(word) {
return this.match(this.root,word,0);
};
WordDictionary.prototype.match = function(nodeEle,matchExe,index){
console.log(nodeEle)
if(matchExe.length==index){
return nodeEle.isWords;
}
let c = matchExe.charAt(index);
if(c!="."){
if(nodeEle.next.get(c)!=null){
return this.match(nodeEle.next.get(c),matchExe,index+1);
}
return false;
}
for(let val of nodeEle.next.values()){
if(this.match(val,matchExe,index+1)){
return true;
}
}
return false;
}
/**
* Your WordDictionary object will be instantiated and called as such:
* var obj = new WordDictionary()
* obj.addWord(word)
* var param_2 = obj.search(word)
*/
来源:CSDN
作者:Lemon
链接:https://blog.csdn.net/weixin_43314846/article/details/104619492