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

詳解Java中字典樹(Trie樹)的圖解與實現

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

簡介

Trie又稱為前綴樹或字典樹,是一種有序樹,它是一種專門用來處理串匹配的數據結構,用來解決一組字符中快速查找某個字符串的問題。Google搜索的關鍵字提示功能相信大家都不陌生,我們在輸入框中進行搜索的時候,會下拉出一系列候選關鍵詞。

image-20220511111955847

上面這個關鍵詞提示功能,底層最基本的原理就是我們今天說的數據結構:Trie樹

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

image-20220511113614538

工作過程

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

數據結構

一個value存儲當前節(jié)點值,用一個26大小的數組存儲當前節(jié)點的孩子節(jié)點,這是一個簡單但是可能產生浪費的方法,可以采用有序存入采用二分法查找,或者采用hash表,跳表進行優(yōu)化。一個標志當前節(jié)點是否可作為尾節(jié)點。

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

初始化

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

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

構建字典樹

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

image-20220511222429713

/**
     * 將當前串插入字典樹
     * @param chars
     */
    public void insertStr(char[] chars) {
        //首先判斷首字符是否已經在字典樹中,然后判斷第二字符,依次往下進行判斷,找到第一個不存在的字符進行插入孩節(jié)點
        Node p = root;
        //表明當前處理到了第幾個字符
        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]) {
                        //當前字符已經存在,不需要再進行存儲
                        //從當前節(jié)點出發(fā),存儲下一個字符
                        p = child;
                        ++ chIndex;
                        find = true;
                        break;
                    }
                }
                if (Boolean.TRUE.equals(find)) {
                    //在孩子中找到了 不用再次存儲
                    break;
                }
                //如果把孩子節(jié)點都找遍了,還沒有找到這個字符,直接將這個字符加入當前節(jié)點的孩子節(jié)點
                Node node = new Node(chars[chIndex]);
                node.childNode = new Node[26];
                children[chars[chIndex] - 'a'] = node;
                p = node;
                ++ chIndex;
            }
        }
        //字符串中字符全部進入tire樹中后,將最后一個字符所在節(jié)點標志為結尾節(jié)點
        p.isTail = true;
    }

應用

匹配有效單詞

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

 /**
     * 查看當前字符串是否可以在trie中找到
     * @param str 主串
     * @return true/false
     */
    public boolean isMatch(String str) {
        //從root開始進行匹配,只要有一個找不到即為匹配失敗
        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;
                    //當比較最后一個字符的時候,這個字符需要是結尾字符才能完全匹配
                    if (chIndex == chars.length && p.isTail) {
                        return true;
                    }
                    break;
                }
            }
            if (Boolean.FALSE.equals(flag)) {
                return false;
            }
        }
        return false;
    }

測試樣例

public static void main(String[] args) {
        //he, him, lot, a
        //初始化Tire樹
        Trie trie = new Trie();
        trie.init();
        //構建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"));

    }

運行結果

image-20220511223308571

關鍵詞提示

根據輸入的關鍵詞前綴,匹配所有可能出現的關鍵詞。首先遍歷字符串,從節(jié)點出發(fā),只要有一個找不到,直接返回null,直至找到最后一個字符對應的節(jié)點,從該節(jié)點出發(fā)找到所有尾節(jié)點。

 /**
     * 找到所有以str為前綴的字符串
     * @param str 前綴串
     * @return 所有以str為前綴的單詞
     */
    public List<String> findStrPrefix(String str) {
        //根據str首先找到str最后一個字符,然后從這個字符出發(fā),找到所有字符串
        List<String> result = new ArrayList<>();
        char[] chars = str.toCharArray();
        //分成兩步走
        //1。找到str最后一個自字符在字典樹中的node
        //2。從該node出發(fā),找到所有的結尾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]) {
                    //已經找到
                    p = child;
                    flag = true;
                    ++ chIndex;
                    break;
                }
            }
            //如果沒有找到,直接返回空
            if (Boolean.FALSE.equals(flag)) {
                return null;
            }
        }
        //找到了最后一個節(jié)點
        //深度優(yōu)先遍歷,查找所有尾節(jié)點
        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());
            }
            //再遞歸查當前節(jié)點的孩子節(jié)點
            dfs(child, str, result);
            //需要將剛剛set進去的節(jié)點刪除,否則影響當前節(jié)點的下一個孩子節(jié)點
            //舉個例子,h的孩子節(jié)點有e,i,當e放進去之后不拿出來,在遍歷到i的時候,就會形成hei
            str.setLength(str.length() - 1);
        }
    }

測試樣例

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

運行結果

總結

到這里Trie樹就講完了,主要就是聚合前綴,通過樹的特性,按照鏈路進行訪問,同時標志尾節(jié)點,標志到當前節(jié)點是一個完整的字符串。

以上就是詳解Java中字典樹(Trie樹)的圖解與實現的詳細內容,更多關于Java字典樹的資料請關注腳本之家其它相關文章!

相關文章

  • Spring中的@RestControllerAdvice注解使用方法解析

    Spring中的@RestControllerAdvice注解使用方法解析

    這篇文章主要介紹了Spring中的@RestControllerAdvice注解使用方法解析,@RestControllerAdvice是Controller的增強 常用于全局異常的捕獲處理 和請求參數的增強,需要的朋友可以參考下
    2024-01-01
  • Spring注解驅動之ApplicationListener異步處理事件說明

    Spring注解驅動之ApplicationListener異步處理事件說明

    這篇文章主要介紹了Spring注解驅動之ApplicationListener異步處理事件說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Spring Validation方法實現原理分析

    Spring Validation方法實現原理分析

    這篇文章主要介紹了Spring Validation實現原理分析,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • java尋找迷宮路徑的簡單實現示例

    java尋找迷宮路徑的簡單實現示例

    這篇文章主要介紹了java尋找迷宮路徑的簡單實現示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10
  • IDEA插件Statistic統(tǒng)計代碼快速分辨爛項目

    IDEA插件Statistic統(tǒng)計代碼快速分辨爛項目

    這篇文章主要為大家介紹了使用IDEA插件Statistic來統(tǒng)計項目代碼,幫助大家快速識別出爛項目,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-01-01
  • Eclipse中配置Maven build打包的方法步驟

    Eclipse中配置Maven build打包的方法步驟

    這篇文章主要介紹了Eclipse中配置Maven build打包的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • Springboot+Mybatis-plus不使用SQL語句進行多表添加操作及問題小結

    Springboot+Mybatis-plus不使用SQL語句進行多表添加操作及問題小結

    這篇文章主要介紹了在Springboot+Mybatis-plus不使用SQL語句進行多表添加操作,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • 如何使用Spring自定義Xml標簽

    如何使用Spring自定義Xml標簽

    要實現自定義的xml配置,需要有兩個默認spring配置文件來支持。一個是spring.schemas,一個是spring.handlers,前者是為了驗證你自定義的xml配置文件是否符合你的格式要求,后者是告訴spring該如何來解析你自定義的配置文件。本文將介紹如何使用Spring自定義Xml標簽
    2021-06-06
  • Java import導入及訪問控制權限修飾符原理解析

    Java import導入及訪問控制權限修飾符原理解析

    這篇文章主要介紹了Java import導入及訪問控制權限修飾符過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • Java兩種方法計算出階乘尾部連續(xù)0的個數

    Java兩種方法計算出階乘尾部連續(xù)0的個數

    這篇文章主要介紹了Java兩種方法計算出階乘尾部連續(xù)0的個數,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03

最新評論