本文地址:http://blog.csdn.net/spch2008/article/details/9138557
使用字典树来存储数据,数据是一个键值对,即键值-数值。
字典树允许数据键值共享相同的前缀。本文采用的键值为字符串,数据如下:
amy 56
ann 15
emma 30
rob 27
roger 52
首先存入amy, level 0表示根,不持有数据。其余每个节点持有一个字符;叶子节点持有数据,且持有的字符为’\0′
. <- level 0 (root)
|
a <- level 1
|
m <- level 2
|
y <- level 3
|
\0 56 <- level 4
添加ann后,后缀树如下,二者共享前缀’a’
.
|
a
/ \
m n
| |
y n
| |
\0 56 \0 15
下面加入emma,由于emma与之前添加的键值没有相同前缀,所以直接加入根节点下。
.
/ \
a e
/ \ |
m n m
| | |
y n m
| | |
\0 56 \0 15 a
|
\0 30
将rob与roger加入后,所得字典树如下:
.
/ | \
a e r
/ \ | |
m n m o
| | | / \
y n m b g
| | | | |
\0 56 \0 15 a \0 27 e
| |
\0 30 r
|
\0 52
现在有2个问题:
如果继续添加anne为67,将出现什么状况?
每个节点有多少个子节点?
解决办法:
1. 每个节点存放一个数组,可以容纳所有键值中的字符
2. 采用链表
3. 左儿子右兄弟表示法
这里我们采用左孩子右兄弟的表示方法。上述数据可以表示为:
|
a --------- e ----- r
| | |
m --- n m o
| | | |
y n m b ----- g
| | | | |
\0 56 \0 15 a \0 27 e
| |
\0 30 r
|
\0 52
.h
class TrieTree
{
private:
struct Node
{
char key;
int val;
Node *next, *child;
Node(char k =' ', int v = 0)
{
key = k;
val = v;
next = child = NULL;
}
};
public:
TrieTree(void);
~TrieTree(void);
bool IsMember(char str[]);
void Add(char str[], int val);
void Remove(char str[]);
int GetVal(char str[]);
private:
Node *root;
Node* _Insert(Node* subtree, char *str, int val);
Node* _CreateSubTree(char *str, int val);
Node* _Delete(Node* subtree, char *str);
Node* _Find(char *str);
void _Destroy(Node* subtree);
};
.cpp
TrieTree::TrieTree(void)
{
root = new Node;
root->next = root->child = NULL;
}
TrieTree::~TrieTree(void)
{
_Destroy(root);
}
bool TrieTree::IsMember(char str[])
{
Node *found = _Find(str);
return found != NULL;
}
void TrieTree::Add(char str[], int val)
{
root->next = _Insert(root->next, str, val);
}
void TrieTree::Remove(char str[])
{
root->next = _Delete(root->next, str);
}
int TrieTree::GetVal(char str[])
{
Node *found = _Find(str);
if(found == NULL)
return -1;
else
return found->val;
}
TrieTree::Node* TrieTree::_Insert(Node* subtree, char *str, int val)
{
if(subtree == NULL)
{
subtree = _CreateSubTree(str, val);
}
else if(subtree->key != str[0])
{
subtree->next = _Insert(subtree->next, str, val);
}
else
{
if(str[0] == '\0') //修改原值
subtree->val = val;
else
subtree->child = _Insert(subtree->child, &str[1], val);
}
return subtree;
}
void TrieTree::_Destroy(Node *subtree)
{
if(subtree != NULL)
{
_Destroy(subtree->child);
_Destroy(subtree->next);
delete subtree;
}
}
TrieTree::Node* TrieTree::_Find(char *str)
{
int index = 0;
Node *level = root->next;
while(true)
{
Node *found = NULL;
//当前层匹配
for(Node *curr = level; curr != NULL; curr = curr->next)
{
if(curr->key == str[index])
{
found = curr;
break;
}
}
if(found == NULL)
return NULL;
//不能found->key == str[index]
//str可以先匹配完成
if(str[index] == '\0')
return found;
//匹配上后,向下在孩子中匹配
level = found ->child;
index += 1;
}
return NULL;
}
TrieTree::Node* TrieTree::_Delete(Node* subtree, char *str)
{
if(subtree == NULL)
return NULL;
if(subtree->key != str[0])
{
subtree->next = _Delete(subtree->next, str);
}
else
{
if(subtree->key == '\0')
{
delete subtree;
subtree = NULL;
}
else
{
subtree->child = _Delete(subtree->child, &str[1]);
if(subtree->next == NULL && subtree->child == NULL)
{
delete subtree;
subtree = NULL;
}
}
}
return subtree;
}
TrieTree::Node * TrieTree::_CreateSubTree(char *str, int val)
{
Node *subtree = new Node('\0', val);
for(int i = strlen(str) - 1; i >= 0; i--)
{
Node *node = new Node(str[i]);
node->child = subtree;
subtree = node;
}
return subtree;
}
来自:http://www.cs.bu.edu/teaching/c/tree/trie/