日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Trie树进阶:Double-Array Trie原理及状态转移过程详解

發(fā)布時間:2025/3/20 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Trie树进阶:Double-Array Trie原理及状态转移过程详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言:

? Trie樹本身就是一個很迷人的數(shù)據(jù)結(jié)構(gòu),何況是其改進(jìn)的方案。

? 在本博客中我會從DAT(Double-Array Tire)的原理開始,并結(jié)合其源代碼對DAT的狀態(tài)轉(zhuǎn)移過程進(jìn)行解析。如果因此你能從我的博客中有所收獲或啟發(fā),It's my pleasure.


本文鏈接:http://blog.csdn.net/lemon_tree12138/article/details/49281865 -- Q-WHai?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?--轉(zhuǎn)載請注明出處


特別說明:

0.關(guān)于Trie樹的構(gòu)建及使用,請移步:http://blog.csdn.net/lemon_tree12138/article/details/49177509

1.本文參考:

(0)雙數(shù)組Trie樹(DoubleArrayTrie)Java實現(xiàn)-碼農(nóng)場

(1)基于雙數(shù)組Trie樹算法的字典改進(jìn)和實現(xiàn) - 期刊論文 - 道客巴巴


圖形展示及說明:

0.樸素Trie樹示意圖:

??

? 從上圖中可以看到,這樣的樹結(jié)構(gòu)是非常稀疏的。造成了資源的巨大浪費。


1.DAT節(jié)點示意圖:

??

? 這里"NULL"代表結(jié)束。


DAT原理說明:

0.簡介:

? 在學(xué)習(xí)DAT(Double-Array Trie)之前,如果你對Tire樹的了解還是處在一個模糊的狀態(tài),那么我想你現(xiàn)在可以移步到本人的另一篇博客《數(shù)據(jù)結(jié)構(gòu):字典樹的基本使用》,在對Trie樹有一個基本的了解之后,再來學(xué)習(xí)本文的內(nèi)容應(yīng)該會更加輕松自如(如果你對Trie樹已經(jīng)有了或淺或深的了解,那么可以直接看下面的內(nèi)容了)。

? DAT的本質(zhì)是一個有限自動機(jī)(因為博主在學(xué)習(xí)DAT之前對自動機(jī)的相關(guān)內(nèi)容也是一知半解,在學(xué)習(xí)DAT的過程,難免有一些痛苦。博主也緊追一下這方面的知識,也會在后面的博客中寫一些相關(guān)的博文).我們要構(gòu)建一些狀態(tài),用于狀態(tài)的自動轉(zhuǎn)移。顧名思義,在DAT中用的就是雙數(shù)組:base數(shù)組和check數(shù)組。雙數(shù)組的分工是:base負(fù)責(zé)記錄狀態(tài),用于狀態(tài)轉(zhuǎn)移;check負(fù)責(zé)檢查各個字符串是否是從同一個狀態(tài)轉(zhuǎn)移而來,當(dāng)check[i]為負(fù)值時,表示此狀態(tài)為字符串的結(jié)束。

? 你可能問一個這樣的問題:那么base數(shù)組和check數(shù)組是怎么來進(jìn)行狀態(tài)轉(zhuǎn)移呢?

? 請看下面關(guān)于DAT雙數(shù)組的計算過程。


1.DAT中雙數(shù)組的計算過程:

假定有字符串狀態(tài)s,當(dāng)前字符串狀態(tài)為t,假定t加了一個字符c就等于狀態(tài)tc,加了一個字符x等于狀態(tài)tx,那么有:
base[t] + c.code = base[tc]
base[t] + x.code = base[tx]
check[tc] = check[tx]

上面的幾個等式就是狀態(tài)base和它的轉(zhuǎn)移方程。


Double-Array Trie源碼解析:

0.特別說明:

? DAT中的節(jié)點信息如下:

private static class Node {int code;int depth;int left;int right; }? code: 代表節(jié)點字符的編碼。如:'a'.code = 97

? depth: 代表節(jié)點所在樹的深度。root.depth = 0

? left: 代表節(jié)點的子節(jié)點在字典中范圍的左邊界

? rigth:?代表節(jié)點的子節(jié)點在字典中范圍的右邊界


1.DAT的創(chuàng)建

? 和Trie樹一樣,DAT的創(chuàng)建只是創(chuàng)建Root的過程。如下:

public int build(List<String> _key) {key = _key;...resize(65536 * 32);...Node root_node = new Node();root_node.left = 0;root_node.right = keySize;root_node.depth = 0;...return error_;}


2.為節(jié)點parent生成子節(jié)點

? 在生成子節(jié)點的過程中,如果碰到parent='B',而'B'又是某一個key的結(jié)尾。該如何辦呢?

? 比如:比如若以"一舉"中的'舉'字符為parent,那么parent.depth = 2,"一舉".length = 2.

? 遇到這種情況,我們就需要對其進(jìn)行過濾操作,過程如下:

String tmp = key.get(i);int currCode = 0;if (tmp.length() != parent.depth) {currCode = (int) tmp.charAt(parent.depth) + 1;}完整過程:

private int fetch(Node parent, List<Node> siblings) {...int prevCode = 0;for (int i = parent.left; i < parent.right; i++) {if (key.get(i).length() < parent.depth) {continue;}String tmp = key.get(i);int currCode = 0;if (tmp.length() != parent.depth) {currCode = (int) tmp.charAt(parent.depth) + 1;}...if (currCode != prevCode || siblings.size() == 0) {Node tmp_node = new Node();tmp_node.depth = parent.depth + 1;tmp_node.code = currCode;tmp_node.left = i;if (siblings.size() != 0) {siblings.get(siblings.size() - 1).right = i;}siblings.add(tmp_node);}prevCode = currCode;}if (siblings.size() != 0) {siblings.get(siblings.size() - 1).right = parent.right;}return siblings.size();}

3.向Trie樹中插入子節(jié)點

? 在DAT的創(chuàng)建過程中,insert是關(guān)鍵部分。

? 在insert操作里,我們使用了遞歸的思路來解決問題。為什么要利用遞歸呢?因為在我們狀態(tài)轉(zhuǎn)移的過程中,父節(jié)點的base值需要依賴子返回的begin值,因為在DAT中,code[null] = 0,所以也可以認(rèn)為是依賴于子節(jié)點的check值,如此反復(fù),層層嵌套。關(guān)于這一點在下面的結(jié)構(gòu)圖展示中更容易體現(xiàn)。

(0)check的合法性檢查

? 之前我們說check數(shù)組是為了檢查各個字符串是否是從同一個狀態(tài)轉(zhuǎn)移而來,但是,要如何檢查呢?看下面這段代碼:

outer: while (true) {position++;if (check[position] != 0) {continue;} else if (first == 0) {...}begin = position - siblings.get(0).code; // 當(dāng)前位置離第一個兄弟節(jié)點的距離...for (int i = 1; i < siblings.size(); i++) {if (check[begin + siblings.get(i).code] != 0) {continue outer;}}break;}? 這里的position即在數(shù)組中的下標(biāo)。可以看到這是一個循環(huán)遍歷的過程,我們在一個合適的位置開始,逐步地嘗試check值是否合法,找到第一個合法的begin值即可。
? 而check[i]合法的條件就是check[i]是否為0。如果check[i]不為0,則說明此位置已經(jīng)被別的狀態(tài)占領(lǐng)了,需要更換到下一個位置。

(1)計算所有子節(jié)點的check值

for (int i = 0; i < siblings.size(); i++) {check[begin + siblings.get(i).code] = begin;}

(2)計算所有子節(jié)點的base值

private int insert(List<Node> siblings) {...for (int i = 0; i < siblings.size(); i++) {List<Node> new_siblings = new ArrayList<Node>();if (fetch(siblings.get(i), new_siblings) == 0) {base[begin + siblings.get(i).code] = (value != null) ? (-value[siblings.get(i).left] - 1) : (-siblings.get(i).left - 1);...} else {int h = insert(new_siblings);base[begin + siblings.get(i).code] = h;}}return begin;}在這一步中,大家可以很明顯地看到,這是一個遞歸的過程。我們需要獲得子節(jié)點的begin值。


采用遞歸之后,我們的DAT節(jié)點的狀態(tài)轉(zhuǎn)移過程

(3)整體的insert過程:

private int insert(List<Node> siblings) {...// check的合法性檢查...// 計算所有子節(jié)點的check值// 計算所有子節(jié)點的base值...}

DAT中雙數(shù)組的狀態(tài)轉(zhuǎn)移過程


4.前綴查詢

? 現(xiàn)在假設(shè)待查找字符串T="走廊里的壁畫",我們需要在之前的字典中查找所有是T前綴的字符串。我們要怎么做呢?

? 其實在上面的雙數(shù)組狀態(tài)轉(zhuǎn)移過程圖中,我們可以很清楚地找到一條滿足條件的路徑.如下:

??

關(guān)鍵代碼如下:

public List<Integer> commonPrefixSearch(String key, int pos, int len, int nodePos) {...int b = base[nodePos];...for (int i = pos; i < len; i++) {p = b;n = base[p];if (b == check[p] && n < 0) {result.add(-n - 1);}p = b + (int) (keyChars[i]) + 1;if (b == check[p]) {b = base[p];} else {return result;}}p = b;n = base[p];if (b == check[p] && n < 0) {result.add(-n - 1);}return result;}

5.關(guān)鍵詞智能提示:

? 在上面“前綴查詢”的例子中,我們的匹配字符串中比較長,在還沒到字符串的最后一位就遇到狀態(tài)停止標(biāo)志。而如果匹配字符串比較短,我就還可以做一些其他的事情了,比如常見的搜索引擎中關(guān)鍵詞智能提示。

? 過程就是在上一步的基礎(chǔ)上,把終止循環(huán)的條件修改為直到遇到一個狀態(tài)停步標(biāo)志.這樣我們就可以在遍歷整條路徑。

? 這個功能,在源碼中沒有涉及。而本文的目的是在于解釋DAT的原理和其狀態(tài)轉(zhuǎn)移的過程。所以,這里就暫不貼代碼了。不過,在后期的《搜索引擎:對用戶輸入有誤的關(guān)鍵詞進(jìn)行糾錯處理》博客中應(yīng)該會有所涉及。感興趣的朋友,可以關(guān)注下。


實現(xiàn)源碼下載:

http://download.csdn.net/detail/u013761665/9201933

《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的Trie树进阶:Double-Array Trie原理及状态转移过程详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。