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;
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;
}
}
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);
}
}
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;
}
}
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--;
}
}
#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--;
}
}