patterncppMinor
Trinary Search Tree
Viewed 0 times
trinarytreesearch
Problem
I'm probably not the first to think this up, but here we go. An trinary search tree is a generalization of a binary search tree in the sense that instead of a comparison function returning true or false, it will return -1 for less than, 1 for greater than, and 0 for equivalent.
Once equivalence is found, a search along the "equivalence branch" will be made until equality if found. An example is where
outputs -1 if a.length() b.length(),
and 0 if a.length() == b.length(). Inserting a node shall always be at the bottom of the equivalence branch (if any).
The time complexity of a search in a trinary search tree is O(logN) + O(M), where M is the length of the equivalence branch (if any) of the target value (I think). Hence a trinary search tree is less efficient than a binary search tree,
but nevertheless it is good practice (and fun!) to write one up.
It does have the advantage of allowing for using tricky types (for the node values) where it is not clear what a comparison function such that
both being false implying equality would be. I cannot yet think of such a class that would fall in this category, but there is should be examples out there. In the example below, lexicographical ordering would work for strings to allow using a binary tree instead of a trinary tree, but let's assume that such an ordering does
not exist for strings, and we have to resort to using the above comparison function and thus using a trinary tree.
At any rate, here is my (fully compiling) implementation of a Trinary Search Tree. All tests conducted have passed. I tried to be as up-to-date in the C++ language as I could. Anyone reviewing this need not read every function I've defined here, but reading a handful and critiquing them is welcomed, especially the move constructor, move assignment operator, and such.
```
#include
#include
#include
#include
template
class TrinaryTree {
public:
enum TreeTr
Once equivalence is found, a search along the "equivalence branch" will be made until equality if found. An example is where
compare (const std::string& a, const std::string& b)outputs -1 if a.length() b.length(),
and 0 if a.length() == b.length(). Inserting a node shall always be at the bottom of the equivalence branch (if any).
The time complexity of a search in a trinary search tree is O(logN) + O(M), where M is the length of the equivalence branch (if any) of the target value (I think). Hence a trinary search tree is less efficient than a binary search tree,
but nevertheless it is good practice (and fun!) to write one up.
It does have the advantage of allowing for using tricky types (for the node values) where it is not clear what a comparison function such that
both being false implying equality would be. I cannot yet think of such a class that would fall in this category, but there is should be examples out there. In the example below, lexicographical ordering would work for strings to allow using a binary tree instead of a trinary tree, but let's assume that such an ordering does
not exist for strings, and we have to resort to using the above comparison function and thus using a trinary tree.
At any rate, here is my (fully compiling) implementation of a Trinary Search Tree. All tests conducted have passed. I tried to be as up-to-date in the C++ language as I could. Anyone reviewing this need not read every function I've defined here, but reading a handful and critiquing them is welcomed, especially the move constructor, move assignment operator, and such.
```
#include
#include
#include
#include
template
class TrinaryTree {
public:
enum TreeTr
Solution
In all, this code is not bad at all. And generically, no it's not new, but is a particular instance of an m-ary tree with \$m = 3\$. Here are some things that may help you improve it.
Separate declaration from definition
This could work as a header-only template but it's a bit difficult to discern the interface because most of the functions are defined inline. That's not a technical error, but I would probably separate the two, even if they are still in the same file. It makes the code longer but easier to use, in my opinion.
Allow variadic functions
The code currently has an
If you make all of the corresponding changes in
Rethink the relationship between tree and node
Every well-formed non-nullptr Node is also a tree, so I'd suggest that the
Consider making the tree more generic
As mentioned earlier, this is a specific instance of an m-ary tree for which \$m = 3\$. This suggests that one way to make this more generic would be to create an array of child nodes and pass in \$m\$ as a template parameter.
Separate declaration from definition
This could work as a header-only template but it's a bit difficult to discern the interface because most of the functions are defined inline. That's not a technical error, but I would probably separate the two, even if they are still in the same file. It makes the code longer but easier to use, in my opinion.
Allow variadic functions
The code currently has an
executeAction function which is a nice idea, but is unfortunately limited because it can't accept arguments. So I would suggest rewriting executeActionHelper as this:template decltype(auto) executeActionHelper (const std::shared_ptr& node, const F& f, Args&&... args) const {ExecuteActionHelper::template execute(node, f, std::forward(args)...);}If you make all of the corresponding changes in
execute and executeAction, the result is that you can now do things like this:auto action = [](const std::string& str, std::ostream& out)->void { out << str << ' ';};
std::cout << "\nPreorder traversal with action at each node:\n ";
tree.executeAction(action, std::cout);Rethink the relationship between tree and node
Every well-formed non-nullptr Node is also a tree, so I'd suggest that the
trinaryTree member of Node is not needed. I'd also make Node much simpler, moving most of its functionality to the TrinaryTree instead. It might look something like this:struct Node {
std::shared_ptr left, center, right;
T value;
};Consider making the tree more generic
As mentioned earlier, this is a specific instance of an m-ary tree for which \$m = 3\$. This suggests that one way to make this more generic would be to create an array of child nodes and pass in \$m\$ as a template parameter.
Code Snippets
template <TreeTraversal Tr, typename F, typename ...Args> decltype(auto) executeActionHelper (const std::shared_ptr<Node>& node, const F& f, Args&&... args) const {ExecuteActionHelper<Tr>::template execute(node, f, std::forward<Args>(args)...);}auto action = [](const std::string& str, std::ostream& out)->void { out << str << ' ';};
std::cout << "\nPreorder traversal with action at each node:\n ";
tree.executeAction(action, std::cout);struct Node {
std::shared_ptr<Node> left, center, right;
T value;
};Context
StackExchange Code Review Q#105548, answer score: 2
Revisions (0)
No revisions yet.