I have two questions:
How can the Array Abstract data type be modified to implement an Associative Array?
How can the tree abstract data type be modified to implement an Associative Array?
To create an associative array out of an array, you'd typically start with an array of some sort of structure:
struct item {
key_type key;
value_type value;
};
Then you'd use keys to look up values. For the sake of efficiency, you'd typically want to sort the array based on the keys, so you could use a binary search (or an interpolating search, if there's any degree of predictability to your key distribution).
For a tree, you'd do pretty much the same, except that for a tree a binary search is the default. You end up with a node pretty similar to that for an array, plus a couple of pointers:
struct node {
key_type key;
value_type value;
struct node *left;
struct node *right;
};
Depending on the type of tree involved, you might also want another pointer to create a threaded tree and/or some balance information (e.g., for an AVL or R-B tree). Conversely, for a B-Tree you'd end up with arrays of nodes about like for the associative array, and link those together into a balanced tree.
Related
I have a tree whose nodes are large strings. I don't really need to navigate the tree other than to follow the path from a node back to the root, so it suffices for each node to consist of the string and a pointer to its parent. I also need to be able to quickly find strings in the tree. The nodes of the tree themselves are not ordered, so this would require some sort of index. However, the strings are big enough that I would rather not duplicate them by storing them both in my tree and in my index.
I could implement both my tree and the index with a single std::map if the key for the map was the string and the mapped value was the pointer to its parent. However, I cannot figure out a way to write either of these types. My best guess would be something like this:
using Tree = std::map<std::string, typename Tree::const_iterator>;
or maybe:
using Node = std::pair<std::string const, typename Node const*>;
using Tree = std::map<std::string, Node const*>;
But these recursive type definitions don't compile. Is there any way to create this type in C++? Or a better way to do what I am trying to do?
You can wrap the iterator in a type of your own and reference that type instead to avoid the recurisve type problem.
struct const_iterator_wrapper {
using iterator_type = map<string, const_iterator_wrapper>::const_iterator;
iterator_type iter;
const_iterator_wrapper() {}
const_iterator_wrapper(iterator_type _iter) : iter(_iter) {}
};
using tree = map<string, const_iterator_wrapper>;
Your using Node definition doesn't compile, but an actual structure would:
struct Node {
std::string const s;
Node const* n;
};
It was not mentioned whether or not a node's parent might change after creation. If it does not, then a set might be a better fit than a map. (If it does change, then the set option is not completely off the table, but it might require weakening const correctness guarantees, possibly by making the parent pointer mutable.) In fact, you might not have to change your data structure much to use a set.
Let's say your nodes currently look like the following.
struct Node {
std::string data;
const Node * parent; // Might need to add `const`
};
You want these sorted by the data, ignoring the parent pointer. This might require defining a new function. If the following operator< is already defined as something else, then it takes a little more work to define your set, but still not hard.
bool operator<(const Node &a, const Node &b) {
return a.data < b.data;
}
This is all you need to define a set of these nodes that will function much like your desired map.
std::set<Node> tree;
// Add a root element.
auto result = tree.emplace("root", nullptr);
auto root_it = result.first;
// Add a child to the root.
tree.emplace("child", &*root_it);
// The `&*` combination may look odd. It is, though, a way to
// convert an iterator to a pointer.
There are a few gotchas that some people might find unexpected, but nothing other than what comes from using a map for this role.
In the end, as far as the structure of the data is concerned, a map<K, const V> is equivalent to a set<pair<K, V>> with a suitable comparison function. While the member functions differ, the biggest real functional difference between a map and a set of pairs is that the map's values can be changed (hence const V instead of V earlier).
I'm trying to use an std::list and an std::unordered_map to store a directed acyclic graph. Each list element stores a node key (unsigned) and its children. And the map stores, for each key, an iterator to the node in the list:
std::list<std::pair<unsigned, std::list<decltype(lst.begin())>>> lst;
std::unordered_map<unsigned, decltype(lst.begin())> nodemap;
But decltype(lst.begin()) in the declaration of lst results in a compile error since lst is not defined yet. Can I define lst another way ?
Edit: using std::vector<unsigned, std::list<unsigned>> vec would probably work, where list<unsigned> contains indexes into vec. Not sure whether sticking with the initial std::list is better or worse.
Write classes. Within the definition of the class, it is an incomplete type, which means you can use pointers (or references) to it.
The children pointers can be non-owning, with the map owning all the nodes.
class Graph {
struct Node {
unsigned key;
std::vector<Node *> children;
};
std::unordered_map<unsigned, Node> nodes;
public:
// graph behaviours
};
In c++ there is a way to create a tree-like map that contains maps as a value?
map<string, map<string, map<string, map<...>>> name;
For arbitrary depth, you're going to need some dynamic allocation.
struct Node
{
map<string, std::unique_ptr<Node>> children;
};
Node root;
Your bottom-most nodes will have no children.
We can't just write map<string, Node> children (even though the child Nodes will themselves be dynamically allocated within the map), because Node is "incomplete" in the definition; in a sense, that's a limitation of C++ (though you can work around this with a little more thought).
Not to an arbitrary depth, no.
But, like most problems, you can solve it by adding a level of indirection.
For instance,
struct Tree
{
std::map<std::string, std::optional<Tree>> children;
};
The C++ STL apparently is missing an ordered tree data structure. See here. Boost is also missing an ordered tree, but it does have an "un"ordered one, Property Tree where the data is ordered by insertion. I want the order to be irrespective of memory.
The boost page on Property Trees says that this is conceptually the boost::ptree structure.
struct ptree
{
data_type data; // data associated with the node
list< pair<key_type, ptree> > children; // ordered list of named children by insertion
};
I want to extend boost to keep track of order.
Is this the correct way?
class ordered_ptree : public boost::property_tree::ptree {
public:
ordered_ptree(int id) : _id{id}{};
protected:
int _id;
};
(From the comments in your question, I understand you want something like Python's OrderedDict but taking into account keys' relative order.)
Since none of the standard library's (or boost's) containers are exactly what you want, you might want to extend std::map (especially if you don't need all of the interface).
Say you start with
template<
typename Key,
typename Value,
class Compare=std::less<Key>,
class Alloc=std::allocator<pair<const Key, Value> >
class ordered_map
{
// This needs to be filled.
};
Now inside, you can hold an insertion counter:
std::size_t m_ins_count;
which is initialized to 0 and incremented at each insert.
Internally, your new keys will be std::pairs of the original key and the insertion count. Standard properties of binary search trees imply that nodes with keys differing only by the second pair item (which is the insertion count), will be consecutive in an in-order walk, which means that
you retain order of different keys
you retain order of insertion within a key
the operations are logarithmic time
traversing same-key items is (amortized) linear time
So, internally you'd have something like
typedef
std::map<
std::pair<Key, std::size_t>,
Value,
lex_compare<Compare>,
std::allocator<std::pair<std::pair<Key, std::size_t>, Value> >
internal_map_t;
(where lex_compare<Compare> compares first by the given functor, then by the insertion index).
Now you can choose a (minimal) interface, and implement it, by translating keys in the "outer world" and pairs of keys + insertion indices in the "inner world" of the tree.
If you plan on providing an iterator interface as well, you might find the boost iterator library useful, as you simply want to modify std::map's iterators.
I need a map whose keys are of some composite type T to a vector of iterators of the map itself.
(e.g., think of a graph in which every node holds iterators that point to its parents.)
The reason I'm avoiding storing the parents' keys as values is that the keys are expensive objects, so I'd like to store iterators instead.
Is such a thing possible?
If not, what's the best alternative?
(I know I can use polymorphism and type erasure as a brute-force way to solve this, but I'm wondering if there's a better way.)
Due to 23.2.1 General container requirements:
Containers are objects that store other objects. They control allocation
and deallocation of these objects through constructors, destructors,
insert and erase operations.
Hence it is possible:
struct Key;
struct Value;
typedef std::map<Key, Value> Map;
struct Key {};
struct Value {
std::vector<Map::iterator> parents;
};
(All types and sizes are known)
You most likely want to store a smart pointer to the parent rather than an iterator.