最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java中字典樹(Trie樹)的圖解與實(shí)現(xiàn)

 更新時(shí)間:2022年05月12日 10:18:46   作者:Carol淋  
Trie又稱為前綴樹或字典樹,是一種有序樹,它是一種專門用來處理串匹配的數(shù)據(jù)結(jié)構(gòu)。本文將利用圖解詳細(xì)講解Trie樹的實(shí)現(xiàn),需要的可以參考一下

簡(jiǎn)介

Trie又稱為前綴樹或字典樹,是一種有序樹,它是一種專門用來處理串匹配的數(shù)據(jù)結(jié)構(gòu),用來解決一組字符中快速查找某個(gè)字符串的問題。Google搜索的關(guān)鍵字提示功能相信大家都不陌生,我們?cè)谳斎肟蛑羞M(jìn)行搜索的時(shí)候,會(huì)下拉出一系列候選關(guān)鍵詞。

image-20220511111955847

上面這個(gè)關(guān)鍵詞提示功能,底層最基本的原理就是我們今天說的數(shù)據(jù)結(jié)構(gòu):Trie樹

我們先看看Tire樹長(zhǎng)什么樣子,以單純的單詞匹配為例,首先它是一棵多叉樹結(jié)構(gòu),根節(jié)點(diǎn)是一個(gè)空字符,樹中節(jié)點(diǎn)分為普通節(jié)點(diǎn)和結(jié)尾節(jié)點(diǎn)(如圖中紅色節(jié)點(diǎn))。結(jié)尾節(jié)點(diǎn)表示加上前面前綴,可以稱為一個(gè)單詞,如圖中hi,him。

image-20220511113614538

工作過程

Tire樹與之前串匹配最大的不同點(diǎn)是,之前我們都是單模式串,查看主串中是否有與模式串匹配的子串,操作過程也是用模式串去與主串進(jìn)行比較。而Tire樹是多模式串,我們先將模式串提前構(gòu)建成Tire樹,然后查看主串是否匹配模式串,且更適用于類似如上關(guān)鍵詞提示的前綴匹配。接下來我們自己通過實(shí)現(xiàn)一個(gè)簡(jiǎn)易的關(guān)鍵詞提示功能來講解Tire樹。

數(shù)據(jù)結(jié)構(gòu)

一個(gè)value存儲(chǔ)當(dāng)前節(jié)點(diǎn)值,用一個(gè)26大小的數(shù)組存儲(chǔ)當(dāng)前節(jié)點(diǎn)的孩子節(jié)點(diǎn),這是一個(gè)簡(jiǎn)單但是可能產(chǎn)生浪費(fèi)的方法,可以采用有序存入采用二分法查找,或者采用hash表,跳表進(jìn)行優(yōu)化。一個(gè)標(biāo)志當(dāng)前節(jié)點(diǎn)是否可作為尾節(jié)點(diǎn)。

/**
     * Trie樹節(jié)點(diǎn)
     * 假設(shè)我們只做26個(gè)小寫字母下的匹配
     */
    public static class Node{
        //當(dāng)前節(jié)點(diǎn)值
        private char value;
        //當(dāng)前節(jié)點(diǎn)的孩子節(jié)點(diǎn)
        private Node[] childNode;
        //標(biāo)志當(dāng)前節(jié)點(diǎn)是否是某單詞結(jié)尾
        private boolean isTail;
        public Node(char value) {
            this.value = value;
        }
    }

初始化

初始化一個(gè)僅有root節(jié)點(diǎn)的Tire樹,root節(jié)點(diǎn)值為'/0'。

Node root;
public void init() {
        root = new Node('\0');
        root.childNode = new Node[26];
}

構(gòu)建字典樹

將需要加入的模式串加入Tire樹,遍歷當(dāng)前字符串字符,從Tire樹根節(jié)點(diǎn)開始查找當(dāng)前字符,如果字符已經(jīng)存在不需要處理,并且從這個(gè)字符節(jié)點(diǎn)出發(fā),查看下一個(gè)字符是否存在,如果當(dāng)前節(jié)點(diǎn)不存Tire樹,才需要插入當(dāng)前字符,當(dāng)插入最后一個(gè)字符時(shí)需要標(biāo)志當(dāng)前字符節(jié)點(diǎn)為尾節(jié)點(diǎn)。

image-20220511222429713

/**
     * 將當(dāng)前串插入字典樹
     * @param chars
     */
    public void insertStr(char[] chars) {
        //首先判斷首字符是否已經(jīng)在字典樹中,然后判斷第二字符,依次往下進(jìn)行判斷,找到第一個(gè)不存在的字符進(jìn)行插入孩節(jié)點(diǎn)
        Node p = root;
        //表明當(dāng)前處理到了第幾個(gè)字符
        int chIndex = 0;
        while (chIndex < chars.length) {
            while (chIndex < chars.length && null != p) {
                Node[] children = p.childNode;
                boolean find = false;
                for (Node child : children) {
                    if (null == child) {continue;}
                    if (child.value == chars[chIndex]) {
                        //當(dāng)前字符已經(jīng)存在,不需要再進(jìn)行存儲(chǔ)
                        //從當(dāng)前節(jié)點(diǎn)出發(fā),存儲(chǔ)下一個(gè)字符
                        p = child;
                        ++ chIndex;
                        find = true;
                        break;
                    }
                }
                if (Boolean.TRUE.equals(find)) {
                    //在孩子中找到了 不用再次存儲(chǔ)
                    break;
                }
                //如果把孩子節(jié)點(diǎn)都找遍了,還沒有找到這個(gè)字符,直接將這個(gè)字符加入當(dāng)前節(jié)點(diǎn)的孩子節(jié)點(diǎn)
                Node node = new Node(chars[chIndex]);
                node.childNode = new Node[26];
                children[chars[chIndex] - 'a'] = node;
                p = node;
                ++ chIndex;
            }
        }
        //字符串中字符全部進(jìn)入tire樹中后,將最后一個(gè)字符所在節(jié)點(diǎn)標(biāo)志為結(jié)尾節(jié)點(diǎn)
        p.isTail = true;
    }

應(yīng)用

匹配有效單詞

遍歷字符串,從根節(jié)點(diǎn)出發(fā),查看字符是否存在,只要存在不存在的情況,直接返回false,如果每個(gè)字符都存在,判斷最后一個(gè)字符是否為結(jié)尾節(jié)點(diǎn),如果不是,到這里還不是一個(gè)有效單詞,返回false,否則,返回true。

 /**
     * 查看當(dāng)前字符串是否可以在trie中找到
     * @param str 主串
     * @return true/false
     */
    public boolean isMatch(String str) {
        //從root開始進(jìn)行匹配,只要有一個(gè)找不到即為匹配失敗
        char[] chars = str.toCharArray();
        int chIndex = 0;
        Node p = root;
        while (null != p) {
            Node[] children = p.childNode;
            boolean flag = false;
            for (Node child : children) {
                if (null == child) {continue;}
                if (child.value == chars[chIndex]) {
                    flag = true;
                    p = child;
                    ++ chIndex;
                    //當(dāng)比較最后一個(gè)字符的時(shí)候,這個(gè)字符需要是結(jié)尾字符才能完全匹配
                    if (chIndex == chars.length && p.isTail) {
                        return true;
                    }
                    break;
                }
            }
            if (Boolean.FALSE.equals(flag)) {
                return false;
            }
        }
        return false;
    }

測(cè)試樣例

public static void main(String[] args) {
        //he, him, lot, a
        //初始化Tire樹
        Trie trie = new Trie();
        trie.init();
        //構(gòu)建Tire樹,只有以下單詞才是有效單詞
        trie.insertStr("he".toCharArray());
        trie.insertStr("him".toCharArray());
        trie.insertStr("lot".toCharArray());
        trie.insertStr("a".toCharArray());
        //匹配字符串是否為有效單詞
        System.out.println(trie.isMatch("lot"));
        System.out.println(trie.isMatch("lit"));

    }

運(yùn)行結(jié)果

image-20220511223308571

關(guān)鍵詞提示

根據(jù)輸入的關(guān)鍵詞前綴,匹配所有可能出現(xiàn)的關(guān)鍵詞。首先遍歷字符串,從節(jié)點(diǎn)出發(fā),只要有一個(gè)找不到,直接返回null,直至找到最后一個(gè)字符對(duì)應(yīng)的節(jié)點(diǎn),從該節(jié)點(diǎn)出發(fā)找到所有尾節(jié)點(diǎn)。

 /**
     * 找到所有以str為前綴的字符串
     * @param str 前綴串
     * @return 所有以str為前綴的單詞
     */
    public List<String> findStrPrefix(String str) {
        //根據(jù)str首先找到str最后一個(gè)字符,然后從這個(gè)字符出發(fā),找到所有字符串
        List<String> result = new ArrayList<>();
        char[] chars = str.toCharArray();
        //分成兩步走
        //1。找到str最后一個(gè)自字符在字典樹中的node
        //2。從該node出發(fā),找到所有的結(jié)尾node,即為以str為前綴的字符串
        int chIndex = 0;
        Node p = root;
        while (null != p && chIndex < chars.length) {
            Node[] children = p.childNode;
            boolean flag = false;
            for (Node child : children) {
                if (null == child) {continue;}
                if (child.value == chars[chIndex]) {
                    //已經(jīng)找到
                    p = child;
                    flag = true;
                    ++ chIndex;
                    break;
                }
            }
            //如果沒有找到,直接返回空
            if (Boolean.FALSE.equals(flag)) {
                return null;
            }
        }
        //找到了最后一個(gè)節(jié)點(diǎn)
        //深度優(yōu)先遍歷,查找所有尾節(jié)點(diǎn)
        this.dfs(p, new StringBuilder(str), result);
        return result;
    }

    public void dfs(Node p, StringBuilder str, List<String> result) {
        Node[] children = p.childNode;
        for (Node child : children) {
            if (null == child) {
                continue;
            }
            str.append(child.value);
            if (child.isTail) {
                result.add(str.toString());
            }
            //再遞歸查當(dāng)前節(jié)點(diǎn)的孩子節(jié)點(diǎn)
            dfs(child, str, result);
            //需要將剛剛set進(jìn)去的節(jié)點(diǎn)刪除,否則影響當(dāng)前節(jié)點(diǎn)的下一個(gè)孩子節(jié)點(diǎn)
            //舉個(gè)例子,h的孩子節(jié)點(diǎn)有e,i,當(dāng)e放進(jìn)去之后不拿出來,在遍歷到i的時(shí)候,就會(huì)形成hei
            str.setLength(str.length() - 1);
        }
    }

測(cè)試樣例

public static void main(String[] args) {
        //he, him, lot, a
        //初始化Tire樹
        Trie trie = new Trie();
        trie.init();
        //構(gòu)建Tire樹,只有以下單詞才是有效單詞
        trie.insertStr("he".toCharArray());
        trie.insertStr("him".toCharArray());
        trie.insertStr("lot".toCharArray());
        trie.insertStr("a".toCharArray());
        //匹配字符串是否為有效單詞
        List<String> strings = trie.findStrPrefix("h");
    }

運(yùn)行結(jié)果

總結(jié)

到這里Trie樹就講完了,主要就是聚合前綴,通過樹的特性,按照鏈路進(jìn)行訪問,同時(shí)標(biāo)志尾節(jié)點(diǎn),標(biāo)志到當(dāng)前節(jié)點(diǎn)是一個(gè)完整的字符串。

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

相關(guān)文章

  • Java設(shè)計(jì)模式之狀態(tài)模式詳解

    Java設(shè)計(jì)模式之狀態(tài)模式詳解

    Java?中的狀態(tài)模式(State?Pattern)是一種行為型設(shè)計(jì)模式,它允許對(duì)象在內(nèi)部狀態(tài)發(fā)生改變時(shí)改變其行為,本文將詳細(xì)介紹?Java?中的狀態(tài)模式,我們將從狀態(tài)模式的概述、結(jié)構(gòu)與實(shí)現(xiàn)、優(yōu)缺點(diǎn)、適用場(chǎng)景等方面進(jìn)行講解,需要的朋友可以參考下
    2023-05-05
  • Java 十進(jìn)制轉(zhuǎn)二、八、十六進(jìn)制的字符串

    Java 十進(jìn)制轉(zhuǎn)二、八、十六進(jìn)制的字符串

    本文主要介紹了十進(jìn)制轉(zhuǎn)二進(jìn)制;十進(jìn)制轉(zhuǎn)八進(jìn)制;十進(jìn)制轉(zhuǎn)十六進(jìn)制的方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • java實(shí)現(xiàn)163郵箱發(fā)送郵件到qq郵箱成功案例

    java實(shí)現(xiàn)163郵箱發(fā)送郵件到qq郵箱成功案例

    這篇文章主要為大家分享了java實(shí)現(xiàn)163郵箱發(fā)送郵件到qq郵箱成功案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • java在cmd運(yùn)行"-d"和"-cp"參數(shù)解讀

    java在cmd運(yùn)行"-d"和"-cp"參數(shù)解讀

    這篇文章主要介紹了java在cmd運(yùn)行"-d"和"-cp"參數(shù)用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Java進(jìn)行Appium自動(dòng)化測(cè)試的實(shí)現(xiàn)

    Java進(jìn)行Appium自動(dòng)化測(cè)試的實(shí)現(xiàn)

    這篇文章主要介紹了Java進(jìn)行Appium自動(dòng)化測(cè)試的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Mybatis批量更新數(shù)據(jù)庫(kù)錯(cuò)誤問題

    Mybatis批量更新數(shù)據(jù)庫(kù)錯(cuò)誤問題

    這篇文章主要介紹了Mybatis批量更新數(shù)據(jù)庫(kù)錯(cuò)誤問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java使用Hutool執(zhí)行日期的加法和減法操作方法

    Java使用Hutool執(zhí)行日期的加法和減法操作方法

    使用Hutool進(jìn)行日期的加法和減法操作,可以使用`DateUtil.offsetXXX()`方法來實(shí)現(xiàn),這些方法會(huì)返回一個(gè)新的日期,而不是在原日期上進(jìn)行修改,本文給大家介紹Java使用Hutool執(zhí)行日期的加法和減法操作方法,感興趣的朋友一起看看吧
    2023-11-11
  • Java I/O中I/O流的典型使用方式詳解

    Java I/O中I/O流的典型使用方式詳解

    這篇文章主要介紹了Java I/O中I/O流的典型使用方式詳解,盡管可以通過不同的方式組合IO流類,但我們可能也就只用到其中的幾種組合。下面的例子可以作為典型的IO用法的基本參考,,需要的朋友可以參考下
    2019-06-06
  • SpringBoot集成WebSocket實(shí)現(xiàn)前后端消息互傳的方法

    SpringBoot集成WebSocket實(shí)現(xiàn)前后端消息互傳的方法

    這篇文章主要介紹了SpringBoot集成WebSocket實(shí)現(xiàn)前后端消息互傳的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 通過JDK源碼學(xué)習(xí)InputStream詳解

    通過JDK源碼學(xué)習(xí)InputStream詳解

    InputStream抽象類是所有字節(jié)輸入流的類的超類。這篇文章主要給大家介紹了關(guān)于通過JDK源碼學(xué)習(xí)InputStream的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11

最新評(píng)論

玉门市| 靖宇县| 腾冲县| 理塘县| 扶绥县| 岳阳市| 南投县| 扶风县| 扶风县| 芮城县| 宜宾县| 青海省| 新宁县| 连南| 阜康市| 三原县| 建昌县| 澳门| 赫章县| 华坪县| 清原| 丹阳市| 益阳市| 峨眉山市| 嘉禾县| 雅安市| 和田市| 宣城市| 汝城县| 合肥市| 包头市| 兴安县| 芦山县| 黄骅市| 东宁县| 阿合奇县| 牙克石市| 东辽县| 土默特右旗| 共和县| 公主岭市|