1. 程式人生 > >字典樹簡介、應用以及與雜湊表的比較

字典樹簡介、應用以及與雜湊表的比較

題目要求:

1、設計並實現N-array trie,包括初始化,查詢,插入,刪除等。
2、應用trie結構實現文字文件的索引化,首先掃描文字文件,然後利用trie結構記錄單詞行號,最後在trie上實現查詢
3、使用者的查詢可以是針對一個單詞,也可以是某些字母開頭的。

我的思路:

根據題目的要求,用字典樹這一資料結構實現最為符合,下面介紹一下字典樹:

字典樹介紹:

我們拿儲存英文單詞的字典樹為例,從實質上來講,該字典樹就是一個26叉樹。(若是儲存數字如電話號碼,那麼就是一個10叉樹)。
比如說我想儲存he,her,boy這三個單詞,那麼其結構如下圖所示:

這裡寫圖片描述

總結其基本性質如下:
根節點不包含字元,除根節點意外每個節點至多包含一個單詞。
從根節點到某一個節點,路徑上經過的字元連線起來,為該節點對應的字串。

程式碼實現:

TrieNode類(節點類):string rownum(儲存行號),26個指向TrieNode的指標(*a,*b,……),建構函式等;

Trie類(樹):
root(根節點)
storeWord()(插入單詞的方法):從根節點向子節點遍歷(若子節點為空,則新建子節點,直到到達對應位置)
queryWord(查詢單詞行號):從根節點向子節點遍歷到達對應位置返回行號

其中題目中提到的查詢以某些字母開頭的單詞,那麼只需遍歷以該節點為根的子樹即可(比如層次遍歷)。如下,想查詢以he開頭的單詞,遍歷藍色圈起來的子樹。
這裡寫圖片描述

對字典樹的分析:

優點:

1、插入,查詢,刪除等操作複雜度為O(h),其中h為單詞的長度。為什麼會這麼快呢,本質是空間換時間(空間複雜度為26的h次方),利用指標來避免做其他不必要的查詢。(初始化的時間複雜度為n O(h),n為單詞個數)
2、當儲存大量單詞或者說儲存的單詞有著共同字首時節省了空間。(比如說用線性儲存boy,boyfriend如用trie儲存的差別)

缺點:

指標佔用的空間,空間複雜度大。如果儲存少量的單詞,並不能節省空間。

字典樹的應用:

1、  字串檢索:
事先將已知的一些字串(字典)的有關資訊儲存到trie樹裡,查詢另外一些未知字串是否出現過或者出現頻率。(本題既是如此)
2、  字串最長公共字首(轉化為尋找共同祖先問題)

與雜湊的比較

這裡寫圖片描述

實際效果:

我儲存26400行的單詞,其中查詢很快,達到了預期,但初始化過程表現很差,用時約3分鐘。
這裡寫圖片描述

我的改進:

在每個節點直接申請所有的指標,來獲得連續的空間,防止記憶體過於碎片化,但效果並不好。

下面是程式碼版本一


#include <
stdio.h> #include<iostream> #include<string.h> using namespace std; #include <stdio.h> #include<string.h> #include <iostream> #include <cstdlib> #include <cstdio> #include <stack> #include <string> #include <fstream> #include <sstream> #include <iostream> #include <vector> using namespace std; class BinaryTreeNode//二叉樹的節點類 { public: BinaryTreeNode() { a = b= c = d = e= f =g=h=i=j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=y=z=0; } BinaryTreeNode(string num) { rownum = num; a = b= c = d = e= f =g=h=i=j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=y=z=0; } //BinaryTreeNode(char c, BinaryTreeNode* left, BinaryTreeNode* right) { data = c; leftChild = left; rightChild = right; } string rownum ; BinaryTreeNode* a;BinaryTreeNode* b;BinaryTreeNode* c;BinaryTreeNode* d;BinaryTreeNode* e;BinaryTreeNode* f; BinaryTreeNode* g;BinaryTreeNode* h;BinaryTreeNode* i;BinaryTreeNode* j;BinaryTreeNode* k;BinaryTreeNode* l; BinaryTreeNode* m;BinaryTreeNode* n;BinaryTreeNode* o;BinaryTreeNode* p;BinaryTreeNode* q;BinaryTreeNode* r; BinaryTreeNode* s;BinaryTreeNode* t;BinaryTreeNode* u;BinaryTreeNode* v;BinaryTreeNode* w;BinaryTreeNode* x; BinaryTreeNode* y;BinaryTreeNode* z; }; class Node //佇列類中用連結串列存 { public: BinaryTreeNode *data; Node *next; Node() { next = NULL; } Node(BinaryTreeNode *item, Node* link = NULL) { data = item; next = link; } }; //佇列類,作為層次遍歷的輔助資料結構用 class LinkQueue { private: Node *front, *rear; public: LinkQueue() { rear = front = new Node; }; bool empty() { return front == rear; } void outQueue(BinaryTreeNode * &e)//出佇列 { Node *tmpPtr = front->next; e = tmpPtr->data; front->next = tmpPtr->next; if (rear == tmpPtr) rear = front; delete tmpPtr; } void inQueue(BinaryTreeNode * &e)//入佇列 { Node *tmpPtr = new Node(e); rear->next = tmpPtr; rear = tmpPtr; } }; //二叉樹類 class BinaryTree { public: BinaryTree() { root = 0; } void insertaChild(BinaryTreeNode* t, string rownum)//插入A孩子 { t->a = new BinaryTreeNode(rownum); } void insertbChild(BinaryTreeNode* t, string rownum)//插入B孩子 { t->b = new BinaryTreeNode(rownum); } void insertcChild(BinaryTreeNode* t, string rownum)//插入C孩子 { t->c = new BinaryTreeNode(rownum); } void insertdChild(BinaryTreeNode* t, string rownum)//插入D孩子 { t->d = new BinaryTreeNode(rownum); } void inserteChild(BinaryTreeNode* t, string rownum)//插入E孩子 { t->e = new BinaryTreeNode(rownum); } void insertfChild(BinaryTreeNode* t, string rownum)//插入F孩子 { t->f = new BinaryTreeNode(rownum); } void insertgChild(BinaryTreeNode* t, string rownum)//插入G孩子 { t->g = new BinaryTreeNode(rownum); } void inserthChild(BinaryTreeNode* t, string rownum)//插入H孩子 { t->h = new BinaryTreeNode(rownum); } void insertiChild(BinaryTreeNode* t, string rownum)//插入I孩子 { t->i = new BinaryTreeNode(rownum); } void insertjChild(BinaryTreeNode* t, string rownum)//插入J孩子 { t->j = new BinaryTreeNode(rownum); } void insertkChild(BinaryTreeNode* t, string rownum)//插入K孩子 { t->k = new BinaryTreeNode(rownum); } void insertlChild(BinaryTreeNode* t, string rownum)//插入L孩子 { t->l = new BinaryTreeNode(rownum); } void insertmChild(BinaryTreeNode* t, string rownum)//插入M孩子 { t->m = new BinaryTreeNode(rownum); } void insertnChild(BinaryTreeNode* t, string rownum)//插入N孩子 { t->n = new BinaryTreeNode(rownum); } void insertoChild(BinaryTreeNode* t, string rownum)//插入O孩子 { t->o = new BinaryTreeNode(rownum); } void insertpChild(BinaryTreeNode* t, string rownum)//插入P孩子 { t->p = new BinaryTreeNode(rownum); } void insertqChild(BinaryTreeNode* t, string rownum)//插入Q孩子 { t->q = new BinaryTreeNode(rownum); } void insertrChild(BinaryTreeNode* t, string rownum)//插入R孩子 { t->r = new BinaryTreeNode(rownum); } void insertsChild(BinaryTreeNode* t, string rownum)//插入S孩子 { t->s = new BinaryTreeNode(rownum); } void inserttChild(BinaryTreeNode* t, string rownum)//插入T孩子 { t->t = new BinaryTreeNode(rownum); } void insertuChild(BinaryTreeNode* t, string rownum)//插入U孩子 { t->u = new BinaryTreeNode(rownum); } void insertvChild(BinaryTreeNode* t, string rownum)//插入V孩子 { t->v = new BinaryTreeNode(rownum); } void insertwChild(BinaryTreeNode* t, string rownum)//插入W孩子 { t->w = new BinaryTreeNode(rownum); } void insertxChild(BinaryTreeNode* t, string rownum)//插入X孩子 { t->x = new BinaryTreeNode(rownum); } void insertyChild(BinaryTreeNode* t, string rownum)//插入Y孩子 { t->y = new BinaryTreeNode(rownum); } void insertzChild(BinaryTreeNode* t, string rownum)//插入Z孩子 { t->z = new BinaryTreeNode(rownum); } //下面是接收一個單詞儲存到樹中的方法 void storeWord(string word ,string rownum) { int length=word.length();//獲取當前單詞的長度 BinaryTreeNode* current = this->root;//current用來遍歷樹 //下面將word的字母一個個取出來判斷該單詞在樹中的位置 for(int i=0;i<length;i++) { string first; first=word.substr(i,1);//依次獲取字母 cout << "當前的字母是:"+first<< endl; if(i<length-1)//還沒到最終的位置 { if(first.compare("a")==0) { if(current->a==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertaChild(current,"0"); current=current->a; } else//當前節點有該兒子 { current=current->a; } } else if(first.compare("b")==0) { if(current->b==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertbChild(current,"0"); current=current->b; } else//當前節點有該兒子 { current=current->b; } } else if(first.compare("c")==0) { if(current->c==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertcChild(current,"0"); current=current->c; } else//當前節點有該兒子 { current=current->c; } } else if(first.compare("d")==0) { if(current->d==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertdChild(current,"0"); current=current->d; } else//當前節點有該兒子 { current=current->d; } } else if(first.compare("e")==0) { if(current->e==NULL)//若當前節點沒有兒子則插入新兒子 { this->inserteChild(current,"0"); current=current->e; } else//當前節點有該兒子 { current=current->e; } } else if(first.compare("f")==0) { if(current->f==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertfChild(current,"0"); current=current->f; } else//當前節點有該兒子 { current=current->f; } } else if(first.compare("g")==0) { if(current->g==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertgChild(current,"0"); current=current->g; } else//當前節點有該兒子 { current=current->g; } } else if(first.compare("h")==0) { if(current->h==NULL)//若當前節點沒有兒子則插入新兒子 { this->inserthChild(current,"0"); current=current->h; } else//當前節點有該兒子 { current=current->h; } } else if(first.compare("i")==0) { if(current->i==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertiChild(current,"0"); current=current->i; } else//當前節點有該兒子 { current=current->i; } } else if(first.compare("j")==0) { if(current->j==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertjChild(current,"0"); current=current->j; } else//當前節點有該兒子 { current=current->j; } } else if(first.compare("k")==0) { if(current->k==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertkChild(current,"0"); current=current->k; } else//當前節點有該兒子 { current=current->k; } } else if(first.compare("l")==0) { if(current->l==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertlChild(current,"0"); current=current->l; } else//當前節點有該兒子 { current=current->l; } } else if(first.compare("m")==0) { if(current->m==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertmChild(current,"0"); current=current->m; } else//當前節點有該兒子 { current=current->m; } } else if(first.compare("n")==0) { if(current->n==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertnChild(current,"0"); current=current->n; } else//當前節點有該兒子 { current=current->n; } } else if(first.compare("o")==0) { if(current->o==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertoChild(current,"0"); current=current->o; } else//當前節點有該兒子 { current=current->o; } } else if(first.compare("p")==0) { if(current->p==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertpChild(current,"0"); current=current->p; } else//當前節點有該兒子 { current=current->p; } } else if(first.compare("q")==0) { if(current->q==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertqChild(current,"0"); current=current->q; } else//當前節點有該兒子 { current=current->q; } } else if(first.compare("r")==0) { if(current->r==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertrChild(current,"0"); current=current->r; } else//當前節點有該兒子 { current=current->r; } } else if(first.compare("s")==0) { if(current->s==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertsChild(current,"0"); current=current->s; } else//當前節點有該兒子 { current=current->s; } } else if(first.compare("t")==0) { if(current->t==NULL)//若當前節點沒有兒子則插入新兒子 { this->inserttChild(current,"0"); current=current->t; } else//當前節點有該兒子 { current=current->t; } } else if(first.compare("u")==0) { if(current->u==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertuChild(current,"0"); current=current->u; } else//當前節點有該兒子 { current=current->u; } } else if(first.compare("v")==0) { if(current->v==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertvChild(current,"0"); current=current->v; } else//當前節點有該兒子 { current=current->v; } } else if(first.compare("w")==0) { if(current->w==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertwChild(current,"0"); current=current->w; } else//當前節點有該兒子 { current=current->w; } } else if(first.compare("x")==0) { if(current->x==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertxChild(current,"0"); current=current->x; } else//當前節點有該兒子 { current=current->x; } } else if(first.compare("y")==0) { if(current->y==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertyChild(current,"0"); current=current->y; } else//當前節點有該兒子 { current=current->y; } } else if(first.compare("z")==0) { if(current->z==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertzChild(current,"0"); current=current->z; } else//當前節點有該兒子 { current=current->z; } } } else//到了最終的位置 { if(first.compare("a")==0) { if(current->a==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertaChild(current,word+":"+rownum); current=current->a; } else { this->insertaChild(current,word+":"+rownum); } } else if(first.compare("b")==0) { if(current->b==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertbChild(current,word+":"+rownum); current=current->b; } else { this->insertbChild(current,word+":"+rownum); } } else if(first.compare("c")==0) { if(current->c==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertcChild(current,word+":"+rownum); current=current->c; } else { this->insertcChild(current,word+":"+rownum); } } else if(first.compare("d")==0) { if(current->d==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertdChild(current,word+":"+rownum); current=current->d; } else { this->insertdChild(current,word+":"+rownum); } } else if(first.compare("e")==0) { if(current->e==NULL)//若當前節點沒有兒子則插入新兒子 { this->inserteChild(current,word+":"+rownum); current=current->e; } else { this->inserteChild(current,word+":"+rownum); } } else if(first.compare("f")==0) { if(current->f==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertfChild(current,word+":"+rownum); current=current->f; } else { this->insertfChild(current,word+":"+rownum); } } else if(first.compare("g")==0) { if(current->g==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertgChild(current,word+":"+rownum); current=current->g; } else { this->insertgChild(current,word+":"+rownum); } } else if(first.compare("h")==0) { if(current->h==NULL)//若當前節點沒有兒子則插入新兒子 { this->inserthChild(current,word+":"+rownum); current=current->h; } else { this->inserthChild(current,word+":"+rownum); } } else if(first.compare("i")==0) { if(current->i==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertiChild(current,word+":"+rownum); current=current->i; } else { this->insertiChild(current,word+":"+rownum); } } else if(first.compare("j")==0) { if(current->j==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertjChild(current,word+":"+rownum); current=current->j; } else { this->insertjChild(current,word+":"+rownum); } } else if(first.compare("k")==0) { if(current->k==NULL)//若當前節點沒有兒子則插入新兒子 { this->insertkChild(current,word+":"+rownum); current=