I want to make an iterator that can handle my selfmade structs:
struct node {
int n; //data element
node * parent;
node * left;
node * right;
node (int elem): n(elem){ //create root
parent = NULL; left = NULL; right = NULL;
}
node (int elem, node* p): n(elem), parent(p) { //addchild
left = NULL;
right = NULL;
}
node(int elem, node * p, node * l, node * r): n(elem), parent(p), left(l), right(r){ //insert element
}
};
First of all, is this possible? If yes: how do I start to make the iterator that traverses the tree. If not: what do you suggest for me to do if I want to access data elements in the list.
Yes.
Hint: Your iterator, when it advances, should go to the parent and figure out whether it's a left child or right child of that parent. That will tell you where to go next.
Yes, given the node structure you've outlined with a right pointer (which presumably points to the next node in traversal order), implementing a forward iterator should be quie easy.
The basic idea is something like this:
template <class Node>
class iterator {
Node *pos;
public:
iterator(Node *tree = nullptr) : pos(tree) {}
iterator operator++() { pos = pos->right; return *this; }
Node &operator*() { return *pos; }
bool operator!=(iterator const &other) const { return pos != other.pos; }
};
There is a little more than that involved in a real iterator though. In particular, you normally (always?) want to specify things like the value_type, reference_type and category of the iterator. Most of these are pretty close to pure boilerplate though, and you can handle most of them by deriving from std::iterator.
You also (typically) want to support a few more operations than I've shown here, such as post-fix increment, operator==, etc. Given this as a "framework", I think filling in those blanks should be fairly straightforward though.
I should probably also point out the existence of the Boost iterators library. This simplifies the task of implementing an iterator (somewhat) and cuts down on the boilerplate you have to write (quite a bit).
Related
I have implemented a simple binary tree class in C++.
have using smart pointers objects to hold the pointers to each node (shared for children and weak for parent).
I was trying to implement a nested class for custom iterator (in-order, pre-order and post-order), but I couldn't figure out how to implement efficiently the .next() method.
how can I get current position in traversal without holding the entire tree in a priority queue.
the tree nodes are a struct -
struct node : std::enable_shared_from_this
{
T _val;
std::weak_ptr<node> _parent;
std::shared_ptr<node> _leftChild;
std::shared_ptr<node> _rightChild;
// CONSTRUCTORS
node(T val): _val(val){}
node(T val, weak_ptr<node> parent): _val(val), _parent(parent){}
};
You need to know which child each node is, and from that you can derive the next node from the structure. You can do that with pointer equality
struct iter_base {
std::shared_ptr<node> current;
bool isRoot() const { return !current->_parent.lock(); }
bool isLeft() const { auto parent = current->_parent.lock(); return parent && (current == parent->_leftChild); }
bool isRight() const { auto parent = current->_parent.lock(); return parent && (current == parent->_rightChild); }
};
E.g. for inorder:
If you have a right child, follow that node's left descendants until there are no more, the last one is the next node.
Otherwise, if you are a left child, the next node is your parent.
Otherwise, if you are a right child, follow parent pointers until you find one that is a left child, and the next node is the parent of that left child. You've reached the end if you get to the root doing this.
I have an iterator class nested in a LinkedList class. My question is how do I make the insert_after function using iterators. The rest of the code is given for information purposes, but the function I'm trying to get working is at the end.
Insert_After takes a position and inserts a value after it.
template <typename T>
class LinkedList : public LinkedListInterface<T> {
private:
struct Node {
T data; // data can be any type
Node* next; // points to the next Node in the list
Node(const T& d, Node* n) : data(d), next(n) {}
};
Node* head; // Is a pointer
class Iterator
{
private:
Node* iNode;
public:
Iterator(Node* head) : iNode(head){ }
~Iterator() {}
bool operator!=(const Iterator& rhs) const { return iNode != rhs.iNode; }
Iterator& operator++() { iNode = iNode->next; return *this; }
T& operator*() const { return iNode->data; }
};
/** Return iterator pointing to the first value in linked list */
Iterator begin(void) {
return LinkedList<T>::Iterator(head);
}
/** Return iterator pointing to something not in linked list */
Iterator end(void) {
return LinkedList<T>::Iterator(NULL);
}
/** Return iterator pointing found value in linked list */
Iterator find(Iterator first, Iterator last, const T& value) {
Iterator current = first;
bool found = false;
while (current != last) {
if (*current == value) {
return current;
}
++current;
}
return last;
}
Iterator insert_after(Iterator position, const T& value)
{
// Need help here
}
What I've tried so far resulted in a few errors.
Iterator insert_after(Iterator position, const T& value)
{
// Need to insert after position
Iterator previous = position;
++position;
Node* newNode = new Node(value, position);
previous->next = newNode;
}
The error I got was Error C2664 'function' : cannot convert argument n from 'type1' to 'type2' for the line
Node* newNode = new Node(value, position);
Compiler Error C2819 type 'type' does not have an overloaded member 'operator ->' for line
previous->next = newNode;
I understand the errors but I'm not sure how to work around them.
I think the short answer to your compiler errors is that you are likely supposed to pass a Node or Node* as your second argument and not an iterator. previous is also an iterator, and therefore does not have a next call.
Long answer below about generally fixing the function in question:
There's a lot [not] going on in that function, which gets me wondering about the rest of the linked list class as well. I've only looked at this function, as it's the one you claim is causing you trouble.
SUBJECTIVE THOUGHT I generally hate working with iterators in my class functions. Deal with the Nodes directly as much as possible. The iterator pattern exists for universal traversal of containers no matter how they're laid out, and that abstraction makes it a pain to deal with inside your class.
Iterator insert_after(Iterator position, const T& value)
{
// Need to insert after position
Iterator previous = position;
++position;
Node* newNode = new Node(value, position);
previous->next = newNode;
}
As it currently stands, if position is anywhere but the last element, you will break your list and leak memory. This is because you never check what is after position. That first mistake leads into the second. newNode->next never gets set. Maybe it's default constructed to nullptr, that's fine. But if I'm inserting into the middle of my list, I need to connect newNode to whatever came after position originally.
Another question that you need to consider is "what if this is called on an empty list?" Does your begin() function handle that? Is it supposed to throw?
Iterator insert_after(Iterator position, const T& value)
{
Node* pos = position.iNode;
if (!pos) { // assumes an empty list if this is true
// Correctly build first Node of your list and get everything assigned
// that you can
// return the new iterator;
// or just throw
}
if (!pos->next) { // position at end of list if true
pos->next = new Node(value, position); // Is that second argument
// supposed to be an iterator?
return Iterator(pos->next);
} else {
// Some of this is probably redundant depending on how you are actually
// building Nodes, but the gist is it's important to ensure the list is
// not broken; connecting tmp before changing existing nodes helps the
// list stay intact for as long as possible
Node* tmp = new Node(value, position);
tmp->next = pos->next;
pos->next = tmp;
return Iterator(tmp);
}
A doubly linked list never seems attractive at first blush to a student, but it makes certain operations like erasing from the middle of the list so much easier. Yes, you have one extra pointer to deal with, but it makes it harder to lose Nodes as well. Along with bi-directional iteration.
Working on a tree class with an STL-like interface, I encountered a problem using non default-constructible elements:
As I'm implementing iterators, I need a past-the-last element at any point in time. My approach is to create one in the constructor, which asserts the value type to be default constructible.
Is there an approach to get rid of this constraint?
Pointing past the end can't possibly be nullptr if your iterator is bidirectional, end()-- needs to be legal.
It can be instead implemented by a sentinel, and in this case, the sentinel shouldn't even contain a default constructed element.
This can be done as such
struct link
{
link *parent, *left, *right;
};
template<typename T>
struct node : link
{
T data;
};
template<typename T>
struct tree : link
{
// tree itself serves as the sentinel
// At initialization parent and childs should all point to the sentinel
tree() : parent(this), left(this), right(this) {}
// ...
};
And iterators need no special handling for one past the end case.
// nested within tree
struct iterator
{
explicit iterator(link* l) : n(l) {}
iterator& operator--() { n = n->parent; return *this; } // or something else
auto& operator*() { return reinterpret_cast<node<T>*>(n)->data; }
// ...
link* n;
};
iterator begin() { return {left}; } // or something else
iterator end() { return {this}; }
I solved the problem by storing another pointer to the root node, from which I can recreate the last element in constant time.
I keep finding myself wanting to do things the right way™. However, I am a bit confused about data encapsulation (not the principle of it, but how to do it correctly in C++)
Let's say I have the following class:
template <class T, class Alloc = std::allocator<T> >
class Tree
{
public:
class Node
{
public:
T data;
Node** get_children() const { return children; }
Node* get_parent() const { return parent; }
Node* get_right() const { return right; }
friend class Tree;
private:
Node** children;
Node* parent;
Node* right;
};
// typedefs for STL ...
class iterator // linear iterator
{
// ...
};
class const_iterator // linear iterator
{
// ...
};
// Tree operations ...
private:
Node root;
};
I want the tree to be able to modify the structure of the nodes freely, so I made it a friend class to the node.
I also want the user to be able to traverse the tree as a tree (rather than using the tree structure for storage behind the scenes, and just letting the user iterate over it linearly).
The data should be freely modifiable. If the owner of the tree object doesn't want another user to modify its contents, it can pass a const reference.
Now I'm left wondering about the return types of my getter methods. Whatever happens, I don't want the user ever to be able to change the structural information of the node directly. The tree should always be the one modifying the private members. Will this code guarantee that? What if instead of a Node**, we save a std::vector<Node*> and return a const reference to the vector?
These two are secure:
Node* get_parent() const { return parent; }
Node* get_right() const { return right; }
Since these functions return pointers by value, the user cannot modify the member variables.
This one is unsafe:
Node** get_children() const { return children; }
This too returns a pointer by value, so that the user cannot modify children, but the user can modify the elements in the array, which I presume are pointers to children. Here's how to make it safe:
Node * const * get_children() const { return children; }
(And when you're ready, you can advance to STL containers and stop using arrays.)
I don't get what Bjarne has on mind stating:
A list iterator must be something more complicated than a simple
pointer to an element because an element of a list in general does not
know where the next element of that list is. Thus, a list iterator
might be a pointer to a link
I know that list's node has pointers to the next and previous nodes. So how it "in general does not know"?
For contrast, let's consider an iterator for std::vector. Though you probably don't want to, you could basically implement it as a simple pointer:
template <class T>
class vector {
T *data;
size_t current_size;
size_t alloc_size;
public:
typedef T *iterator;
iterator begin() { return data; }
iterator end() { return data+current_size; }
// ...
};
and since the data in the vector is contiguous, when you do (for example) ++ on the iterator, it would do the right thing (get you to the next item in the vector).
With a linked list, it can't be that simple though -- if you used typedef to make iterator an alias for T *, it wouldn't work right. When you tried to do ++ on the iterator, it would just increment the pointer, instead of taking you to the next element in the linked list like it needs to.
You need to build some intelligence into the iterator class so operator++ (for one example) knows to "chase" the pointer instead of just incrementing.
template <class T>
class list {
class node {
T data;
node *next;
node *prev;
};
public:
class iterator {
node *pos;
public:
iterator operator++() {
return pos = pos->next;
}
iterator operator--() {
return pos = pos->prev;
}
};
};
Don't confuse list node with a list element. It might be a pointer, but to a node, which contains element and next/prev links. If it were just a pointer to the element, you wouldn't be able to move around with it. (Though it's unlikely to be just pointer, since it typically has to overload operator++ to fetch next pointer instead of incrementing itself).
This isn't true for intrusive lists, where node == element, but that's not std::list.
// illustrative
template <typename ElementType>
struct list_node {
ElementType element;
list_node *next, *prev;
};