Trie樹的實現
一、定義:
Trie,又稱字典樹,是一種用於快速檢索的二十六叉樹結構。典型的空間換時間
二、結構圖:
三、原理:
Trie把要查找的關鍵詞看作一個字符序列,並根據構成關鍵詞字符的先後順序檢索樹結構;
特別地:和二叉查找樹不同,在Trie樹中,每個結點上並非存儲一個元素。
四、性質:
0、利用串的公共前綴,節約內存
1、在trie樹上進行檢索總是始於根結點
2、根節點不包含字符,除根節點外的每一個節點都只代表一個字母,這裏不是包含。
3、從根節點到某一節點,路徑上經過的字符連接起來,爲該節點對應的字符串。
4、每個節點的所有子節點包含的字符都不相同。
五、效率分析
0、當存儲大量字符串時,Trie耗費的空間較少。因爲鍵值並非顯式存儲的,而是與其他鍵值共享子串。
1、查詢快。在trie樹中查找一個關鍵字的時間和樹中包含的結點數無關,而取決於組成關鍵字的字符數。對於長度爲m的鍵值,最壞情況下只需花費O(m)的時間(對比:二叉查找樹的查找時間和樹中的結點數有關O(log2n)。)
2、如果要查找的關鍵字可以分解成字符序列且不是很長,利用trie樹查找速度優於二叉查找樹。
3、若關鍵字長度最大是5,則利用trie樹,利用5次比較可以從265=11881376個可能的關鍵字中檢索出指定的關鍵字。而利用二叉查找樹至少要進行log2265=23.5次比較。
六、應用:用於字符串的統計與排序,經常被搜索引擎系統用於文本詞頻統計。
1、字典樹在串的快速檢索中的應用。
給出N個單詞組成的熟詞表,以及一篇全用小寫英文書寫的文章,請你按最早出現的順序寫出所有不在熟詞表中的生詞。在這道題中,我們可以用字典樹,先把熟詞建一棵樹,然後讀入文章進行比較,這種方法效率是比較高的。
2、字典樹在“串”排序方面的應用
給定N個互不相同的僅由一個單詞構成的英文名,讓你將他們按字典序從小到大輸出用字典樹進行排序,採用數組的方式創建字典樹,這棵樹的每個結點的所有兒子很顯然地按照其字母大小排序。對這棵樹進行先序遍歷即可。
3.、字典樹在最長公共前綴問題的應用
對所有串建立字典樹,對於兩個串的最長公共前綴的長度即他們所在的結點的公共祖先個數,於是,問題就轉化爲最近公共祖先問題。
代碼
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <assert.h>
using namespace std;
const int MaxBranchNum = 26;//如果區分大小寫,可以擴展到52
/*定義trie樹結點*/
class TrieNode
{
public:
char* word; //節點表示的單詞
int count; //單詞出現的次數
TrieNode* nextBranch[MaxBranchNum];//指向26個字符節點的指針
public:
TrieNode() : word(NULL),count(0)
{
memset(nextBranch,NULL,sizeof(TrieNode*) * MaxBranchNum);
}
};
/*定義類Trie*/
class Trie
{
public:
Trie();
~Trie();
void Insert(const char* str);//插入字符串str
bool Search(const char* str,int& count);//查找字符串str,並返回出現的次數
bool Remove(const char* str);//刪除字符串str
void PrintALL();//打印trie樹中所有的結點
void PrintPre(const char* str);//打印以str爲前綴的單詞
private:
TrieNode* pRoot;
private:
void Destory(TrieNode* pRoot);
void Print(TrieNode* pRoot);
};
Trie::Trie()
{
pRoot = new TrieNode();//注意字典樹的根不存放字符
}
Trie::~Trie()
{
Destory(pRoot);
}
/*插入一個單詞*/
void Trie::Insert(const char* str)
{
assert(NULL != str);
int index;
TrieNode* pLoc = pRoot;
for (int i = 0;str[i];i++)
{
index = str[i] - 'a';//如果區分大小寫,可以擴展
if(index < 0 || index >= MaxBranchNum)//不執行插入
{
return;
}
if (NULL == pLoc->nextBranch[index])//該單詞的前綴不存在,要生成該結點
{
pLoc->nextBranch[index] = new TrieNode();//先new着,裏面的word指向null,count爲0
}
pLoc = pLoc->nextBranch[index];
}
if (NULL != pLoc->word)//單詞已經出現過
{
pLoc->count++;
return;
}
else //單詞沒有出現過,應該插入單詞
{
pLoc->count++;
pLoc->word = new char[strlen(str) + 1];
assert(NULL != pLoc->word);
strcpy(pLoc->word,str);
}
}
/*查找一個單詞,如果存在該單詞,則返回其出現次數*/
bool Trie::Search(const char* str,int& count)
{
assert(str != NULL);
int i = 0;
int index = -1;;
TrieNode* pLoc = pRoot;
while(pLoc && *str)
{
index = *str - 'a';//如果區分大小寫,可以擴展
if(index < 0 || index >= MaxBranchNum)//不是一個單詞,不執行插入
{
return false;
}
pLoc = pLoc->nextBranch[index];
str++;
}
if (pLoc && pLoc->word)//條件成立,找到該單詞
{
count = pLoc->count;
return true;
}
return false;
}
bool Trie::Remove(const char* str)
{
assert(NULL != str);
int index = -1;;
TrieNode* pLoc = pRoot;
while(pLoc && *str)
{
index = *str - 'a';//如果區分大小寫,可以擴展
if(index < 0 || index >= MaxBranchNum)//不是一個單詞,不執行插入
{
return false;
}
pLoc = pLoc->nextBranch[index];
str++;
}
if (pLoc && pLoc-> word)//條件成立,找到該單詞
{
delete[] pLoc->word;
pLoc->word = NULL;
pLoc->count = 0;
return true;
}
return false;
}
void Trie::PrintALL()
{
Print(pRoot);
}
/*按照字典順序輸出以pRoot爲根的所有的單詞*/
void Trie::Print(TrieNode* pRoot)
{
if (NULL == pRoot)
{
return;
}
//輸出單詞
if (NULL != pRoot->word)
{
cout<<pRoot->word<<" "<<pRoot->count<<endl;
}
//遞歸處理分支
for (int i = 0;i < MaxBranchNum;i++)
{
Print(pRoot->nextBranch[i]);
}
}
void Trie::PrintPre(const char* str)
{
assert(str != NULL);
int i = 0;
int index = -1;;
TrieNode* pLoc = pRoot;
while(pLoc && *str)
{
index = *str - 'a';//如果區分大小寫,可以擴展
if(index < 0 || index > MaxBranchNum)//不是一個單詞,不執行插入
{
return;
}
pLoc = pLoc->nextBranch[index];
str++;
}
if (pLoc)//條件成立,找到該單詞
{
Print(pLoc);
}
}
/*銷燬trie樹*/
void Trie::Destory(TrieNode* pRoot)
{
if (NULL == pRoot)
{
return;
}
for (int i = 0;i < MaxBranchNum;i++)
{
Destory(pRoot->nextBranch[i]);
}
//銷燬單詞佔的空間
if (NULL != pRoot->word)
{
delete []pRoot->word;
pRoot->word = NULL;
}
delete pRoot;//銷燬結點
pRoot = NULL;
}
int main(int arg, char **argv)
{
Trie t;
string str;
int count = -1;
ifstream in("word.txt");
//把單詞輸入字典樹
while(in >> str)
{
transform(str.begin(),str.end(),str.begin(),tolower);//大寫變小寫
t.Insert(str.c_str());
}
//查找
bool isFind = t.Search("the",count);
if (isFind)
{
cout<<"存在the,出現次數:"<<count<<endl;
}
else
{
cout<<"不存在the!"<<endl;
}
//輸出
t.PrintALL();
//刪除
bool isDel = t.Remove("the");
if (isDel)
{
cout<<"刪除成功!"<<endl;
}
else
{
cout<<"刪除失敗!"<<endl;
}
t.Insert("the");
isFind = t.Search("the",count);
if (isFind)
{
cout<<"存在the,出現次數:"<<count<<endl;
}
else
{
cout<<"不存在the!"<<endl;
}
//輸出以w開頭的單詞
t.PrintPre("w");
cout<<endl;
system("pause");
}