專利名稱:一種適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表及其生成算法的制作方法
技術(shù)領(lǐng)域:
本發(fā)明屬于計(jì)算機(jī)信息處理領(lǐng)域,特別涉及一種適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表及其生成算法,可以廣泛應(yīng)用于實(shí)時(shí)性要求高的海量數(shù)據(jù)查找。背景技術(shù):
計(jì)算機(jī)信息查詢、規(guī)則匹配等大量實(shí)際問題都可以轉(zhuǎn)換為多關(guān)鍵字查找問題。許多問題對(duì)于實(shí)時(shí)性要求很高,如網(wǎng)絡(luò)安全中“防火墻”的規(guī)則匹配、入侵檢測(cè)中的特征匹配、信息過濾中的信息查找、數(shù)據(jù)挖掘等。這類問題要求在一個(gè)特定的數(shù)據(jù)流,如文件、網(wǎng)絡(luò)數(shù)據(jù)包中實(shí)時(shí)查找特定關(guān)鍵字集合,如“黑客”、“Chinese”、”Microsoft Corporation”等。假設(shè)K={k1,k2,...,kn}是一個(gè)關(guān)鍵字集合,每個(gè)關(guān)鍵字是一個(gè)8位組,T=t1,t2,...,tN是一任意輸入數(shù)據(jù)串,要解決的問題就是在T中發(fā)現(xiàn)所有ki(1≤i≤n)出現(xiàn)的次數(shù)。
我們?cè)谶@里將n稱為關(guān)鍵字集合的大小(關(guān)鍵字地個(gè)數(shù)),SIZE(ki)是關(guān)鍵字ki在計(jì)算機(jī)內(nèi)存中存儲(chǔ)所占用的字節(jié)數(shù)。例如,對(duì)于匹配內(nèi)容集合{“黑客”、“Chinese”、”Microsoft Corporation”},n=3,SIZE(“黑客”)=4,SIZE(“Chinese”)=7。
現(xiàn)有的算法完成此工作,性能一般受3個(gè)因素的影響
n的大小。n越大,查找的時(shí)間越長(zhǎng);
SIZE(ki)的影響。SIZE(ki)越大,查找的時(shí)間越長(zhǎng);
數(shù)據(jù)流的長(zhǎng)度N。N越大,查找的時(shí)間越長(zhǎng)。
Aho和Corasick[AC75]最早提供了解決該問題的線性時(shí)間算法。其后,與基于單關(guān)鍵字查找的Boyer and Moore算法[BM77]類似的思想,試圖在匹配的同時(shí)能夠跳過一些文本,Commentz-Walter[CW79],Hartel[Ha93],C.Jason Coit等[CSJ2001],Sun Wu[SW94]都對(duì)Aho和Corasick算法進(jìn)行了改進(jìn)。[CW79]的算法和[CSJ2001]的算法相似。這些方法雖然數(shù)據(jù)結(jié)構(gòu)相對(duì)簡(jiǎn)單,但是核心匹配部分比較復(fù)雜,其執(zhí)行時(shí)間隨關(guān)鍵字?jǐn)?shù)量的增加而增加是明顯的。
發(fā)明內(nèi)容
本發(fā)明的目的在于克服上述現(xiàn)有技術(shù)的缺點(diǎn),提供了一種通過計(jì)算機(jī)算法中的“空間換時(shí)間”原則,對(duì)關(guān)鍵字集合K,構(gòu)造專用數(shù)據(jù)鏈表,使查找速度大大加快的適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表及其生成算法。
為達(dá)到上述目的,本發(fā)明采用了適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表,樹形鏈表是基于有限自動(dòng)機(jī)DFA的,定義如下
S(a finite set of states) 匹配過程所處的狀態(tài)即每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)節(jié)點(diǎn),對(duì)一個(gè)給定的關(guān)鍵字集P,其最大值是L,狀態(tài)記錄了掃描過程中對(duì)各個(gè)關(guān)鍵字pi的匹配狀況;
E(an alphabet)0-255,即表達(dá)一個(gè)字節(jié)的內(nèi)容;
s(initial state)空字符狀態(tài),即未讀入任何字符狀態(tài),空字符狀態(tài)s∈S;
F(finial state)最終狀態(tài)集,其中每一個(gè)元素表示當(dāng)匹配到某個(gè)關(guān)鍵字時(shí)所處的狀態(tài),F(xiàn)S;
g(transition function)S×E->S,讀入一個(gè)新字符時(shí),狀態(tài)機(jī)狀態(tài)轉(zhuǎn)移的函數(shù)為g(A,a)=B,表示讀入a使?fàn)顟B(tài)機(jī)從狀態(tài)A轉(zhuǎn)移到狀態(tài)B;
本樹形鏈表實(shí)現(xiàn)的關(guān)鍵是找到一個(gè)滿足定義的g的實(shí)現(xiàn);
1)樹形鏈表的構(gòu)造
在DFA的構(gòu)造中g(shù)的設(shè)定
(1)同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移
設(shè)關(guān)鍵字為a1a2a3a4... an;
每一個(gè)字符定義為DFA樹形鏈表的一個(gè)狀態(tài),表示對(duì)該關(guān)鍵字進(jìn)行匹配時(shí)所處的匹配位置,ai對(duì)應(yīng)的狀態(tài)稱為Ai;
則g的定義是
g(Ai,ai)=Ai+1
在樹形鏈表的DFA中,表現(xiàn)為連接一個(gè)關(guān)鍵字各個(gè)字符的鏈,成為樹的一個(gè)分支;
(2)當(dāng)有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移
假設(shè)
關(guān)鍵字A=a1a2a3...aiai+1ai+2ai+3...ajaj+1...an
關(guān)鍵字B=aiai+1...ajb1b2...bm
則稱關(guān)鍵字B前綴包含于A,即B的前綴aiai+1...ai包含在A中,記為PrefixIn(B,A)=true,或A->B,否則PrefixIn(B,A)=false;
PrefixIn是設(shè)定“交叉鏈接”的依據(jù);
定義PrefixInStr(B,A)=aiai+1...aj即包含的前綴部分;
首先,按同一關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移確定狀態(tài)和g,即先確定關(guān)鍵字內(nèi)的狀態(tài)轉(zhuǎn)移關(guān)系;如果A和B之間沒有PrefixIn關(guān)系,則g的構(gòu)造結(jié)束;
如果兩個(gè)關(guān)鍵字有PrefixIn關(guān)系,設(shè)PrefixIn(B,A)=true,且
關(guān)鍵字A=a1a2a3...aiai+1ai+2ai+3...ajaj+1...an;
關(guān)鍵字B=aiai+1...ajb1b2...bm
對(duì)應(yīng)于關(guān)鍵字A的各個(gè)狀態(tài)記為Aa1,Aa2,...Aan;
對(duì)應(yīng)于關(guān)鍵字A的各個(gè)狀態(tài)記為Ba1,Ba2,...Baj,Bb1,Bb2,...Bbm;
在匹配關(guān)鍵字A的同時(shí)也在檢查是否同時(shí)匹配B,在匹配到狀態(tài)Aaj的時(shí)候,如果讀入的下一個(gè)字符是b1,則下一狀態(tài)為Bb1,即應(yīng)添加
g(Aaj,b1)=Bb1
在樹形鏈表的DFA結(jié)構(gòu)中,表現(xiàn)為從A的鏈中指向B的鏈中的一個(gè)“交叉鏈接”;
(3)三個(gè)以上關(guān)鍵字A,B,C的情況
如果任意兩個(gè)關(guān)鍵字之間沒有PrefixIn關(guān)系,按同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移處理,如果同時(shí)最多有兩個(gè)關(guān)鍵字之間有PrefixIn關(guān)系,即僅有小于等于兩個(gè)PrefixIn關(guān)系時(shí),則按兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移處理,同時(shí)有三個(gè)PrefixIn關(guān)系的情況,
設(shè)PrefixIn(B,A)=true,且Prefx(C,A)=true,那么有
g(Ax,bz)=Bz
g(Ay,cz)=Cz
情況1如果Ax≠Ay,則沒有沖突情況發(fā)生,按兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移處理;
情況2如果Ax=Ay,且bz≠cz則也不會(huì)有沖突情況發(fā)生,直接設(shè)置g(Ax,bz)=Bz,g(Ax,cz)=Cz即可;
情況3如果Ax=Ay,且bz=cz,則會(huì)有沖突情況發(fā)生,即
g(Ax,bz)=Bz
g(Ax,bz)=Cz
如果有沖突的情況發(fā)生,B,C之間必有PrefixIn關(guān)系,設(shè)PrefixIn(B,C)=true,則去掉g(Ax,bz)=Bz這個(gè)鏈接,保留g(Ax,bz)=Cz。
適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表的生成算法主要通過兩個(gè)基本操作來實(shí)現(xiàn),即增加關(guān)鍵字的add_key操作和刪除一個(gè)關(guān)鍵字的del_key操作;
1)Add_key算法
節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)如下
struct match_node {struct match_node*ch[256]; //指向下一個(gè)狀態(tài)的256個(gè)指針u_int32_t match_id; //當(dāng)前節(jié)點(diǎn)的一個(gè)標(biāo)志號(hào)u_int32_t depth;//當(dāng)前節(jié)點(diǎn)在樹中的深度struct match_node*parent; //指向當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)struct match_node*left_link,*right link;//一個(gè)循環(huán)鏈表};
首先判斷待插入的關(guān)鍵字cur_key是不是已經(jīng)存在、或與現(xiàn)有的關(guān)鍵字沖突,如有,退出算法;否則,繼續(xù);
其次按g的同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移逐個(gè)當(dāng)前加入key各個(gè)字符對(duì)應(yīng)的節(jié)點(diǎn),如果該節(jié)點(diǎn)已經(jīng)在樹中,處理下一個(gè)節(jié)點(diǎn);否則,添加新的節(jié)點(diǎn);新添加的所有節(jié)點(diǎn)都要設(shè)置相應(yīng)的parent指針,同時(shí)對(duì)新增的節(jié)點(diǎn),把它掛到相應(yīng)的雙向鏈中;
如果root->ch[i]被修改,則初始化并設(shè)置lev1_nodes[i],調(diào)整所有冗余l(xiāng)ev1節(jié)點(diǎn)以保持和更新后的root節(jié)點(diǎn)一致;
按交叉鏈接設(shè)定階段1檢查新添加的cur_key是不是前綴包含于其他key[i],如果是,按g的設(shè)定方法有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移或三個(gè)以上關(guān)鍵字A,B,C的情況設(shè)定從key[i]指向cur_key的交叉鏈接;
最后按交叉鏈接設(shè)定階段2檢查其他key[i]是不是前綴包含于cur_key;如是,按g的設(shè)定方法有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移或三個(gè)以上關(guān)鍵字A,B,C的情況設(shè)定從cur_key指向key[i]的交叉鏈接;
2)Del_key算法
首先在樹中查找是否有待刪除的cur_key,同時(shí)設(shè)置branch_index;Branch_index是待刪除節(jié)點(diǎn)和樹中的現(xiàn)有節(jié)點(diǎn)分叉的地方,設(shè)a1a2a3...ai為cur_key和樹中現(xiàn)有的各個(gè)關(guān)鍵字中有最長(zhǎng)共享前綴,Branch_index指向ai;
其次如果沒有相應(yīng)key,退出算法;否則,繼續(xù);
刪除交叉鏈接;檢查當(dāng)前待刪除關(guān)鍵字cur_key是否前綴包含于其他的key[i],檢查的同時(shí),刪除key[i]中指向cur_key中branch_index之后的節(jié)點(diǎn)的指針,刪除時(shí)要注意尋找替代指針,如g的設(shè)定方法三個(gè)以上關(guān)鍵字A,B,C的情況中刪除關(guān)鍵字C;
安全刪除branch_index之后的節(jié)點(diǎn);
最后調(diào)整branch_index處指向被刪除節(jié)點(diǎn)的指針,該指針為空,應(yīng)該指向相應(yīng)的lev_nodes;
3)樹的結(jié)構(gòu)的保持
在每個(gè)節(jié)點(diǎn)中增加一個(gè)4byte的數(shù)據(jù)域parent,作為一個(gè)指針,指向節(jié)點(diǎn)的父節(jié)點(diǎn),這樣就可以區(qū)分指向子節(jié)點(diǎn)的指針和指向樹的其他分支的指針即交叉鏈接指針和null指針的調(diào)整指針;
4)null指針的處理
所有的null指針都指向root節(jié)點(diǎn)中的256個(gè)指針中的相應(yīng)一個(gè),整棵樹是循環(huán)匹配的,具體指向的定義是
如果Node->ch[i]==null(該指針是null指針)
那么Node->ch[i]=root->ch[i]
在樹做調(diào)整后,root->ch[i]可能會(huì)有變化,要求相關(guān)的null指針也要更新,為了避免搜索整棵樹調(diào)整相關(guān)的null指針,可以讓null指針固定的指向一些節(jié)點(diǎn),當(dāng)root->ch[i]改變的時(shí)候,通過改變相應(yīng)的節(jié)點(diǎn)來達(dá)到一致性;
給樹的第二層256個(gè)節(jié)點(diǎn)分配固定位置的內(nèi)存,稱這256個(gè)節(jié)點(diǎn)為lev1_nodes,有
if root有第I個(gè)分支then
root->ch[i]=lev1_nodes[i]
else
root->ch[i]=root
每個(gè)null指針都是相應(yīng)的節(jié)點(diǎn)中256個(gè)指針中的一個(gè),設(shè)它在ch指針數(shù)組中的索引值為I,則令該null指針為lev1_nodes[i],如果root節(jié)點(diǎn)沒有第I個(gè)分支,為了保持一致性,讓lev1_nodes[i]復(fù)制當(dāng)前root節(jié)點(diǎn)的內(nèi)容,稱復(fù)制root節(jié)點(diǎn)內(nèi)容的lev1_nodes為冗余l(xiāng)ev1節(jié)點(diǎn);
當(dāng)root節(jié)點(diǎn)被修改時(shí),還要修改所有的冗余l(xiāng)ev1節(jié)點(diǎn),每一個(gè)冗余l(xiāng)ev1節(jié)點(diǎn)的內(nèi)容都應(yīng)該和更新后的root節(jié)點(diǎn)的內(nèi)容一致;
除root節(jié)點(diǎn)外,其他節(jié)點(diǎn)中指向lev1_nodes和root節(jié)點(diǎn)的指針都是null指針;
5)為了輔助前綴蘊(yùn)涵的搜索而引入的雙向鏈
當(dāng)一個(gè)新的key要被加入時(shí),要搜索它可能PrefixIn于其他key,為了加速搜索,避免訪問整棵樹,引入一個(gè)雙向鏈,雙向鏈“起始于”樹的256個(gè)lev1_nodes節(jié)點(diǎn),分別代表256個(gè)字符值,每一條鏈把所有節(jié)點(diǎn)值即字符值相同的節(jié)點(diǎn)連接起來,搜索一個(gè)key前綴包含于的所有其他key,同時(shí),這也消除了需要遞歸處理,加速樹的調(diào)整操作;
6)樹形鏈表的生成算法
對(duì)于集合P的每個(gè)元素,執(zhí)行add_key,就可以完成數(shù)據(jù)結(jié)構(gòu)的建立,如下
for(I=1;I<n;I++)
add_key(pi)。
由于隨著半導(dǎo)體技術(shù)的進(jìn)步,內(nèi)存等硬件的價(jià)格大幅度下降,本發(fā)明通過計(jì)算機(jī)算法中的“空間換時(shí)間”原則,對(duì)關(guān)鍵字集合K,構(gòu)造專用數(shù)據(jù)鏈表,從而使得查找速度大大加快,其特點(diǎn)是
1、算法的性能只與N有關(guān),與N是線性關(guān)系,而與k和SIZE(pi)無關(guān);
2、匹配算法簡(jiǎn)單,只需要幾條匯編指令就可以完成;
采用本發(fā)明的方法可對(duì)數(shù)據(jù)鏈表進(jìn)行生成、維護(hù),但本方法占用內(nèi)存空間比較大,對(duì)于一個(gè)匹配內(nèi)容集合P,其所有元素的長(zhǎng)度
i=n
L=∑SIZE(ki)
i=l
則構(gòu)造的專用數(shù)據(jù)鏈表需要占用的內(nèi)存空間為1024L字節(jié)。例如,假設(shè)集合中有1000個(gè)元素,每個(gè)元素的平均長(zhǎng)度為16字節(jié),則需要1000*16*1024,約16MB的空間。對(duì)于128MB的內(nèi)存,可以處理的數(shù)據(jù)串的總長(zhǎng)度L為131072B。四
圖1是本發(fā)明實(shí)施例1的匹配內(nèi)容基本數(shù)據(jù)結(jié)構(gòu)圖2是本發(fā)明實(shí)施例2的匹配內(nèi)容基本數(shù)據(jù)結(jié)構(gòu)圖3是本發(fā)明三個(gè)關(guān)鍵字的樹形結(jié)構(gòu)示意圖。五具體實(shí)施例方式
下面結(jié)合附圖對(duì)本發(fā)明作進(jìn)一步詳細(xì)說明。
實(shí)施例1,首先說明本發(fā)明的算法原理
對(duì)于數(shù)據(jù)串“黑客”、“China”,其內(nèi)碼分別如下
對(duì)于上述數(shù)據(jù)串,構(gòu)造如下數(shù)據(jù)鏈表,鏈表中的每一個(gè)節(jié)點(diǎn)占用1KB內(nèi)存空間,可以理解為256個(gè)指針(在32位系統(tǒng)中,每個(gè)指針占用4個(gè)字節(jié)空間),其C語言描述如下
struct match_node
{
struct match_node*ch[256];
};
將match_node稱為匹配節(jié)點(diǎn)。
根據(jù)數(shù)據(jù)串的內(nèi)容,構(gòu)造如圖1鏈表結(jié)構(gòu),其中,所有的空指針最后都指向首節(jié)點(diǎn),匹配算法描述如下
head=首節(jié)點(diǎn)地址;content=輸入數(shù)據(jù)流首地址;while(content<輸入數(shù)據(jù)流末地址){if(next_head=head->ch[*content++]is匹配節(jié)點(diǎn)){找到一個(gè)串,做有關(guān)處理}<!-- SIPO <DP n="9"> --><dp n="d9"/>head=next_head;}
可見,不論要查找的數(shù)據(jù)串有多少,或者長(zhǎng)度多長(zhǎng),都可以使用上述匹配算法,該算法的執(zhí)行時(shí)間只與輸入數(shù)據(jù)流的長(zhǎng)度有關(guān)。
由于該算法很簡(jiǎn)單,可以很容易通過匯編指令和芯片予以實(shí)現(xiàn)。下面是一個(gè)通過X86匯編實(shí)現(xiàn)的例子(AT&T格式)。
Leal輸入數(shù)據(jù)流首地址,%esi ;初始設(shè)置Leal首節(jié)點(diǎn)地址,%ebxMovl輸入數(shù)據(jù)流長(zhǎng)度,%ecxMovl匹配節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)基地址,%edx;用來判斷指針是否指向匹配節(jié)點(diǎn)Xorl %eax,%eaxNext_match ;查找部分,LodsbMovl(%ebx,%eax,4),%ebxCmp %ebx,%edxJa string_foundLoop next_matchEnd_of_match ;結(jié)束查找.......String_found.......;做一些必要的處理Jmp next_match ;繼續(xù)下一次查找
通過實(shí)施例1可以看出,利用本發(fā)明的數(shù)據(jù)結(jié)構(gòu),只需要5條主要匯編指令就可以處理復(fù)雜的數(shù)據(jù)查找,并且與數(shù)據(jù)串的個(gè)數(shù)、長(zhǎng)度無關(guān)。
實(shí)施例2,對(duì)于關(guān)鍵字“aaabc”,“b78”,對(duì)應(yīng)的鏈表如圖2所示
圖2是一個(gè)完整的數(shù)據(jù)結(jié)構(gòu),可以處理各種關(guān)鍵字的組合。包括1)相同字符出現(xiàn)在不同的關(guān)鍵字中,例如,’b’同時(shí)出現(xiàn)在”aaabc”和”b78”中;2)同一字符串中的子串重復(fù),例如,”aaa”是多個(gè)’a’的重復(fù)。
此外,由于空間關(guān)系,圖中省略了一些連接,即所有節(jié)點(diǎn)中的空項(xiàng)都指向首節(jié)點(diǎn);
上述結(jié)構(gòu)可以處理各種不同的情況,但注意,這里只有2個(gè)關(guān)鍵字。當(dāng)關(guān)鍵字?jǐn)?shù)目n較大時(shí),鏈表會(huì)變的相當(dāng)復(fù)雜。因此,本算法把程序處理的復(fù)雜性轉(zhuǎn)換為了對(duì)空間(數(shù)據(jù)結(jié)構(gòu))處理的復(fù)雜性。
對(duì)于一個(gè)給定的關(guān)鍵字集合K,生成數(shù)形鏈表如下
樹形鏈表是基于有限自動(dòng)機(jī)DFA的,定義為
S(a finite set of states) 匹配過程所處的狀態(tài)即每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)節(jié)點(diǎn),對(duì)一個(gè)給定的關(guān)鍵字集P,其最大值是L,狀態(tài)記錄了掃描過程中對(duì)各個(gè)關(guān)鍵字pi的匹配狀況;
E(an alphabet)0-255,即表達(dá)一個(gè)字節(jié)的內(nèi)容;
s(initial state)空字符狀態(tài),即未讀入任何字符狀態(tài),空字符狀態(tài)s∈S;
F(finial state)最終狀態(tài)集,其中每一個(gè)元素表示當(dāng)匹配到某個(gè)關(guān)鍵字時(shí)所處的狀態(tài),F(xiàn)S;
g(transition function)S×E->S,讀入一個(gè)新字符時(shí),狀態(tài)機(jī)狀態(tài)轉(zhuǎn)移的函數(shù)為g(A,a)=B,表示讀入a使?fàn)顟B(tài)機(jī)從狀態(tài)A轉(zhuǎn)移到狀態(tài)B;
本樹形鏈表實(shí)現(xiàn)的關(guān)鍵是找到一個(gè)滿足定義的g的實(shí)現(xiàn),就是說,狀態(tài)的轉(zhuǎn)移應(yīng)該滿足對(duì)一個(gè)數(shù)據(jù),僅有“唯一”的一個(gè)下一狀態(tài)。
1)樹形鏈表的構(gòu)造
在DFA的構(gòu)造中g(shù)的設(shè)定
(1)同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移
設(shè)關(guān)鍵字為a1 a2 a3 a4... an;
每一個(gè)字符定義為DFA樹形鏈表的一個(gè)狀態(tài),表示對(duì)該關(guān)鍵字進(jìn)行匹配時(shí)所處的匹配位置,ai對(duì)應(yīng)的狀態(tài)稱為Ai;
則g的定義是
g(Ai,ai)=Ai+1
在樹形鏈表的DFA中,表現(xiàn)為連接一個(gè)關(guān)鍵字各個(gè)字符的鏈,成為樹的一個(gè)分支;
(2)當(dāng)有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移
假設(shè)
關(guān)鍵字A=a1a2a3...aiai+1ai+2ai+3...ajaj+1...an
關(guān)鍵字B=aiai+1...ajb1b2...bm
則稱關(guān)鍵字B前綴包含于A,即B的前綴aiai+1...aj包含在A中,記為PrefixIn(B,A)=true,或A->B,否則PrefixIn(B,A)=false;
PrefixIn是設(shè)定“交叉鏈接”的依據(jù);
定義PrefixInStr(B,A)=aiai+1...aj即包含的前綴部分;
首先,按同一關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移確定狀態(tài)和g,即先確定關(guān)鍵字內(nèi)的狀態(tài)轉(zhuǎn)移關(guān)系;如果A和B之間沒有PrefixIn關(guān)系,則g的構(gòu)造結(jié)束;
如果兩個(gè)關(guān)鍵字有PrefixIn關(guān)系,設(shè)PrefixIn(B,A)=true,且
關(guān)鍵字A=a1a2a3...aiai+1ai+2ai+3...ajaj+1...an;
關(guān)鍵字B=aiai+1...ajb1b2...bm
對(duì)應(yīng)于關(guān)鍵字A的各個(gè)狀態(tài)記為Aa1,Aa2,...Aan;
對(duì)應(yīng)于關(guān)鍵字A的各個(gè)狀態(tài)記為Ba1,Ba2,...Baj,Bb1,Bb2,...Bbm;
在匹配關(guān)鍵字A的同時(shí)也在檢查是否同時(shí)匹配B,在匹配到狀態(tài)Aaj的時(shí)候,如果讀入的下一個(gè)字符是b1,則下一狀態(tài)為Bb1,即應(yīng)添加
g(Aaj,b1)=Bb1
在樹形鏈表的DFA結(jié)構(gòu)中,表現(xiàn)為從A的鏈中指向B的鏈中的一個(gè)“交叉鏈接”;
對(duì)于有“一個(gè)關(guān)鍵字不能是另外一個(gè)關(guān)鍵字的子串”這種限制的情況下,兩個(gè)關(guān)鍵字之間的關(guān)系只有這兩種。
(3)三個(gè)以上關(guān)鍵字A,B,C的情況
如果任意兩個(gè)關(guān)鍵字之間沒有PrefixIn關(guān)系,按同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移處理,如果同時(shí)最多有兩個(gè)關(guān)鍵字之間有PrefixIn關(guān)系,即僅有小于等于兩個(gè)PrefixIn關(guān)系時(shí),則按兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移處理,同時(shí)有三個(gè)PrefixIn關(guān)系的情況,
設(shè)PrefixIn(B,A)=true,且Prefix(C,A)=true,那么有
g(Ax,bz)=Bz
g(Ay,cz)=Cz
情況1如果Ax≠Ay,則沒有沖突情況發(fā)生,按兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移處理;
情況2如果Ax=Ay,且bz≠cz則也不會(huì)有沖突情況發(fā)生,直接設(shè)置g(Ax,bz)=Bz,g(Ax,cz)=Cz即可;
情況3如果Ax=Ay,且bz=cz,則會(huì)有沖突情況發(fā)生,即
g(Ax,bz)=Bz
g(Ax,bz)=Cz
如果有沖突的情況發(fā)生,B,C之間必有PrefixIn關(guān)系,設(shè)PrefixIn(B,C)=true,則去掉g(Ax,bz)=Bz這個(gè)鏈接,保留g(Ax,bz)=Cz。
參見圖3,第一條豎線之前是三者重疊部分。第一條豎線之后,僅有B、C有重疊部分。兩條豎線之間是一個(gè)字符寬度。
從圖3中可以很清楚的看出B、C必有PrefixIn關(guān)系。
實(shí)際中交叉鏈接的情況如虛線所示。C在第一條豎線之后有某個(gè)字符(對(duì)應(yīng)的狀態(tài))有指向B的交叉鏈接。
如果從樹的角度來看,則在交叉鏈接發(fā)生沖突時(shí),選擇交叉鏈接的目的端時(shí),優(yōu)先選擇“深度”比較深的。本例選指向C的。
如果刪除關(guān)鍵字C,這時(shí)候需要?jiǎng)h除關(guān)鍵字C中第二條豎線之后的所有節(jié)點(diǎn)。同時(shí),要尋找替代指針,即關(guān)鍵字A中ax之后的節(jié)點(diǎn)要有指向B的交叉鏈接。根據(jù)下文所述的雙向鏈可以從C找到B,如果B存在的話。找到B之后,根據(jù)g的設(shè)定方法有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移重新設(shè)定A指向B的交叉鏈接。
適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表的生成算法主要通過兩個(gè)基本操作來實(shí)現(xiàn),即增加關(guān)鍵字的add_key操作和刪除一個(gè)關(guān)鍵字的del_key操作;
1)Add_key算法
節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)如下
struct match_node {struct match_node*ch[256]; //指向下一個(gè)狀態(tài)的256個(gè)指針<!-- SIPO <DP n="14"> --><dp n="d14"/>u_int32_t match_id; //當(dāng)前節(jié)點(diǎn)的一個(gè)標(biāo)志號(hào)u_int32_t depth; //當(dāng)前節(jié)點(diǎn)在樹中的深度struct match_node*parent;//指向當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)struct match_node*left_link,*right_link;//一個(gè)循環(huán)鏈表};
首先判斷待插入的關(guān)鍵字cur_key是不是已經(jīng)存在、或與現(xiàn)有的關(guān)鍵字沖突,如有,退出算法;否則,繼續(xù);
其次按g的同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移逐個(gè)當(dāng)前加入key各個(gè)字符對(duì)應(yīng)的節(jié)點(diǎn),如果該節(jié)點(diǎn)已經(jīng)在樹中,處理下一個(gè)節(jié)點(diǎn);否則,添加新的節(jié)點(diǎn);新添加的所有節(jié)點(diǎn)都要設(shè)置相應(yīng)的parent指針,同時(shí)對(duì)新增的節(jié)點(diǎn),把它掛到相應(yīng)的雙向鏈中;
如果root->ch[i]被修改,則初始化并設(shè)置lev1_nodes[i],調(diào)整所有冗余l(xiāng)ev1節(jié)點(diǎn)以保持和更新后的root節(jié)點(diǎn)一致;
按交叉鏈接設(shè)定階段1檢查新添加的cur_key是不是前綴包含于其他key[i],如果是,按g的設(shè)定方法有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移或三個(gè)以上關(guān)鍵字A,B,C的情況設(shè)定從key[i]指向cur_key的交叉鏈接;
最后按交叉鏈接設(shè)定階段2檢查其他key[i]是不是前綴包含于cur_key;如是,按g的設(shè)定方法有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移或三個(gè)以上關(guān)鍵字A,B,C的情況設(shè)定從cur_key指向key[i]的交叉鏈接;
2)Del_key算法
首先在樹中查找是否有待刪除的cur_key,同時(shí)設(shè)置branch_index;Branch_index是待刪除節(jié)點(diǎn)和樹中的現(xiàn)有節(jié)點(diǎn)分叉的地方,設(shè)a1a2a3...ai為cur_key和樹中現(xiàn)有的各個(gè)關(guān)鍵字中有最長(zhǎng)共享前綴,Branch_index指向ai;
其次如果沒有相應(yīng)key,退出算法;否則,繼續(xù);
刪除交叉鏈接;檢查當(dāng)前待刪除關(guān)鍵字cur_key是否前綴包含于其他的key[i],檢查的同時(shí),刪除key[i]中指向cur_key中branch_index之后的節(jié)點(diǎn)的指針,刪除時(shí)要注意尋找替代指針,如g的設(shè)定方法三個(gè)以上關(guān)鍵字A,B,C的情況中刪除關(guān)鍵字C;
安全刪除branch_index之后的節(jié)點(diǎn);
最后調(diào)整branch_index處指向被刪除節(jié)點(diǎn)的指針,該指針為空,應(yīng)該指向相應(yīng)的lev_nodes;
3)樹的結(jié)構(gòu)的保持
在每個(gè)節(jié)點(diǎn)中增加一個(gè)4byte的數(shù)據(jù)域parent,作為一個(gè)指針,指向節(jié)點(diǎn)的父節(jié)點(diǎn),這樣就可以區(qū)分指向子節(jié)點(diǎn)的指針和指向樹的其他分支的指針即交叉鏈接指針和null指針的調(diào)整指針;
4)null指針的處理
所有的null指針都指向root節(jié)點(diǎn)中的256個(gè)指針中的相應(yīng)一個(gè),整棵樹是循環(huán)匹配的,具體指向的定義是
如果Node->ch[i]==null(該指針是null指針)
那么Node->ch[i]=root->ch[i]
在樹做調(diào)整后,root->ch[i]可能會(huì)有變化,要求相關(guān)的null指針也要更新,為了避免搜索整棵樹調(diào)整相關(guān)的null指針,可以讓null指針固定的指向一些節(jié)點(diǎn),當(dāng)root->ch[i]改變的時(shí)候,通過改變相應(yīng)的節(jié)點(diǎn)來達(dá)到一致性;
這里采用的方法是給第二層256個(gè)節(jié)點(diǎn)分配固定位置的內(nèi)存,稱這256個(gè)節(jié)點(diǎn)為lev1_nodes,有
if root有第I個(gè)分支then
root->ch[i]=lev1_nodes[i]
else
root->ch[i]=root
每個(gè)null指針都是相應(yīng)的節(jié)點(diǎn)中256個(gè)指針中的一個(gè),設(shè)它在ch指針數(shù)組中的索引值為I,則令該null指針為lev1_nodes[i],如果root節(jié)點(diǎn)沒有第I個(gè)分支,為了保持一致性,讓lev1_nodes[i]復(fù)制當(dāng)前root節(jié)點(diǎn)的內(nèi)容,稱復(fù)制root節(jié)點(diǎn)內(nèi)容的lev1_nodes為冗余l(xiāng)ev1節(jié)點(diǎn)??梢?,這種方法保證了和前面的定義的完全一致,這是通過引入冗余節(jié)點(diǎn)實(shí)現(xiàn)的。稱復(fù)制root節(jié)點(diǎn)內(nèi)容的lev1_nodes為冗余l(xiāng)ev1節(jié)點(diǎn)。
當(dāng)root節(jié)點(diǎn)被修改時(shí),還要修改所有的冗余l(xiāng)ev1節(jié)點(diǎn),每一個(gè)冗余l(xiāng)ev1節(jié)點(diǎn)的內(nèi)容都應(yīng)該和更新后的root節(jié)點(diǎn)的內(nèi)容一致;
除root節(jié)點(diǎn)外,其他節(jié)點(diǎn)中指向lev1_nodes和root節(jié)點(diǎn)的指針都是null指針;
5)為了輔助前綴蘊(yùn)涵的搜索而引入的雙向鏈
當(dāng)一個(gè)新的key要被加入時(shí),要搜索它可能PrefixIn于其他key,為了加速搜索,避免訪問整棵樹,引入一個(gè)雙向鏈,雙向鏈“起始于”樹的256個(gè)lev1_nodes節(jié)點(diǎn),分別代表256個(gè)字符值,每一條鏈把所有節(jié)點(diǎn)值即字符值相同的節(jié)點(diǎn)連接起來,搜索一個(gè)key前綴包含于的所有其他key,同時(shí),這也消除了需要遞歸處理,加速樹的調(diào)整操作;
6)樹形鏈表的生成算法
對(duì)于集合P的每個(gè)元素,執(zhí)行add_key,就可以完成數(shù)據(jù)結(jié)構(gòu)的建立,如下
for(I=1;I<n;I++)
add_key(pi)。
利用本發(fā)明和其它主要的算法進(jìn)行了比較,其中GNU的fgrep 2.0采用了Commentz-Walter的算法,grep采用Hartel的算法,agrep采用了Sun Wu的算法。測(cè)試平臺(tái)在一臺(tái)PC機(jī)上進(jìn)行,機(jī)器的配置如下
CPUP4 1.7GHZ
內(nèi)存256MB
操作系統(tǒng)RedHat Linux 7.1
測(cè)試文件大小為100MB(108318720字節(jié))。
算法測(cè)試與比較
注算法測(cè)試指標(biāo)為CPU時(shí)間(秒)
測(cè)試結(jié)果表明,當(dāng)同時(shí)要處理的關(guān)鍵字比較多的時(shí)候(超過200個(gè)),本算法具有明顯的優(yōu)勢(shì),當(dāng)關(guān)鍵字個(gè)數(shù)超過1000個(gè)時(shí),本方法的性能優(yōu)勢(shì)更為明顯,關(guān)鍵字?jǐn)?shù)量越多,優(yōu)勢(shì)越明顯。因此,本算法更適合于大流量、多關(guān)鍵字處理的應(yīng)用環(huán)境。
權(quán)利要求
1、一種適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表,其特征在于樹形鏈表是基于有限自動(dòng)機(jī)DFA的,定義如下
S(a finite set of states)匹配過程所處的狀態(tài)即每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)節(jié)點(diǎn),對(duì)一個(gè)給定的關(guān)鍵字集P,其最大值是L,狀態(tài)記錄了掃描過程中對(duì)各個(gè)關(guān)鍵字pi的匹配狀況;
E(an alphabet)0-255,即表達(dá)一個(gè)字節(jié)的內(nèi)容;
s(initial state)空字符狀態(tài),即未讀入任何字符狀態(tài),空字符狀態(tài)s∈S;
F(finial state)最終狀態(tài)集,其中每一個(gè)元素表示當(dāng)匹配到某個(gè)關(guān)鍵字時(shí)所處的狀態(tài),F(xiàn)S;
g(transition function)S×E->S,讀入一個(gè)新字符時(shí),狀態(tài)機(jī)狀態(tài)轉(zhuǎn)移的函數(shù)為g(A,a)=B,表示讀入a使?fàn)顟B(tài)機(jī)從狀態(tài)A轉(zhuǎn)移到狀態(tài)B;
本樹形鏈表實(shí)現(xiàn)的關(guān)鍵是找到一個(gè)滿足定義的g的實(shí)現(xiàn);
樹形鏈表的構(gòu)造
在DFA的構(gòu)造中g(shù)的設(shè)定
(1)同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移
設(shè)關(guān)鍵字為a1 a2 a3 a4... an;
每一個(gè)字符定義為DFA樹形鏈表的一個(gè)狀態(tài),表示對(duì)該關(guān)鍵字進(jìn)行匹配時(shí)所處的匹配位置,ai對(duì)應(yīng)的狀態(tài)稱為Ai;
則g的定義是
g(Ai,ai)=Ai+1
在樹形鏈表的DFA中,表現(xiàn)為連接一個(gè)關(guān)鍵字各個(gè)字符的鏈,成為樹的一個(gè)分支;
(2)當(dāng)有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移
假設(shè)
關(guān)鍵字A=a1a2a3...aiai+1ai+2ai+3...ajaj+1...an
關(guān)鍵字B=aiai+1...ajb1b2...bm
則稱關(guān)鍵字B前綴包含于A,即B的前綴aiai+1...aj包含在A中,記為PrefixIn(B,A)=true,或A->B,否則PrefixIn(B,A)=false;
PrefixIn是設(shè)定“交叉鏈接”的依據(jù);
定義PrefixInStr(B,A)=aiai+1...aj即包含的前綴部分;
首先,按同一關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移確定狀態(tài)和g,即先確定關(guān)鍵字內(nèi)的狀態(tài)轉(zhuǎn)移關(guān)系;如果A和B之間沒有PrefixIn關(guān)系,則g的構(gòu)造結(jié)束;
如果兩個(gè)關(guān)鍵字有PrefixIn關(guān)系,設(shè)PrefixIn(B,A)=true,且
關(guān)鍵字A=a1a2a3...aiai+1ai+2ai+3...ajaj+1...an;
關(guān)鍵字B=aiai+1...ajb1b2...bm
對(duì)應(yīng)于關(guān)鍵字A的各個(gè)狀態(tài)記為Aa1,Aa2,...Aan;
對(duì)應(yīng)于關(guān)鍵字A的各個(gè)狀態(tài)記為Ba1,Ba2,...Baj,Bb1,Bb2,...Bbm;
在匹配關(guān)鍵字A的同時(shí)也在檢查是否同時(shí)匹配B,在匹配到狀態(tài)Aaj的時(shí)候,如果讀入的下一個(gè)字符是b1,則下一狀態(tài)為Bb1,即應(yīng)添加
g(Aaj,b1)=Bb1
在樹形鏈表的DFA結(jié)構(gòu)中,表現(xiàn)為從A的鏈中指向B的鏈中的一個(gè)“交叉鏈接”;
(3)三個(gè)以上關(guān)鍵字A,B,C的情況
如果任意兩個(gè)關(guān)鍵字之間沒有PrefixIn關(guān)系,按同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移處理,如果同時(shí)最多有兩個(gè)關(guān)鍵字之間有PrefixIn關(guān)系,即僅有小于等于兩個(gè)PrefixIn關(guān)系時(shí),則按兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移處理,同時(shí)有三個(gè)PrefixIn關(guān)系的情況,
設(shè)PrefixIn(B,A)=true,且Prefix(C,A)=true,那么有
g(Ax,bz)=Bz
g(Ay,cz)=Cz
情況1如果Ax≠Ay,則沒有沖突情況發(fā)生,按兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移處理;
情況2如果Ax=Ay,且bz≠cz則也不會(huì)有沖突情況發(fā)生,直接設(shè)置g(Ax,bz)=Bz,g(Ax,cz)=Cz即可;
情況3如果Ax=Ay,且bz=cz,則會(huì)有沖突情況發(fā)生,即
g(Ax,bz)=Bz
g(Ax,bz)=Cz
如果有沖突的情況發(fā)生,B,C之間必有PrefixIn關(guān)系,設(shè)PrefixIn(B,C)=true,則去掉g(Ax,bz)=Bz這個(gè)鏈接,保留g(Ax,bz)=Cz。
2、根據(jù)權(quán)利要求1所述的一種適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表的生成算法,其特征在于主要通過兩個(gè)基本操作來實(shí)現(xiàn),即增加關(guān)鍵字的add_key操作和刪除一個(gè)關(guān)鍵字的del_key操作;
1)Add_key算法
節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)如下
struct match_node {struct match_node*ch[256];//向下一個(gè)狀態(tài)的256個(gè)指針u_int32_t match_id; //當(dāng)前節(jié)點(diǎn)的一個(gè)標(biāo)志號(hào)<dp n="c3"/>u_int32_t depth; //當(dāng)前節(jié)點(diǎn)在樹中的深度struct match_node*parent;//指向當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)struct match_node*left_link,*right_link;//一個(gè)循環(huán)鏈表};
首先判斷待插入的關(guān)鍵字cur_key是不是已經(jīng)存在、或與現(xiàn)有的關(guān)鍵字沖突,如有,退出算法;否則,繼續(xù);
其次按g的同一個(gè)關(guān)鍵字之內(nèi)的狀態(tài)轉(zhuǎn)移逐個(gè)當(dāng)前加入key各個(gè)字符對(duì)應(yīng)的節(jié)點(diǎn),如果該節(jié)點(diǎn)已經(jīng)在樹中,處理下一個(gè)節(jié)點(diǎn);否則,添加新的節(jié)點(diǎn);新添加的所有節(jié)點(diǎn)都要設(shè)置相應(yīng)的parent指針,同時(shí)對(duì)新增的節(jié)點(diǎn),把它掛到相應(yīng)的雙向鏈中;
如果root->ch[i]被修改,則初始化并設(shè)置lev1_nodes[i],調(diào)整所有冗余l(xiāng)ev1節(jié)點(diǎn)以保持和更新后的root節(jié)點(diǎn)一致;
按交叉鏈接設(shè)定階段1檢查新添加的cur_key是不是前綴包含于其他key[i],如果是,按g的設(shè)定方法有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移或三個(gè)以上關(guān)鍵字A,B,C的情況設(shè)定從key[i]指向cur_key的交叉鏈接;
最后按交叉鏈接設(shè)定階段2檢查其他key[i]是不是前綴包含于cur_key;如是,按g的設(shè)定方法有兩個(gè)關(guān)鍵字A和B時(shí)的狀態(tài)轉(zhuǎn)移或三個(gè)以上關(guān)鍵字A,B,C的情況設(shè)定從cur_key指向key[i]的交叉鏈接;
2)Del_key算法
首先在樹中查找是否有待刪除的cur_key,同時(shí)設(shè)置branch_index;Branch_index是待刪除節(jié)點(diǎn)和樹中的現(xiàn)有節(jié)點(diǎn)分叉的地方,設(shè)a1a2a3...ai為cur_key和樹中現(xiàn)有的各個(gè)關(guān)鍵字中有最長(zhǎng)共享前綴,Branch_index指向ai;
其次如果沒有相應(yīng)key,退出算法;否則,繼續(xù);
刪除交叉鏈接;檢查當(dāng)前待刪除關(guān)鍵字cur_key是否前綴包含于其他的key[i],檢查的同時(shí),刪除key[i]中指向cur_key中branch_index之后的節(jié)點(diǎn)的指針,刪除時(shí)要注意尋找替代指針,如g的設(shè)定方法三個(gè)以上關(guān)鍵字A,B,C的情況中刪除關(guān)鍵字C;
安全刪除branch_index之后的節(jié)點(diǎn);
最后調(diào)整branch_index處指向被刪除節(jié)點(diǎn)的指針,該指針為空,應(yīng)該指向相應(yīng)的lev_nodes;
3)樹的結(jié)構(gòu)的保持
在每個(gè)節(jié)點(diǎn)中增加一個(gè)4byte的數(shù)據(jù)域parent,作為一個(gè)指針,指向節(jié)點(diǎn)的父節(jié)點(diǎn),這樣就可以區(qū)分指向子節(jié)點(diǎn)的指針和指向樹的其他分支的指針即交叉鏈接指針和null指針的調(diào)整指針;
4)null指針的處理
所有的null指針都指向root節(jié)點(diǎn)中的256個(gè)指針中的相應(yīng)一個(gè),整棵樹是循環(huán)匹配的,具體指向的定義是
如果Node->ch[i]==null(該指針是null指針)
那么Node->ch[i]=root->ch[i]
在樹做調(diào)整后,root->ch[i]可能會(huì)有變化,要求相關(guān)的null指針也要更新,為了避免搜索整棵樹調(diào)整相關(guān)的null指針,可以讓null指針固定的指向一些節(jié)點(diǎn),當(dāng)root->ch[i]改變的時(shí)候,通過改變相應(yīng)的節(jié)點(diǎn)來達(dá)到一致性;
給樹的第二層256個(gè)節(jié)點(diǎn)分配固定位置的內(nèi)存,稱這256個(gè)節(jié)點(diǎn)為lev1_nodes,有
if root有第I個(gè)分支then
root->ch[i]=lev1_nodes[i]
else
root->ch[i]=root
每個(gè)null指針都是相應(yīng)的節(jié)點(diǎn)中256個(gè)指針中的一個(gè),設(shè)它在ch指針數(shù)組中的索引值為I,則令該null指針為lev1_nodes[i],如果root節(jié)點(diǎn)沒有第I個(gè)分支,為了保持一致性,讓lev1_nodes[i]復(fù)制當(dāng)前root節(jié)點(diǎn)的內(nèi)容,稱復(fù)制root節(jié)點(diǎn)內(nèi)容的lev1_nodes為冗余l(xiāng)ev1節(jié)點(diǎn);
當(dāng)root節(jié)點(diǎn)被修改時(shí),還要修改所有的冗余l(xiāng)ev1節(jié)點(diǎn),每一個(gè)冗余l(xiāng)ev1節(jié)點(diǎn)的內(nèi)容都應(yīng)該和更新后的root節(jié)點(diǎn)的內(nèi)容一致;
除root節(jié)點(diǎn)外,其他節(jié)點(diǎn)中指向lev1_nodes和root節(jié)點(diǎn)的指針都是null指針;
5)為了輔助前綴蘊(yùn)涵的搜索而引入的雙向鏈
當(dāng)一個(gè)新的key要被加入時(shí),要搜索它可能PrefixIn于其他key,為了加速搜索,避免訪問整棵樹,引入一個(gè)雙向鏈,雙向鏈“起始于”樹的256個(gè)lev1_nodes節(jié)點(diǎn),分別代表256個(gè)字符值,每一條鏈把所有節(jié)點(diǎn)值即字符值相同的節(jié)點(diǎn)連接起來,搜索一個(gè)key前綴包含于的所有其他key,同時(shí),這也消除了需要遞歸處理,加速樹的調(diào)整操作;
6)樹形鏈表的生成算法
對(duì)于集合P的每個(gè)元素,執(zhí)行add_key,就可以完成數(shù)據(jù)結(jié)構(gòu)的建立,如下
for(I=1;I<n;I++)
add_key(pi)。
全文摘要
本發(fā)明公開了一種適應(yīng)于快速數(shù)據(jù)查找的樹形鏈表及其生成算法,主要通過兩個(gè)基本操作來實(shí)現(xiàn),即增加關(guān)鍵字的add_key操作和刪除一個(gè)關(guān)鍵字的del_key操作;反復(fù)進(jìn)行這兩個(gè)操作可以生成整棵樹形鏈表,由于隨著半導(dǎo)體技術(shù)的進(jìn)步,內(nèi)存等硬件的價(jià)格大幅度下降,本發(fā)明通過計(jì)算機(jī)算法中的“空間換時(shí)間”原則,對(duì)關(guān)鍵字集合K,構(gòu)造專用數(shù)據(jù)鏈表,從而使得查找速度大大加快,使算法的性能只與N有關(guān),與N是線性關(guān)系,而與k和SIZE(pi)無關(guān);匹配算法簡(jiǎn)單,只需要幾條匯編指令就可以完成。
文檔編號(hào)G06F7/06GK1387119SQ02114650
公開日2002年12月25日 申請(qǐng)日期2002年6月28日 優(yōu)先權(quán)日2002年6月28日
發(fā)明者李衛(wèi), 管曉宏 申請(qǐng)人:西安交通大學(xué)