Trie樹簡介_其中的代碼不夠好還可以優化

Trie樹

       Trie樹也稱字典樹,因爲其效率很高,所以在在字符串查找、前綴匹配等中應用很廣泛,其高效率是以空間爲代價的。

一.Trie樹的原理

    利用串構建一個字典樹,這個字典樹保存了串的公共前綴信息,因此可以降低查詢操作的複雜度。

    下面以英文單詞構建的字典樹爲例,這棵Trie樹中每個結點包括26個孩子結點,因爲總共有26個英文字母(假設單詞都是小寫字母組成)。

    則可聲明包含Trie樹的結點信息的結構體:

複製代碼
#define MAX 26

typedef struct TrieNode               //Trie結點聲明 
{
    bool isStr;                      //標記該結點處是否構成單詞 
    struct TrieNode *next[MAX];      //兒子分支 
}Trie;
複製代碼

    其中next是一個指針數組,存放着指向各個孩子結點的指針。

    如給出字符串"abc","ab","bd","dda",根據該字符串序列構建一棵Trie樹。則構建的樹如下:

    

 Trie樹的根結點不包含任何信息,第一個字符串爲"abc",第一個字母爲'a',因此根結點中數組next下標爲'a'-97的值不爲NULL,其他同理,構建的Trie樹如圖所示,紅色結點表示在該處可以構成一個單詞。很顯然,如果要查找單詞"abc"是否存在,查找長度則爲O(len),len爲要查找的字符串的長度。而若採用一般的逐個匹配查找,則查找長度爲O(len*n),n爲字符串的個數。顯然基於Trie樹的查找效率要高很多。

但是卻是以空間爲代價的,比如圖中每個結點所佔的空間都爲(26*4+1)Byte=105Byte,那麼這棵Trie樹所佔的空間則爲105*8Byte=840Byte,而普通的逐個查找所佔空間只需(3+2+2+3)Byte=10Byte。

二.Trie樹的操作

    在Trie樹中主要有3個操作,插入、查找和刪除。一般情況下Trie樹中很少存在刪除單獨某個結點的情況,因此只考慮刪除整棵樹。

1.插入

  假設存在字符串str,Trie樹的根結點爲root。i=0,p=root。

  1)取str[i],判斷p->next[str[i]-97]是否爲空,若爲空,則建立結點temp,並將p->next[str[i]-97]指向temp,然後p指向temp;

   若不爲空,則p=p->next[str[i]-97];

  2)i++,繼續取str[i],循環1)中的操作,直到遇到結束符'\0',此時將當前結點p中的isStr置爲true。

2.查找

  假設要查找的字符串爲str,Trie樹的根結點爲root,i=0,p=root

  1)取str[i],判斷判斷p->next[str[i]-97]是否爲空,若爲空,則返回false;若不爲空,則p=p->next[str[i]-97],繼續取字符。

  2)重複1)中的操作直到遇到結束符'\0',若當前結點p不爲空並且isStr爲true,則返回true,否則返回false。

3.刪除

  刪除可以以遞歸的形式進行刪除。

測試程序:

/*Trie樹(字典樹) 2011.10.10*/
 
#include <iostream>
#include<cstdlib>
#define MAX 26
using namespace std;
 
typedef struct TrieNode                     //Trie結點聲明
{
    bool isStr;                            //標記該結點處是否構成單詞
    struct TrieNode *next[MAX];            //兒子分支
}Trie;
 
void insert(Trie *root,const char *s)     //將單詞s插入到字典樹中
{
    if(root==NULL||*s=='\0')
        return;
    int i;
    Trie *p=root;
    while(*s!='\0')
    {
        if(p->next[*s-'a']==NULL)        //如果不存在,則建立結點
        {
            Trie *temp=(Trie *)malloc(sizeof(Trie));
            for(i=0;i<MAX;i++)
            {
                temp->next[i]=NULL;
            }
            temp->isStr=false;
            p->next[*s-'a']=temp;
            p=p->next[*s-'a'];  
        }  
        else
        {
            p=p->next[*s-'a'];
        }
        s++;
    }
    p->isStr=true;                       //單詞結束的地方標記此處可以構成一個單詞
}
 
int search(Trie *root,const char *s)  //查找某個單詞是否已經存在
{
    Trie *p=root;
    while(p!=NULL&&*s!='\0')
    {
        p=p->next[*s-'a'];
        s++;
    }
    return (p!=NULL&&p->isStr==true);      //在單詞結束處的標記爲true時,單詞才存在
}
 
void del(Trie *root)                      //釋放整個字典樹佔的堆區空間
{
    int i;
    for(i=0;i<MAX;i++)
    {
        if(root->next[i]!=NULL)
        {
            del(root->next[i]);
        }
    }
    free(root);
}
 
int main(int argc, char *argv[])
{
    int i;
    int n,m;                              //n爲建立Trie樹輸入的單詞數,m爲要查找的單詞數
    char s[100];
    Trie *root= (Trie *)malloc(sizeof(Trie));
    for(i=0;i<MAX;i++)
    {
        root->next[i]=NULL;
    }
    root->isStr=false;
    scanf("%d",&n);
    getchar();
    for(i=0;i<n;i++)                 //先建立字典樹
    {
        scanf("%s",s);
        insert(root,s);
    }
    while(scanf("%d",&m)!=EOF)
    {
        for(i=0;i<m;i++)                 //查找
        {
            scanf("%s",s);
            if(search(root,s)==1)
                printf("YES\n");
            else
                printf("NO\n");
        }
        printf("\n");  
    }
    del(root);                         //釋放空間很重要
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章