Is there a possiblity to write a template for List (like in STL) that will be made of double linked list using nodes connected to themself and to provide ability to use iterators like begin or end?
If I had nested class:
class Node{
T data;
Node* previous, next;
Node(T &data, Node* next);
};
And my list would have begin() function:
template<class T>
class List {
Node *data; //first element
...
public:
T* begin() { return data->data; }; //return content of the first element
...
I assume that if I would like to use that list with for example std::copy function like
copy(l.begin(), l.end(), out);
then copy function iterates through the list using "begin++" then it would like to increment a pointer that points to the "data" object inside of node. Then it would not take a data from next node.
So is it possible to make that kind of a list?
First of all, there's std::list - which is probably what you want.
Secondly, your implementation of begin() does not fit the expectation for what that function returns for containers. You'll want to return something that at the very least models ForwardIterator (and since it's doubly-linked, BidirectionalIterator). Basically, this needs to work:
List<int> my_list = ...;
auto it = my_list.begin();
int& x = *it; // first value in the list
++it; // next element in the list
int& y = *it; // next value in the list
Right now, begin() yields a List<int>::Node*. That dereferences to a List<int>::Node, but it should dereference to an int. Wrong type and leaking the abstraction. Incrementing the pointer compiles, but it will point to some arbitrary spot in memory rather than the next node. There is no guarantee after all that the next node will be adjacent in memory (almost certainly it isn't!)
So you need to write your own iterator type which wraps your Node class, which will have to do those operations correctly based on the iterator concepts. Basically you're just mapping the iterator concept operations to what those look like for Node. To get you started as an example:
Node* underlying;
iterator& operator++() {
underlying = underlying->next;
return *this;
}
T& operator*() {
return underlying->data;
}
Also, check out the Boost's Iterator Facade library, which is helpful for writing iterators correctly.
Related
I'm trying to implement about list and iterator of list like C++ STL.
The node in list is defined like this:
struct Node{
Node *prev,*next;
value_type data;
};
And I want to overloading operator > and < :
bool list_iterator::operator>(const iterator_impl_base &rhs) const
bool list_iterator::operator<(const iterator_impl_base &rhs) const
which means if I need to call next to reach rhs.node , it will return 0 in > and return 1 in <.
if I need to call prev to reach rhs.node , it return 1 in > and return 0 in <.
And I implement list using circular list. Below is one part of list class :
class List : public ordered_container {
protected:
Node* begin_;
Node* end_;
size_type size_;
public:
List::List() : end_(new Node){
begin_ = end_->prev = end_->next = end_;
size_=0;
}
}
So , I don't know how to distinguish whether I just pass the begin_ of list. Can someone help me about that? Thankyou.
Firstly, using operator< to mean "precedes" is weird. I know it's an exercise, I just don't want you thinking this is normal.
Now, your circular list stores the begin and end in the top-level container object, so there's no way for a node itself to tell whether it's the head, or the trail, or whether a reversal wraps around. Only the container can tell this.
The usual solution for circular lists is to have a sentinel node between head and tail. Then sentinel.next is the head, sentinel.prev is the tail, and you need some way to mark the sentinel itself (either a magic value of data or an extra flag). This sentinel node can then replace the two pointers in your container object (so you're not wasting any space).
When you traverse the list, it's still circular, but you can tell if you pass the sentinel that you've wrapped around.
The sentinel has the additional benefit that you never have to worry about nullptrs in an empty list.
Incidentally, I find it very strange that instructors keep using doubly-linked lists and don't show the sentinel arrangement. It's described within a couple of pages in a copy of Elson's Data Structures book I've inherited, and that was published in 1975. What's the point of deliberately teaching a bad doubly-linked list?
If they wanted you to figure this out for yourself, they ought to have you working through the fundamental list operations rather than these odd precedes/succeeds operators, as the basic operations can actually work without a sentinel but are visibly improved with its addition.
An element a is less than another element b iff you can reach b before end by following next.
An element a is greater than another element b iff b is less than a.
So I have a custom linked list. Every node is a struct which has a next pointer to a the next node and the last->next is null.
struct nodo {
T value;
nodo *next;
nodo() : next(0) {}
nodo(const T &v, nodo *n=0): value(v), next(n){}
};
I want to implement iterators in my class and since my class supports random access with operator[] then I chose to implement Random Access Iterators.
Now, my problem is on the following operator:
difference_type operator-(const iterator &other) {
}
It returns the number of elements between this iterator and other iterator, but I'm not sure what's the best to implement it.
Linked lists are not random access containers. You cannot meaningfully implement random access iteration, nor a conventional operator[](size_t) for regular single or double linked lists.
People who use your list class will not expect random access iteration, and will be confused by it. People who use your "random access" iterators and find out they don't conform to standards and conventions will also be confused.
A RandomAccessIterator is a BidirectionalIterator that can be moved to point to any element in constant time.
Only if your container can access elements by index in constant time should you expose random access interfaces.
In a binary search tree, the end() member function shall simply return iterator(nullptr), right? But the node nullptr contains no information about its left, right, and parent data. So how do we decrement an iterator from end()? The following is what I have so far (defined in the iterator<T> class). My iterator<T> class has node<T>* ptr as its only data member, and my node<T> class has members T value; node *left, *right, *parent;.
iterator& operator--() {
// if (!ptr) what to do???
if (ptr->left)
ptr = node<T>::max_node(ptr->left);
else {
node<T>* previous = ptr;
ptr = ptr->parent;
while (ptr && ptr->left == previous) {
previous = ptr;
ptr = ptr->parent;
}
}
return *this;
}
If you want your iterator to be a bidirectional iterator you’ll need to provide the necessary information to find the last node of the tree in the iterator returned by end(). To avoid dealing with a special case just to decrement the end() iterator you could use a “secret” node which uses its left pointer to point at the root of the tree. This way the end() iterator would simply be a pointer to this secret node: with this approach there is no need for any special treatment in increment, decrement, or comparison.
Since it is generally undesirable to store a node value in the root the usual implementation splits the nodes into a NodeBase holding only the pointer needed for navigation and a NodeValue deriving from the NodeBase and adding the node’s value. The root uses a NodeBase member and the iterator type a NodeBase*: for the navigation only the pointers are needed. Since only valid iterators are allowed to be dereferenced a static_cast<NodeValue*>(n) can be used to obtain the NodeValue when accessing the node’s value.
Instead of end() being iterator(nullptr) make it iterator(&anchor), where anchor is the member of the containing tree that is used to hold the root beginning and final node pointers. And then to decrement you simply step back to the preceding node (the opposite action to increment).
It does require somewhat more work than simply holding a root node pointer but it also allows begin() and end() to be O(1) operations.
In the past I've implemented a linked list using nodes.
I am looking at some of the properties of the standard library list and it has iterators and the appropriate member functions.
What are the iterators in a list exactly? Are they the node pointers?
For a vector, basically you have pointers to the element type and the data structures is built on an underlying dynamic array of that given type.
For the lists, it seems it is just a sequence of nodes, array of nodes. So are the iterators the node pointers rather than a pointer to the node data type?
Basically what I am asking is if for a vector I had this iterator:
tyepdef T* iterator;
Would the iterator for the list be
typedef node* iterator;
where node is something like:
template <class T> struct node {
node() { next = 0; }
node(T i, node* n = 0) : data(i), next(n) {}
node* next;
T data;
}
If this is the case, it seems that operations like dereferencing will have to be overloaded.
The std::list<T>::iterator objects internally point to a node but have operators which appropriately follow the pointers to next or previoys node. That is, they are not pointers as incrementing a pointer just adds one rather than follow a link. You can inagine a list iterator looks somewhat like this:
template <typename T>
class list_iterator {
friend list<T>
Node* node;
list_iterator(T* node):node(node) {}
list_iterator& operator++() {
node = node->next;
return *this;
}
T& operator*() { return *node; }
// ...
};
The iterator on lists should behave similar to the iterators on other sequence containers like vector. I.e. it should behave like a pointer to the list::value_type as if it were in an array or similar (with ++ and -- doing the expected operation going to next and previous). The internals of the holding structure aren't really exposed through the iterator. The iterator abstraction generally frees the programmer from thinking about how the data is stored. In the future, you could theoretically swap your std::list for a std::vector without changing your code, so long as you're only using operations available to both.
My professor has asks us to reimplement all of the LinkedList functions from scratch. However, since this is a beginner class, he has put a few stipulations on it. Below is the full assignment:
"For list, you must implement everything but get_allocator. For all functions that take an "iterator" as an argument, instead accept a node class. For all functions that return an iterator, return a node instead."
Since LinkedList use pointers, I am not sure how to do return a node without just returning a pointer. What would be the syntax for returning a node pointed to by a pointer called *current?
I am also unsure how to format the functions. For example, what would be the syntax for changing the following function to accept a node class and then return a node(i don't need the code for the function, just how to accept a node and return a node class):
iterator insert (iterator position, const int&x)
"For list, you must implement everything but get_allocator. For all
functions that take an "iterator" as an argument, instead accept a
node class. For all functions that return an iterator, return a node
instead."
Since LinkedList use pointers, I am not sure how to do return a node
without just returning a pointer. What would be the syntax for
returning a node pointed to by a pointer called *current?
I think your professor is talking about accepting and returning pointers (or references) to nodes, not node values (copying nodes). I can say that with some degree of confidence because that's the only way to preserve the underlying semantics of std::list with a one-to-one translation of iterators to nodes (node pointers).
However, if your nodes are just mere aggregates storing pointers themselves and don't try to do any memory management, then you can get away with copying them around, but I'd go with pointers if in doubt.
So, something like this:
iterator insert(iterator position, const T& val)
{
...
}
Would look like this for your practice homework:
LinkedListNode* insert(LinkedListNode* position, const T& val)
{
...
}
If you aren't using templates and can just make it a list of integers or whatever:
LinkedListNode* insert(LinkedListNode* position, int val)
{
...
}
Hope later your professor will make you appreciate the power of the iterator-based versions as those allow you to write function templates that work not only on linked lists but any kind of sequence, for example.
I think you shouldn`t use pointers. Try to use reference or iterators?
To dereference a pointer, use the * operator. For example, if you have a valid pointer-to-int, and you want access to use the int that it points to:
*pointer_to_int = 5;
j = *pointer_to_int / 3;
and so forth. Similarly, if you want to pass the value of the pointed-to-int to a function,
k = SomeFunction(*pointer_to_int, 12, foo);
or return it from a function:
int Function() {
return *pointer_to_int;
}
You can use the syntax *pointer anywhere you might otherwise say, variable.
The syntax for declaring functions that return a type doesn't change based on the type. Suppose you have some function that returns an int:
int MyFunction() { return my_int; }
Or, if it returned an iterator:
iterator MyFunction() { return my_iterator; }
Or, if it returned an Apple:
Apple MyFunction() { return my_apple; }
You see, you just need to replace the name of the return type with whatever type you are returning.
Hopefully, you can see now how to declare a function that returns a node object, and how to return a node object, given that you only have a pointer_to_node.