Trie树的应用,一道算法问题求解 代码实现

  上篇文章中我们介绍了Trie树结构,并使用Trie树解决了一个算法问题,具体问题请参看这里:  

  http://www.cnblogs.com/springfield/archive/2010/06/16/1758450.html

 

  这篇文章中我们来介绍这个算法的具体实现:

  

  首先我们要定义Trie树的节点:

  

 

typedef  struct  Node{

    
int  words;                 // 在该节点处结束的字符串数量    
    BOOL hasChild;             // 该节点是否有子节点,用于遍历Trie树
     struct  Node  * edges[ 10 ];    // 该节点的10条边 分别对应0-9每个字符

}TrieNode;

 

  

  在定义好节点之后,我们需要初始化该节点,所以下面的初始化函数用来给节点的各个成员设置默认值:

 

 

void  init(TrieNode  * node){
    
    
int  i;
    node
-> words  =   0 ;
    node -> hasChild  =  FALSE;
    
for (i  =   0 ;i  <   10 ;i ++ ){
        
        node
-> edges[i]  =  NULL;
    }
}

 

 

  下面就可以开始构造Trie树了,由于我们的题目中,输入的是一组电话号码,所以我们逐一用每条电话号码来构造Trie树,从第一个电话号码开始,将每个电话号码插入Trie树中,具体过程前面介绍过了,这里只简单说一下:从根节点开始,如果这个号码的第一个字符为'2',那么就从根节点对应字符为'2'的边上创建一个子节点,然后再从这个子节点开始继续和该号码的下一个字符进行比对,操作方式和上面相同,更详细的资料请参看上一篇文章,这里不再赘述.
  我们这里引入了一个函数,用这个函数来进行增加号码的操作:
 
  
void  addNode(TrieNode  * node, char   * word){

    
char  firstChar;
    TrieNode 
* tempNode;
    
int  edgeIndex;

    
if (strlen(word)  ==   0 ){

        node
-> words  =  node -> words  +   1 ;

    }
else {
                
        node
-> hasChild  =  TRUE;
        firstChar 
=  word[ 0 ];
        edgeIndex 
=  (( int )firstChar)  -   48 ;
        
if (node -> edges[edgeIndex]  ==  NULL){

            tempNode 
=  node -> edges[edgeIndex]  =  createTrieNode();  //创建Trie树节点,进行分配内存空间等操作

            init(tempNode);
        }
        

        cutLeftmostCharacter(word);  //删除字符串中的最左边的字符,因为这个字符已经在Trie树中计算过了
        addNode(node
-> edges[edgeIndex],word);

    }
}

 

  有了AddNode()这个函数,我们就可以构造出整棵Trie树了,这个函数中用到了两个辅助函数createTrieNode()和cutLeftmostCharacter(),分别用来创建新节点和去除最左边的字符,以便进行下一次调用。

  在我们将一组电话号码全都增加到Trie树中之后,我们进可以遍历这颗树,来确定是否要包含关系的字符串了,这个函数主要是通过判断是否有非叶子节点被标识成字符串结尾(这个字上一篇文章中有详细介绍),该函数实现如下:

 

int  calcIfConsistent(TrieNode  * node){
    
    
int  i;
    
long  rs  =   0 ;                 // 用来表示是否有非叶子节点被表示为字符串结尾,如果rs大于0证明有这种节点

    
if (node -> hasChild){             // 如果该节点不是叶子节点,判断它的标识情况
        
        rs 
=  node -> words;         // 该节点被标识为字符串结尾的数量

        
for (i  =   0 ;i  <   10 ;i ++ ){     // 对该节点的所有子节点进行同样的操作
            
            
if (node -> edges[i]  !=  NULL){
                
                rs 
+=  calcIfConsistent(node -> edges[i]);
            }
        }            

        
return  rs;

    }
else {
        
return   0 ;
    }
}

 

 

   我们通过这个函数的返回值,就可以判断出这组电话号码是否一致,然后输出相应的结果,这道问题就可以解决了。当然这里给出的代码肯定不是最好的,如果大家有更好的思路,欢迎大家一起交流。完整代码如下:

 

  

  

算法代码
#include  < stdio.h >
#include 
< string .h >
#include 
< stdlib.h >

#define  TRUE 1
#define  FALSE 0

typedef 
int  BOOL;

typedef 
struct  Node{

    
int  words;                 // 在该节点处结束的字符串数量    
    BOOL hasChild;             // 该节点是否有子节点,用于遍历Trie树
     struct  Node  * edges[ 10 ];     // 该节点的10条边 分别对应0-9每个字符

}TrieNode;

TrieNode
*  createTrieNode(){
    
    
return  (TrieNode * )malloc( sizeof (TrieNode));
}

void  cutLeftmostCharacter( char   * str){

    
int  len  =  strlen(str);
    
int  i;

    
for (i  =   1 ;i  <  len;i ++ ){
        
        str[i 
-   1 =  str[i];

    }

    str[i 
-   1 =   ' \0 ' ;
}

void  init(TrieNode  * node){
    
    
int  i;
    node
-> words  =   0 ;
    
// node->prefixes = 0;
    node -> hasChild  =  FALSE;
    
for (i  =   0 ;i  <   10 ;i ++ ){
        
        node
-> edges[i]  =  NULL;
    }
}

void  addNode(TrieNode  * node, char   * word){

    
char  firstChar;
    TrieNode 
* tempNode;
    
int  edgeIndex;

    
if (strlen(word)  ==   0 ){

        node
-> words  =  node -> words  +   1 ;

    }
else {
                
        node
-> hasChild  =  TRUE;
        firstChar 
=  word[ 0 ];
        edgeIndex 
=  (( int )firstChar)  -   48 ;
        
if (node -> edges[edgeIndex]  ==  NULL){

            tempNode 
=  node -> edges[edgeIndex]  =  createTrieNode();

            init(tempNode);
        }
        

        cutLeftmostCharacter(word);
        addNode(node
-> edges[edgeIndex],word);

    }
}

int  calcIfConsistent(TrieNode  * node){
    
    
int  i;
    
long  rs  =   0 ;                 // 用来表示是否有非叶子节点被表示为字符串结尾,如果rs大于0证明有这种节点

    
if (node -> hasChild){             // 如果该节点不是叶子节点,判断它的标识情况
        
        rs 
=  node -> words;         // 该节点被标识为字符串结尾的数量

        
for (i  =   0 ;i  <   10 ;i ++ ){     // 对该节点的所有子节点进行同样的操作
            
            
if (node -> edges[i]  !=  NULL){
                
                rs 
+=  calcIfConsistent(node -> edges[i]);
            }
        }            

        
return  rs;

    }
else {
        
return   0 ;
    }
}


void  freeTrieTree(TrieNode  * node){

    
int  i;

    
if (node -> hasChild){
    
        
for (i  =   0 ;i  <   10 ;i ++ ){
            
            
if (node -> edges[i]  !=  NULL){

                freeTrieTree(node
-> edges[i]);
            }

        }

        free(node);

    }
else {

        free(node);
    }
}

int  main(){

    
int  group_count  =   0 ;
    
int  n  =   0 ;
    
char  phoneNumber[ 11 ];
    TrieNode 
* root;
    scanf(
" %d\n " , & group_count);

    
while (group_count  !=   0 ){
                
        scanf(
" %d\n " , & n);
        
        root 
=  createTrieNode();        
        init(root);        
        
while (n  !=   0 ){
            
            scanf(
" %s\n " ,phoneNumber);
            addNode(root,phoneNumber);
            n
-- ;

        }

        
if (calcIfConsistent(root)  >   0 ){
            
            printf(
" NO\n " );

        }
else {
        
            printf(
" YES\n " );
        }
        
        freeTrieTree(root);
        group_count
-- ;
    }
}

 

 

  

 

  

你可能感兴趣的:(trie)