As the title says, it's a simple trie class written in C++11. The only operations implemented are insert, remove, and search (both prefix and exact matches). I sort of abandoned the test/debug process, and while I'm somewhat confident that there aren't any serious bugs, I can't guarantee it. Use this class at your own risk. ;)
Simple Trie Class
nitin1 commented: well done sir!! :-) +0
#include <map>
#include <memory>
#include <stack>
#include <string>
template <typename CharT>
class trie {
struct node {
std::map<CharT, std::shared_ptr<node>> link;
bool terminal;
CharT key;
public:
node(CharT key): terminal(false), key(key) { }
};
std::shared_ptr<node> root;
public:
trie(): root(std::make_shared<node>(CharT())) {}
/*
@description:
Adds a word path specified by s to the trie.
*/
void add(const std::basic_string<CharT>& s);
/*
@description:
Removes a word path specified by s from the trie.
*/
void remove(const std::basic_string<CharT>& s);
/*
@description:
Searches for the prefix s when require_terminal is false or exactly matches
a terminal path created by add() when require_terminal is true.
@returns:
basic_string::npos is returned if a match is found. If no match is found, the
index of the mismatch is returned. If require_terminal is true and a prefix is
matched, the size of s is returned.
*/
typename std::basic_string<CharT>::size_type match(const std::basic_string<CharT>& s, bool require_terminal = true);
};
template <typename CharT>
void trie<CharT>::add(const std::basic_string<CharT>& s)
{
auto it = root;
// Traverse the path and extend it as necessary
for (auto c : s) {
if (it->link.find(c) == it->link.end()) {
it->link[c] = std::make_shared<node>(c);
}
it = it->link[c];
}
// Mark the last key node as a terminal for the path. Useful for
// determining the difference between a prefix and a full word.
it->terminal = true;
}
template <typename CharT>
void trie<CharT>::remove(const std::basic_string<CharT>& s)
{
if (match(s) != std::basic_string<CharT>::npos) {
// This key doesn't exist in the trie, removing a prefix would break continuity
return;
}
std::stack<decltype(root)> bak;
auto it = root;
// Store the reversed path
for (auto c : s) {
bak.push(it);
it = it->link[c];
}
// Only necessary when another path intersects and continues
// from the end of this one, but it's cheaper just to always
// unmark the node as terminal.
it->terminal = false;
if (it->link.size() == 0) {
/*
Truncate any unshared tail section of this path
*/
CharT key = it->key;
// Find the start of the unshared tail for this path
while (!bak.empty() && bak.top()->link.size() <= 1 && !bak.top()->terminal) {
key = bak.top()->key;
bak.pop();
}
if (!bak.empty()) {
// Average case: Trim the tail from the parent's links
bak.top()->link.erase(key);
}
else {
// Edge case: This was the last path in the trie
root->link.clear();
}
}
}
template <typename CharT>
typename std::basic_string<CharT>::size_type trie<CharT>::match(const std::basic_string<CharT>& s, bool require_terminal)
{
auto end = s.begin();
auto it = root;
// Follow the path until the end of the string or a mismatch
while (end != s.end() && it->link.find(*end) != it->link.end()) {
it = it->link[*end++];
}
if (end == s.end() && (!require_terminal || it->terminal)) {
// The path was matched completely
return std::basic_string<CharT>::npos;
}
return end - s.begin(); // Return the index of the mismatch
}
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster
deceptikon commented: Thanks for the feedback! +12
deceptikon 1,790 Code Sniper Team Colleague Featured Poster
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster
deceptikon 1,790 Code Sniper Team Colleague Featured Poster
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.