實現(xiàn)Trie Tree(實現(xiàn)前綴樹)(LeetCode.208)

題目

實現(xiàn)Trie Tree(前綴樹)包含 insert, search, 和 startsWith 這三個操作。


LeetCode.208

解析與編碼實現(xiàn)

  1. 什么是Trie Tree?

前綴樹皆怕,也稱為字典樹或查找樹,是一種樹形結(jié)構(gòu)翩剪。典型應(yīng)用是用于統(tǒng)計寿羞,排序和保存大量的字符串毯焕,利用字符串的公共前綴來減少查詢時間嘹狞,減少存儲空間岂膳,并且最大限度地減少無謂的字符串比較。


Trie Tree如圖所示——圖片來源于網(wǎng)絡(luò)
  1. Trie Tree Node的結(jié)構(gòu)定義

題目中要求磅网,輸入都是由小寫字母a-z構(gòu)成的谈截,一個Node初始化26個孩子節(jié)點(diǎn)引用(26個小寫英文字母),26個孩子節(jié)點(diǎn)使用數(shù)組結(jié)構(gòu)實現(xiàn)涧偷,a-z固定在對應(yīng)的數(shù)組元素上簸喂,雖然這樣會有存儲上的損耗,某些字母并沒有存在燎潮,但是通過a-z可以直接定位到數(shù)組的元素查詢時間復(fù)雜度O(1)喻鳄。

  • insert操作可以沿著根節(jié)點(diǎn)root(不存儲任何值)依次向下遍歷和插入。
  • startsWith操作前綴匹配确封,從root出發(fā)向下遍歷除呵,如果對應(yīng)的字符節(jié)點(diǎn)均存在,則返回true爪喘,表明找到了颜曾。只要一個字符對應(yīng)的節(jié)點(diǎn)不存在就沒有找到。
  • search操作從root出發(fā)秉剑,需要全部匹配的同時泛豪,還要確定最后一個匹配的字符節(jié)點(diǎn)是否是存儲的字符串中的某個字符串最后的一個字符,也就是某個字符串的結(jié)束位置侦鹏。例如诡曙,樹中存儲了abcd,那么search(abc)時略水,匹配的最后一個字符節(jié)點(diǎn)是c价卤,而c不是存儲的字符串中的某個字符串最后的一個字符,所以不匹配聚请。這樣在Node結(jié)構(gòu)中我們需要增加一個標(biāo)志符用于標(biāo)記當(dāng)前節(jié)點(diǎn)是否是存儲的某個字符串的最后一個字符荠雕。

Trie Tree Node的結(jié)構(gòu)代碼如下:

class TrieNode {
    private final int R = 26;
    //代表的字符a-z
    private String val;
    //初始化26個子節(jié)點(diǎn)
    private TrieNode[] children;
    //標(biāo)記當(dāng)前節(jié)點(diǎn)是否是存儲的某個字符串的最后一個字符
    private boolean isEnd;

    public TrieNode(String val){
        this.val = val;
        this.children = new TrieNode[R];
        this.isEnd = false;
    }

    public String getVal(){
        return val;
    }

    public TrieNode[] getChildren(){
        return children;
    }

    public void setIsEnd(boolean isEnd){
        this.isEnd = isEnd;
    }

    public boolean getIsEnd(){
        return this.isEnd;
    }
}
  1. insert操作

沿著根節(jié)點(diǎn)root(不存儲任何值)依次向下遍歷,如果字符存在繼續(xù)向下驶赏,若不存在插入這個字符炸卑。當(dāng)?shù)竭_(dá)字符串的最后一個字符時,將節(jié)點(diǎn)的isEnd標(biāo)記為true煤傍,表示到這個字符為止存儲了一個字符串盖文。

代碼如下:

public void insert(String word) {
    TrieNode parent = root;
    //向下遍歷
    for(int i = 0; i < word.length(); i++){
        //a-z對應(yīng)的節(jié)點(diǎn)位置
        int index = (int)(word.charAt(i) - 'a');
        TrieNode child = parent.getChildren()[index];
        //若不存在當(dāng)前字符的節(jié)點(diǎn),插入
        if (null == child){
            child = new TrieNode(word.substring(i, i + 1));
            parent.getChildren()[index] = child;
        }
        //繼續(xù)向下
        parent = child;
    }
    //最后一個字符的節(jié)點(diǎn)isEnd標(biāo)記為true
    parent.setIsEnd(true);
}
  1. search操作

從root出發(fā)蚯姆,向下遍歷尋找五续,如果字符節(jié)點(diǎn)不存在洒敏,則返回false。當(dāng)字符串都遍歷完成疙驾,并且對應(yīng)節(jié)點(diǎn)都存在時凶伙,需要判斷最后一個字符節(jié)點(diǎn)的isEnd是否為true,若為true則證明搜尋到對應(yīng)的字符串它碎。

代碼如下:

public boolean search(String word) {
    TrieNode parent = root;
    //從root出發(fā)函荣,向下遍歷尋找
    for(int i = 0; i < word.length(); i++){
        int index = (int)(word.charAt(i) - 'a');
        TrieNode child = parent.getChildren()[index];
        //如果字符節(jié)點(diǎn)不存在,則返回false
        if (null == child){
            return false;
        }
        parent = child;
    }
    //判斷最后一個字符節(jié)點(diǎn)的isEnd如果是false扳肛,表明不是字符串最后一個字符
    if (!parent.getIsEnd()){
        return false;
    }
    return true;
}
  1. startsWith操作

root出發(fā)向下遍歷傻挂,如果對應(yīng)的字符節(jié)點(diǎn)均存在,則返回true挖息。只要一個字符對應(yīng)的節(jié)點(diǎn)不存在就沒有找到金拒。

代碼如下:

public boolean startsWith(String prefix) {
    TrieNode parent = root;
    //從root出發(fā),向下遍歷尋找
    for(int i = 0; i < prefix.length(); i++){
        int index = (int)(prefix.charAt(i) - 'a');
        TrieNode child = parent.getChildren()[index];
        //只要一個字符對應(yīng)的節(jié)點(diǎn)不存在就沒有找到
        if (null == child){
            return false;
        }
        parent = child;
    }
    return true;
}

完整代碼如下

class Trie {
    private TrieNode root;
    /** Initialize your data structure here. */
    public Trie() {
        root = new TrieNode(null);
    }
    
    public void insert(String word) {
        TrieNode parent = root;
        //向下遍歷
        for(int i = 0; i < word.length(); i++){
            //a-z對應(yīng)的節(jié)點(diǎn)位置
            int index = (int)(word.charAt(i) - 'a');
            TrieNode child = parent.getChildren()[index];
            //若不存在當(dāng)前字符的節(jié)點(diǎn)套腹,插入
            if (null == child){
                child = new TrieNode(word.substring(i, i + 1));
                parent.getChildren()[index] = child;
            }
            //繼續(xù)向下
            parent = child;
        }
        //最后一個字符的節(jié)點(diǎn)isEnd標(biāo)記為true
        parent.setIsEnd(true);
    }
    
    public boolean search(String word) {
        TrieNode parent = root;
        //從root出發(fā)绪抛,向下遍歷尋找
        for(int i = 0; i < word.length(); i++){
            int index = (int)(word.charAt(i) - 'a');
            TrieNode child = parent.getChildren()[index];
            //如果字符節(jié)點(diǎn)不存在,則返回false
            if (null == child){
                return false;
            }
            parent = child;
        }
        //判斷最后一個字符節(jié)點(diǎn)的isEnd如果是false沉迹,表明不是字符串最后一個字符
        if (!parent.getIsEnd()){
            return false;
        }
        return true;
    }
    
    public boolean startsWith(String prefix) {
       TrieNode parent = root;
        //從root出發(fā)睦疫,向下遍歷尋找
        for(int i = 0; i < prefix.length(); i++){
            int index = (int)(prefix.charAt(i) - 'a');
            TrieNode child = parent.getChildren()[index];
            //只要一個字符對應(yīng)的節(jié)點(diǎn)不存在就沒有找到
            if (null == child){
                return false;
            }
            parent = child;
        }
        return true;
    }
}
class TrieNode {
    private final int R = 26;
    //代表的字符a-z
    private String val;
    //初始化26個子節(jié)點(diǎn)
    private TrieNode[] children;
    //標(biāo)記當(dāng)前節(jié)點(diǎn)是否是存儲的某個字符串的最后一個字符
    private boolean isEnd;

    public TrieNode(String val){
        this.val = val;
        this.children = new TrieNode[R];
        this.isEnd = false;
    }

    public String getVal(){
        return val;
    }

    public TrieNode[] getChildren(){
        return children;
    }

    public void setIsEnd(boolean isEnd){
        this.isEnd = isEnd;
    }

    public boolean getIsEnd(){
        return this.isEnd;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鞭呕,隨后出現(xiàn)的幾起案子蛤育,更是在濱河造成了極大的恐慌,老刑警劉巖葫松,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓦糕,死亡現(xiàn)場離奇詭異,居然都是意外死亡腋么,警方通過查閱死者的電腦和手機(jī)咕娄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珊擂,“玉大人圣勒,你說我怎么就攤上這事〈萆龋” “怎么了圣贸?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扛稽。 經(jīng)常有香客問我吁峻,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任用含,我火速辦了婚禮矮慕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘啄骇。我一直安慰自己痴鳄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布肠缔。 她就那樣靜靜地躺著夏跷,像睡著了一般哼转。 火紅的嫁衣襯著肌膚如雪明未。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天壹蔓,我揣著相機(jī)與錄音趟妥,去河邊找鬼。 笑死佣蓉,一個胖子當(dāng)著我的面吹牛披摄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勇凭,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼疚膊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虾标?” 一聲冷哼從身側(cè)響起寓盗,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎璧函,沒想到半個月后傀蚌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蘸吓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年善炫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片库继。...
    茶點(diǎn)故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡箩艺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宪萄,到底是詐尸還是另有隱情艺谆,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布雨膨,位于F島的核電站擂涛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撒妈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一恢暖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狰右,春花似錦杰捂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谷暮,卻和暖如春蒿往,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背湿弦。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工瓤漏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颊埃。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓蔬充,卻偏偏與公主長得像,于是被迫代替她去往敵國和親班利。 傳聞我的和親對象是個殘疾皇子饥漫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容