欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java前綴樹Trie的原理及代碼實(shí)現(xiàn)

 更新時(shí)間:2022年11月18日 08:56:58   作者:劉Java  
Trie又被稱為前綴樹、字典樹。Trie利用字符串的公共前綴來高效地存儲(chǔ)和檢索字符串?dāng)?shù)據(jù)集中的關(guān)鍵詞,最大限度地減少無謂的字符串比較,其核心思想是用空間換時(shí)間。本文主要介紹了Trie的原理及實(shí)現(xiàn),感興趣的可以了解一下

Trie的概念

Trie(發(fā)音類似 “try”)又被稱為前綴樹、字典樹。Trie利用字符串的公共前綴來高效地存儲(chǔ)和檢索字符串?dāng)?shù)據(jù)集中的關(guān)鍵詞,最大限度地減少無謂的字符串比較,其核心思想是用空間換時(shí)間。

Trie樹可被用來實(shí)現(xiàn)字符串查詢、前綴查詢、詞頻統(tǒng)計(jì)、自動(dòng)拼寫、補(bǔ)完檢查等等功能。

Trie樹的三個(gè)性質(zhì):

  • 根節(jié)點(diǎn)不包含字符,除根節(jié)點(diǎn)外每一個(gè)節(jié)點(diǎn)都只包含一個(gè)字符。
  • 從根節(jié)點(diǎn)到某一節(jié)點(diǎn),路徑上經(jīng)過的字符連接起來,為該節(jié)點(diǎn)對(duì)應(yīng)的字符串。
  • 每個(gè)節(jié)點(diǎn)的所有子節(jié)點(diǎn)包含的字符都不相同。

Trie的實(shí)現(xiàn)

Trie是一顆非典型的多叉樹形結(jié)構(gòu),多叉樹是因?yàn)橐粋€(gè)節(jié)點(diǎn)可以有多個(gè)字節(jié)點(diǎn),而非典型在于節(jié)點(diǎn)中沒有專門的字段直接保存此節(jié)點(diǎn)的值,而是通過一個(gè)數(shù)組或者map保存了當(dāng)前節(jié)點(diǎn)的所有下層子節(jié)點(diǎn)的值,也正是因此,根節(jié)點(diǎn)不表示任何字符。

基本結(jié)構(gòu)

最簡(jiǎn)單的前綴樹的結(jié)構(gòu)如下:

  • 內(nèi)部包含一個(gè)哈希表next,存儲(chǔ)著子節(jié)點(diǎn)的值到對(duì)應(yīng)的節(jié)點(diǎn)的映射關(guān)系。
  • 還有一個(gè)布爾值isEnd,用來標(biāo)識(shí)該節(jié)點(diǎn)是否是一個(gè)字符串的結(jié)束。
  • 調(diào)用無參構(gòu)造器將會(huì)初始化這兩個(gè)屬性。

實(shí)際上,還可以包含其他的屬性以實(shí)現(xiàn)特定的功能,例如加入count表示以當(dāng)前單詞結(jié)尾的單詞數(shù)量,加入prefix表示以該處節(jié)點(diǎn)之前的字符串為前綴的單詞數(shù)量。

另外,對(duì)于下層子節(jié)點(diǎn)的存儲(chǔ),如果字符串僅包含小寫字母,或者固定范圍的字符,那么我們可以使用定長(zhǎng)(例如26)的數(shù)組來表示next,這樣省去了hash操作的開銷,但同樣可能造成空間的浪費(fèi)。

class Trie {
    /**
* 經(jīng)過該節(jié)點(diǎn)的字符串的下層節(jié)點(diǎn)
*/
    Map<Character, Trie> next;
    /**
* 該節(jié)點(diǎn)是否是一個(gè)字符串的結(jié)束
*/
    boolean isEnd;

    public Trie() {
        this.next = new HashMap<>();
        this.isEnd = false;
    }

}

構(gòu)建Trie

通過調(diào)用構(gòu)造器初始化一個(gè)Trie的根節(jié)點(diǎn),通過insert操作向前綴樹中插入關(guān)鍵詞字符串(模式串)。

可以看到其實(shí)現(xiàn)的方法比較簡(jiǎn)單:將字符串轉(zhuǎn)換為char數(shù)組,順序遍歷char數(shù)組的每個(gè)字符,然后從根節(jié)點(diǎn)開始判斷該節(jié)點(diǎn)的下層子節(jié)點(diǎn)映射next:

  • 如果不包含此字符,那么加入一個(gè)新子節(jié)點(diǎn)進(jìn)去,值對(duì)應(yīng)著當(dāng)前字符。然后使用該子節(jié)點(diǎn),進(jìn)入下一次循環(huán)判斷下一個(gè)字符。
  • 如果包含此字符或者新插入了節(jié)點(diǎn),那么當(dāng)前字符獲取對(duì)應(yīng)的子節(jié)點(diǎn),進(jìn)入下一次循環(huán)判斷下一個(gè)字符。

循環(huán)完畢,我們完成了當(dāng)前字符串的Trie構(gòu)建,那么還需要將最后一個(gè)節(jié)點(diǎn)的isEnd改為true,表示該節(jié)點(diǎn)是一個(gè)字符串的結(jié)束。

public void insert(String word) {
    //初始默認(rèn)為根節(jié)點(diǎn),根節(jié)點(diǎn)不包含任何字符
    Trie cur = this;
    //遍歷該字符串的字符數(shù)組
    for (char c : word.toCharArray()) {
        //如果該節(jié)點(diǎn)的下層不包含此字符,那么加入一個(gè)新節(jié)點(diǎn)進(jìn)去
        if (!cur.next.containsKey(c)) {
            cur.next.put(c, new Trie());
        }
        //查找下一層節(jié)點(diǎn)
        cur = cur.next.get(c);
    }
    //遍歷字符串完畢,最后的節(jié)點(diǎn)isEnd置為true,表示一個(gè)字符串的結(jié)束
    cur.isEnd = true;
}

查找字符串

基于Trie結(jié)構(gòu)可以查找字符串、匹配前綴、查找出現(xiàn)次數(shù)等等,這里我們給出查找字符串和查找前綴的方法。

比較簡(jiǎn)單,我們從字典樹的根開始,查找前綴:

  • 如果子節(jié)點(diǎn)存在。沿著指針移動(dòng)到子節(jié)點(diǎn),繼續(xù)搜索下一個(gè)字符。
  • 如果子節(jié)點(diǎn)不存在。說明字典樹中不包含該前綴,返回空指針。

可以看到查找字符串相比于匹配前綴,僅僅是多了一個(gè)最下層子節(jié)點(diǎn)是否是一個(gè)字符串的結(jié)束的判斷而已。

/**
 * 查找字符串
 */
public boolean search(String word) {
    Trie end = searchPrefix(word);
    return end != null && end.isEnd;
}

/**
 * 匹配前綴
 */
public boolean startsWith(String prefix) {
    return searchPrefix(prefix) != null;
}

private Trie searchPrefix(String prefix) {
    //初始默認(rèn)為根節(jié)點(diǎn),根節(jié)點(diǎn)不包含任何字符
    Trie cur = this;
    //遍歷該字符串的字符數(shù)組
    for (char c : prefix.toCharArray()) {
        //如果該節(jié)點(diǎn)的下層不包含此字符,那么直接返回null
        if (!cur.next.containsKey(c)) {
            return null;
        }
        //查找下一層節(jié)點(diǎn)
        cur = cur.next.get(c);
    }
    return cur;
}

Trie的總結(jié)

假設(shè)我們加入了she、he、his、her這四個(gè)字符串,那么Trie的結(jié)構(gòu)如下,其中紅色節(jié)點(diǎn)表示其屬于某個(gè)字符串的尾部節(jié)點(diǎn)。

Trie時(shí)間復(fù)雜度:初始化為O(1),每次操作為O(N),N為插入或查找的字符串的長(zhǎng)度。

Trie空間復(fù)雜度:O(N),N表示Trie結(jié)點(diǎn)數(shù)量,或者說所有插入字符串的長(zhǎng)度之和(減去相同前綴長(zhǎng)度)。如果是采用定長(zhǎng)數(shù)組表示next,那么空間復(fù)雜度為O(N*M),M表示字符集的大小,即數(shù)組長(zhǎng)度。

可以看到,使用Trie的數(shù)據(jù)結(jié)構(gòu)使得插入、查詢?nèi)~、查詢前綴的時(shí)間復(fù)雜度與已插入的單詞數(shù)目和長(zhǎng)度無關(guān),這是它的一個(gè)優(yōu)點(diǎn)。

但是,Trie又名前綴樹,因?yàn)樗荒芑谇熬Y匹配實(shí)現(xiàn)某些功能。另一些功能,例如判斷一段字符串中是否包含某些關(guān)鍵詞,不需要前綴匹配,此時(shí)就無法使用Trie了。

相關(guān)題目如下:208. 實(shí)現(xiàn) Trie (前綴樹)

完整實(shí)現(xiàn)如下:

class Trie {
    /**
     * 經(jīng)過該節(jié)點(diǎn)的字符串的下層節(jié)點(diǎn)
     */
    Map<Character, Trie> next;
    /**
     * 該節(jié)點(diǎn)是否是一個(gè)字符串的結(jié)束
     */
    boolean isEnd;

    public Trie() {
        this.next = new HashMap<>();
        this.isEnd = false;
    }

    public void insert(String word) {
        //初始默認(rèn)為根節(jié)點(diǎn),根節(jié)點(diǎn)不包含任何字符
        Trie cur = this;
        //遍歷該字符串的字符數(shù)組
        for (char c : word.toCharArray()) {
            //如果該節(jié)點(diǎn)的下層不包含此字符,那么加入一個(gè)新節(jié)點(diǎn)進(jìn)去
            if (!cur.next.containsKey(c)) {
                cur.next.put(c, new Trie());
            }
            //查找下一層節(jié)點(diǎn)
            cur = cur.next.get(c);
        }
        //遍歷字符串完畢,最后的節(jié)點(diǎn)isEnd置為true,表示一個(gè)字符串的結(jié)束
        cur.isEnd = true;
    }

    /**
     * 查找字符串
     */
    public boolean search(String word) {
        Trie end = searchPrefix(word);
        return end != null && end.isEnd;
    }

    /**
     * 匹配前綴
     */
    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;
    }

    private Trie searchPrefix(String prefix) {
        //初始默認(rèn)為根節(jié)點(diǎn),根節(jié)點(diǎn)不包含任何字符
        Trie cur = this;
        //遍歷該字符串的字符數(shù)組
        for (char c : prefix.toCharArray()) {
            //如果該節(jié)點(diǎn)的下層不包含此字符,那么直接返回null
            if (!cur.next.containsKey(c)) {
                return null;
            }
            //查找下一層節(jié)點(diǎn)
            cur = cur.next.get(c);
        }
        return cur;
    }

    public static void main(String[] args) {
        Trie trie = new Trie();
        trie.insert("你是xxl嗎?");
    }
}

以上就是詳解Java前綴樹Trie的原理及代碼實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于Java前綴樹Trie的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java實(shí)現(xiàn)掃雷游戲入門程序

    java實(shí)現(xiàn)掃雷游戲入門程序

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)掃雷游戲入門程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • MybatisPlus調(diào)用原生SQL的三種方法實(shí)例詳解

    MybatisPlus調(diào)用原生SQL的三種方法實(shí)例詳解

    這篇文章主要介紹了MybatisPlus調(diào)用原生SQL的三種方法,在有些情況下需要用到MybatisPlus查詢?cè)鶶QL,MybatisPlus其實(shí)帶有運(yùn)行原生SQL的方法,我這里列舉三種,需要的朋友可以參考下
    2022-09-09
  • java連接zookeeper實(shí)現(xiàn)zookeeper教程

    java連接zookeeper實(shí)現(xiàn)zookeeper教程

    這篇文章主要介紹了java連接zookeeper實(shí)現(xiàn)zookeeper教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot整合RabbitMQ及生產(chǎn)全場(chǎng)景高級(jí)特性實(shí)戰(zhàn)

    SpringBoot整合RabbitMQ及生產(chǎn)全場(chǎng)景高級(jí)特性實(shí)戰(zhàn)

    本文主要介紹了SpringBoot整合RabbitMQ及生產(chǎn)全場(chǎng)景高級(jí)特性實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • 區(qū)分Java的方法覆蓋與變量覆蓋

    區(qū)分Java的方法覆蓋與變量覆蓋

    作為初學(xué)者2個(gè)比較容易出錯(cuò)的定義,方法覆蓋和變量覆蓋。下面我們一起來看看作者如何去探討Java的方法覆蓋和變量覆蓋。
    2015-09-09
  • Java語言Iterator轉(zhuǎn)換成 List的方法

    Java語言Iterator轉(zhuǎn)換成 List的方法

    在 Java 中,迭代器(Iterator)是一種用于遍歷集合中元素的對(duì)象,它提供了一種簡(jiǎn)單而一致的方式來訪問集合中的元素,而不需要暴露集合內(nèi)部的結(jié)構(gòu),這篇文章主要介紹了Java語言Iterator轉(zhuǎn)換成 List的方法,需要的朋友可以參考下
    2023-08-08
  • 淺談Springboot整合RocketMQ使用心得

    淺談Springboot整合RocketMQ使用心得

    本篇文章主要介紹了Springboot整合RocketMQ使用心得,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • Spring的BeanFactoryPostProcessor接口示例代碼詳解

    Spring的BeanFactoryPostProcessor接口示例代碼詳解

    這篇文章主要介紹了Spring的BeanFactoryPostProcessor接口,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • Java中的觀察者模式實(shí)例講解

    Java中的觀察者模式實(shí)例講解

    這篇文章主要介紹了Java中的觀察者模式實(shí)例講解,本文先是講解了觀察者模式的概念,然后以實(shí)例講解觀察者模式的實(shí)現(xiàn),以及給出了UML圖,需要的朋友可以參考下
    2014-12-12
  • Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的兩種方法詳解

    Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的兩種方法詳解

    使用具將Excel轉(zhuǎn)為PDF的方法有很多,在這里我給大家介紹兩種常用的方法:使用spire轉(zhuǎn)化PDF、使用jacob實(shí)現(xiàn)Excel轉(zhuǎn)PDF,分別應(yīng)對(duì)兩種不一樣的使用場(chǎng)景,需要的可以參考一下
    2022-01-01

最新評(píng)論