1. 程式人生 > >字首匹配問題與trie樹

字首匹配問題與trie樹

字首匹配問題就是類似於你在某個輸入框中輸入某個字串, 根據你的輸入猜測你要輸入的字串, 比如說, 今天在我的firefox搜尋欄裡面搜尋了"lighttpd"這個關鍵字, 當我再次輸入"ligh"的時候, 輸入框有一個下拉列表提示我"lighttpd".或者, 類似輸入法中的智慧聯想, 輸入前面幾個字元聯想以其為字首的其它片語.這些都是字首匹配技術的典型應用場合.

為了簡單起見, 我們下面的講述假設你所查詢的字串都是由小寫英文字母組成的.

字首匹配問題最自然的想法就是採用樹, 我最開始的考慮是採用一個二叉樹, 根據字典順序排列來進行搜尋.但是這個想法有一些問題, 比如"lighttpd"這個字串, 在我匹配了最前面的"li"之後去搜索字元"g"的時候, 中間可能要跳躍過由字典順序排在'g'之前的字元, 比如'a','b'等等.也就是說, 根據字首"li"去查詢"lig"的時候, 我們不能馬上定位到"lig"的位置, 或者說, 這樣的定位不是O(1)的, 需要O(log2(n))次, 其中n為你所查詢的字元距離字元'a'的距離.

為了解決這個問題, trie樹採用了另一種解決辦法, trie樹中每個節點擁有一個數組, 這個陣列的數量是所有可能出現的字元的數量, 基於前面的假設這裡提到的字串全部由小寫字母組成, 那麼就是26個元素,而陣列的下標是按照字典排序距離字母'a'的距離:
const int num_chars = 2;
struct Trie_node
{
    char* data;
    Trie_node* branch[num_chars];
    Trie_node();
};
假設要搜尋字首'l'開始的字串, 那麼以'a'為字首的所有字串的根節點就是root->branch['l' - 'a'], 其中root是樹的根節點.
搜尋所有以'lg'為字首的字串可以類似展開, 其它的搜尋字首也可以同樣展開.

於是, 字首匹配問題在trie樹中就可以如下展開:比如要搜尋以"li"為字首的索引, 首先根據前面的演算法找到索引為"li"的節點, 則以"li"為字首的字串都在以這個節點為根的子節點中.實際情況中, 這樣的子節點可能是很多的, 需要根據情況進行過濾.

這裡不再多闡述trie樹的資料結構,
這裡
有一份實現原始碼和演算法說明.

可以看到, trie樹對於實現查詢可變字串的索引有很高的效率, 如果要查詢n個字元組成的字串, 只需要n次操作.
同時, trie樹也節省了空間, 比如索引字串"lig"和"ligh"共享了前面的三個字元.

其它相關文章:
http://blog.csdn.net/lwl_ls/archive/2008/05/03/2373069.aspx