Trie Tree 實(shí)現(xiàn)中文分詞器

前言

繼上一篇HashMap實(shí)現(xiàn)中文分詞器后罩旋,對(duì)Trie Tree的好奇和二,又使用Trie Tree實(shí)現(xiàn)了下中文分詞器。效率比HashMap實(shí)現(xiàn)的分詞器更高携冤。

Trie Tree 簡(jiǎn)介

Trie Tree卵皂,又稱單詞字典樹秩铆、查找樹,是一種樹形結(jié)構(gòu)渐裂,是一種哈希樹的變種豺旬。典型應(yīng)用是用于統(tǒng)計(jì)和排序大量的字符串(但不僅限于字符串),所以經(jīng)常被搜索引擎系統(tǒng)用于文本詞頻統(tǒng)計(jì)柒凉。它的優(yōu)點(diǎn)是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高篓跛。

性質(zhì)

它有3個(gè)基本性質(zhì):

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

Trie Tree 結(jié)構(gòu)

Trie Tree

Trie Tree分詞原理:

(1) 從根結(jié)點(diǎn)開始一次搜索林艘,比如搜索【北京】;
(2) 取得要查找關(guān)鍵詞的第一個(gè)字符【北】混坞,并根據(jù)該字符選擇對(duì)應(yīng)的子樹并轉(zhuǎn)到該子樹繼續(xù)進(jìn)行檢索狐援;
(3) 在相應(yīng)的子樹上钢坦,取得要查找關(guān)鍵詞的第二個(gè)字符【京】,并進(jìn)一步選擇對(duì)應(yīng)的子樹進(jìn)行檢索。
(4) 迭代過程……
(5) 在直到判斷樹節(jié)點(diǎn)的isEnd節(jié)點(diǎn)為true則查找結(jié)束(最小匹配原則)啥酱,然后發(fā)現(xiàn)【京】isEnd=true爹凹,則結(jié)束查找。

示例

下面用java簡(jiǎn)單實(shí)現(xiàn)

package cn.com.infcn.algorithm;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * jijs
 * 正向最大匹配
 */
public class TrieTreeDemo {
    static class Node {
        //記錄當(dāng)前節(jié)點(diǎn)的字
        char c;
        //判斷該字是否詞語的末尾镶殷,如果是則為false
        boolean isEnd;
        //子節(jié)點(diǎn)
        List<Node> childList;
        
        public Node(char c) {
            super();
            this.c = c;
            isEnd = false;
            childList = new LinkedList<Node>();
        }
        
        //查找當(dāng)前子節(jié)點(diǎn)中是否保護(hù)c的節(jié)點(diǎn)
        public Node findNode(char c){
            for(Node node : childList){
                if(node.c == c){
                    return node;
                }
            }
            
            return null;
        }
    }
    
    static class TrieTree{
        Node root = new Node(' ');
        
        //構(gòu)建Trie Tree
        public void insert(String words){
            char[] arr = words.toCharArray();
            Node currentNode = root;
            for (char c : arr) {
                Node node = currentNode.findNode(c);
                //如果不存在該節(jié)點(diǎn)則添加
                if(node == null){
                    Node n = new Node(c);
                    currentNode.childList.add(n);
                    currentNode = n;
                }else{
                    currentNode = node;
                }
            }
            //在詞的最后一個(gè)字節(jié)點(diǎn)標(biāo)記為true
            currentNode.isEnd = true;
        }
        
        //判斷Trie Tree中是否包含該詞
        public boolean search(String word){
            char[] arr = word.toCharArray();
            Node currentNode = root;
            for (int i=0; i<arr.length; i++) {
                Node n = currentNode.findNode(arr[i]);
                if(n != null){
                    currentNode = n;
                    //判斷是否為詞的尾節(jié)點(diǎn)節(jié)點(diǎn)
                    if(n.isEnd){
                        if(n.c == arr[arr.length-1]){
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        
        //最大匹配優(yōu)先原則
        public Map<String, Integer> tokenizer(String words){
            char[] arr = words.toCharArray();
            Node currentNode = root;
            Map<String, Integer> map = new HashMap<String, Integer>();
            //記錄Trie Tree 從root開始匹配的所有字
            StringBuilder sb = new StringBuilder();;
            //最后一次匹配到的詞禾酱,最大匹配原則,可能會(huì)匹配到多個(gè)字绘趋,以最長的那個(gè)為準(zhǔn)
            String word="";
            //記錄記錄最后一次匹配坐標(biāo)
            int idx = 0;
            for (int i=0; i<arr.length; i++) {
                Node n = currentNode.findNode(arr[i]);
                if(n != null){
                    sb.append(n.c);
                    currentNode = n;
                    //匹配到詞
                    if(n.isEnd){
                        //記錄最后一次匹配的詞
                        word = sb.toString();
                        //記錄最后一次匹配坐標(biāo)
                        idx = i;
                    }
                }else{
                    //判斷word是否有值
                    if(word!=null && word.length()>0){
                        Integer num = map.get(word);
                        if(num==null){
                            map.put(word, 1);
                        }else{
                            map.put(word, num+1);
                        }
                        //i回退到最后匹配的坐標(biāo)
                        i=idx;
                        //從root的開始匹配
                        currentNode = root;
                        //清空匹配到的詞
                        word = null;
                        //清空當(dāng)前路徑匹配到的所有字
                        sb = new StringBuilder();
                    }
                }
                //已匹配到最后一位
                if(i==arr.length-1){
                    if(word!=null && word.length()>0){
                        Integer num = map.get(word);
                        if(num==null){
                            map.put(word, 1);
                        }else{
                            map.put(word, num+1);
                        }
                    }
                }
            }
            
            return map;
        }
    }

    public static void main(String[] args) {
        TrieTree tree = new TrieTree();
        tree.insert("北京");
        tree.insert("海淀區(qū)");
        tree.insert("中國");
        tree.insert("中國人民");
        tree.insert("中關(guān)村");
        
        String word = "中國";
        //查找該詞是否存在 Trid Tree 中
        boolean flag = tree.search(word);
        if(flag){
            System.out.println("Trie Tree 中已經(jīng)存在【"+word+"】");
        }else{
            System.out.println("Trie Tree 不包含【"+word+"】");
        }
        
        //分詞
        Map<String, Integer> map = tree.tokenizer("中國人民颤陶,中國首都是北京,中關(guān)村在海淀區(qū),中國北京天安門陷遮。中國人");
        for (Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
        
    }
}

想了解更多精彩內(nèi)容請(qǐng)關(guān)注我的公眾號(hào)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滓走,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拷呆,更是在濱河造成了極大的恐慌闲坎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茬斧,死亡現(xiàn)場(chǎng)離奇詭異腰懂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)项秉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門绣溜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人娄蔼,你說我怎么就攤上這事怖喻。” “怎么了岁诉?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵锚沸,是天一觀的道長。 經(jīng)常有香客問我涕癣,道長哗蜈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任坠韩,我火速辦了婚禮距潘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘只搁。我一直安慰自己音比,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布氢惋。 她就那樣靜靜地躺著洞翩,像睡著了一般稽犁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菱农,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天缭付,我揣著相機(jī)與錄音,去河邊找鬼循未。 笑死陷猫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的的妖。 我是一名探鬼主播绣檬,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嫂粟!你這毒婦竟也來了娇未?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤星虹,失蹤者是張志新(化名)和其女友劉穎零抬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宽涌,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡平夜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卸亮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忽妒。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖兼贸,靈堂內(nèi)的尸體忽然破棺而出段直,到底是詐尸還是另有隱情,我是刑警寧澤溶诞,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布鸯檬,位于F島的核電站,受9級(jí)特大地震影響螺垢,放射性物質(zhì)發(fā)生泄漏京闰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一甩苛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧俏站,春花似錦讯蒲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赁酝。三九已至,卻和暖如春旭等,著一層夾襖步出監(jiān)牢的瞬間酌呆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工搔耕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隙袁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓弃榨,卻偏偏與公主長得像菩收,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鲸睛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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