Trie ——前缀树、字典树

☆樱花仙子☆ 提交于 2020-03-05 07:13:57

说在前面:

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)
     */
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!