1. 程式人生 > >C++實現Trie字典樹

C++實現Trie字典樹

一、前言

這篇文章來源於HihoCode的#1014題,此處為連結

二、Trie字典樹

字典樹,顧名思義就是將單詞存在一個樹結構中,用來對單詞進行儲存和查詢功能,並且可以統計出以某一特定字串為字首共有多少單詞,本文中只涉及英文單詞。

字典樹是一個26叉樹,每一個父節點都有26個子節點,分別對應了26個英文字母,樹的結構大致如下:

理所當然的根節點不代表任何字母,作為一個空節點。那麼每個節點應當具有什麼樣的結構呢?

三、節點結構

節點中應當包含一個指向26個子節點的指標陣列,為了統計以某一字串為字首的單詞出現了多少次,節點中應當有此節點儲存了多少次的計數值,並且應當儲存下當前節點代表的字元。

class TrieNode{
    public:

    int count;
    TrieNode * nextNode[26];
    char *word;
    public:
    TrieNode():word(NULL),count(0){
        for(int i=0;i<26;i++){
            nextNode[i]=NULL;
        }
    }

};
四、針對題目的分析

題目中要求先將給出的單詞存入樹中,這就要求我們的樹需要有插入操作。插入操作可以通過遞迴和迴圈來實現,在這裡我們介紹迴圈的方式。

每次插入一個單詞時我們需要將對應的字串的每一位提取出來,判斷這個字元是否為合法的英文字元,並且在當前節點的子節點中找到這個字元對應的子節點。如果這個子節點不為空,說明之前已經插入過一次,那麼這次插入只需要更新它的count計數,否則的話我們需要初始化這個節點,並且將它對應的字元和count計數值都更新。

void insert(string str){
        int index;
        TrieNode *tmp=root;
        tmp->count++;
		for(int i=0;i<str.size();i++){
            index=str[i]-'a';
            if(index < 0 || index > 25) return;
            else if(tmp->nextNode[index]==NULL){
                tmp->nextNode[index]=new TrieNode();


                //tmp->nextNode[index]->word
            }

            tmp=tmp->nextNode[index];
            tmp->count++;
            tmp->word[0]=str[i];
        }

    }
當所有的單詞都插入後,我們需要對給定的前綴出現的次數進行統計,也就是我們要找到這個字首的最後一個字元所在的節點的count計數值。因此從根節點開始層層尋找,如果沒有到這個字首的字元的最後一位就遇到了空節點,說明字典中並沒有存放這個字首單詞,需要返回零。如果找到了最後一個字元所在的節點,那麼就返回它的計數值。
bool isFound(const string str,int & cnt){
        int i=0;
        int index=-1;
        TrieNode *tmp=root;
        for(;i<str.size();i++){
            index=str[i]-'a';
			if(0>index || index>25) {cnt=0;return false;}

            tmp=tmp->nextNode[index];
            if(tmp!=NULL) cnt=tmp->count;
			else    {cnt=0;return false;}
        }

        return true;
    }
這兩個方法已經足夠我們應付這道題目了,下面來介紹一下方便我們除錯用的一個函式——列印整棵樹。

列印整棵樹其實就是遍歷整棵樹,我們為了方便就採取前序遍歷,遍歷採用簡單的遞迴。

void print(TrieNode * tmp){
		if(tmp==NULL) return;
		else{
			cout<<tmp->word[0]<<"\t";
		}
		for(int i=0;i<26;i++)
			print(tmp->nextNode[i]);
	}
	void printAll(){
		print(root);
	}
五、完整程式碼

這裡貼出完整的程式碼供大家參考。

#include <iostream>
#include <string>

using namespace std;


class TrieNode{
    public:

    int count;
    TrieNode * nextNode[26];
    char *word;
    public:
    TrieNode():word(NULL),count(0){
        for(int i=0;i<26;i++){
            nextNode[i]=NULL;
        }
    }

};


class TrieTree{
    public:
    TrieNode * root;
    TrieTree(){
        root=new TrieNode();
    }
    ~TrieTree(){
        delete [] root;
    }
    void insert(string str){
        int index;
        TrieNode *tmp=root;
        tmp->count++;
		for(int i=0;i<str.size();i++){
            index=str[i]-'a';
            if(index < 0 || index > 25) return;
            else if(tmp->nextNode[index]==NULL){
                tmp->nextNode[index]=new TrieNode();


                //tmp->nextNode[index]->word
            }

            tmp=tmp->nextNode[index];
            tmp->count++;
            tmp->word[0]=str[i];
        }

    }

    bool isFound(const string str,int & cnt){
        int i=0;
        int index=-1;
        TrieNode *tmp=root;
        for(;i<str.size();i++){
            index=str[i]-'a';
			if(0>index || index>25) {cnt=0;return false;}

            tmp=tmp->nextNode[index];
            if(tmp!=NULL) cnt=tmp->count;
			else    {cnt=0;return false;}
        }

        return true;
    }

	void print(TrieNode * tmp){
		if(tmp==NULL) return;
		else{
			cout<<tmp->word[0]<<"\t";
		}
		for(int i=0;i<26;i++)
			print(tmp->nextNode[i]);
	}
	void printAll(){
		print(root);
	}
};
int N;
int S;
int main(){
    TrieTree t;
    int cnt=0;
    string str;
    cin>>N;
    int i;
	int *a;
    for(i=0;i<N;i++){
        cin>>str;
        //cout<<"hi"<<endl;
        t.insert(str);
    }
	t.printAll();
    cin>>S;
	a=new int[S];
    for(i=0;i<S;i++){
        cnt=0;
        cin>>str;
        t.isFound(str,cnt);
        a[i]=cnt;
    }

	for(i=0;i<S;i++){
		cout<<a[i]<<endl;
	}
    return 0;
}