Trie树也称字典树,因为其效率很高,所以在在字符串查找、前缀匹配等中应用很广泛,下来就跟着我来建一棵自己的Trie树(以要存储的数据为字符串)。
(1)分析.
如上图,是一个简单的Trie树,我们可以看到以下特征:
1.多个子节点共用一个"根节点".
2.根节点为root里面没有存有任何数据.
3.对每个节点来说,由两部分构成,数据和一系列"指向"(可理解为与其子节点的联系).
4.它是一棵树,因此对树的操作,实际就是对节点的操作.
(2)构架
由此,我们和容易就可以构造一个节点,
class TrieNode {
char data;
list<TrieNode*> child;
};
那么一棵Trie树也就更简单了,
class Trie {
TrieNode* root;
};
由此我们已经大体上建好了一棵Trie数,只不过是差一些操作函数而已。
看过一些其他博客里面的定义,有些会定义一个定常的数组在节点立,我认为还是链表好一点,首先它可以节省空间,其次在STL中list已经被封装好,使用起来很方便。
(2)功能构想.
Trie树的操作主要有插入,删除,查找和销毁等。
想一下所有的功能,不难发现我们会频繁在一个节点的字孩子中查找,所以为了方便,我们直接在节点里面就定义一个查找函数TrieNode* find(char key)在当前节点的子孩子中寻找能与key匹配的节点,没有找到返回NULL.
还有插入操作就是在一个节点的孩子联表中插入一个新的指针,于是我们在节点里面在定义一个插入函数void insert(TrieNode* &newchild)王当前节点孩子中插入一个新的孩子newchild.
查找value[]:从根节点开始扎找value[0],找到的话继续向下查找,直至每个元素都找完,否则返回false。
插入value[]:先从根节点开始在还在查找value[0],找到的话,向下继续;没有找到,就建立一个新的节点,然后插入上一个节点中,继续向下查找,直到把value整个插入完成。
删除value[]:用一个栈来保存访问路径。从根节点开始找value[0],找到的话继续向下查找,直至找完,否则返回false,然后,开始压栈,如果第一次压栈发现栈顶有子孩子,说明value[strlen(value)-]为树中中间节点不能删除,返回false;如果栈顶没有孩子则将其删除,继续压栈直至栈中只有root,否则返回true;
销毁:
函数的功能实现方法很多,上面只供参考。
(3)实现.
/////////////////main.cpp
#include <iostream>
#include "Tree.h"
#include <cstring>
using namespace std;
//测试函数
void test() {
Trie t;
char a[20];
int i = 0;
while (i == 0) {
if (i < 5)
cout << "Input the data you want to insert: " << endl;
while (i++ < 1) {
cin >> a;
t.insert(a, strlen(a));
}
cout << "prin: " << endl;
t.prin();
cout << endl << "Input the data you want to find: ";
i = 3;
while (i++ < 7) {
cin >> a;
if (t.find(a, strlen(a)) == NULL)
cout << "No find " << a << "!!" << endl;
else
cout << "Yes find " << a << endl;
}
cout << "Input the data you want to delete: ";
cin >> a;
t.del(a, strlen(a));
t.prin();
cout << endl;
}
}
int main() {
test();
return 0;
}
////////////Trie.h
#ifndef TREE_H_INCLUDED
#define TREE_H_INCLUDED
#include <vector>
#include <list>
using namespace std;
class Node {
public:
char data;
list<Node*> p;
public:
Node() {}
Node(char value):data(value) {}
Node* find(char value);
void insert(Node*&);
};
class Trie {
Node* root;
public:
Trie();
~Trie() {remove();}
Node* find(char*, int size); //查找
bool insert(char*, int size); //插入
bool del(char*, int size); //删除
bool remove(); //销毁
void prin(); //测试打印函数
};
#endif
/////////////Trie.cpp
#include "Tree.h"
#include <cstring>
#include <iostream>
#include <stdlib.h>
#include <stack>
Node* Node::find(char value) {
list<Node*>::iterator t;
for (t=p.begin(); t!=p.end(); t++)
if (value == (*t)->data)
return *t;
return NULL;
}
void Node::insert(Node* &newnode) {
p.push_back(newnode);
}
Node* Trie::find(char* value, int size) {
int j =-1;
Node* p = root;
while (++j != size) {
p = p->find(value[j]);
if (p == NULL)
return NULL;
}
return p;
}
Trie::Trie() {
root = new Node();
root->data = '#';
}
bool Trie::insert(char* value, int size) {
int i = 0, j =0;
bool sign;
Node* p, *temp = root;
p = root->find(value[j]);
while (j < size) {
sign = false;
if (p == NULL) {
p = new Node(value[j++]);
temp->insert(p);
sign = true;
}
temp = p;
if (sign)
p = p->find(value[j]);
else
p = p->find(value[++j]);
}
return (sign && (size == j));
}
bool Trie::del(char* value, int size) {
int j =-1;
stack<Node*> s;
Node* p = root, *pointer;
while (++j != size) {
s.push(p);
p = p->find(value[j]);
if (p == NULL)
return false;
}
s.push(p);
j = 0;
while (s.size() > 1) {
p = s.top();
s.pop();
int len = p->p.size();
if (j == 0) {
if (len != 0)
return false;
pointer = p;
delete p;
p = s.top();
p->p.remove(pointer);
continue;
}
if (len == 0) {
pointer = p;
delete p;
p = s.top();
p->p.remove(pointer);
} else
return true;
}
}
bool Trie::remove() {
stack<Node*> s;
list<Node*>::iterator t;
Node* temp = root;
s.push(root);
bool sign = false;
while (!s.empty()) {
s.pop();
for (t=temp->p.begin(); t!=temp->p.end(); t++)
s.push(*t);
delete temp;
if (s.empty())
break;
temp = s.top();
}
root = NULL;
return true;
}
void Trie::prin() {
stack<Node*> s;
list<Node*>::iterator t;
Node* temp = root;
if (root == NULL) {
std::cout << "empty tree" << std::endl;
return;
}
s.push(root);
bool sign = false;
while (!s.empty()) {
s.pop();
for (t=temp->p.begin(); t!=temp->p.end(); t++)
s.push(*t);
if (s.empty())
break;
temp = s.top();
std::cout << temp->data << " ";
}
}