You have done a lot of things correctly, but you are confused on your constructor and on your use of the ->prev
and tail
pointers.
You immediate issue with your constructor, as identified in the comments, is you set head
and tail
to nullptr
and then immediately derefernce both head
and tail
attempting to make head
and tail
self-referencing (which is only needed in a circular linked-list).
list(){ head = nullptr; tail = nullptr; head->next = tail; tail->prev = head;}
With head
and tail
to set to nullptr
, you don't have a pointer to a valid node
than can be dereferenced. Your attempt to set head->next = tail; tail->prev = head;
fails immediately resulting in a SegFault.
For purposes on a normal non-circular list, you simply omit setting head->next
and tail->prev
in your constructor, e.g.
list() { head = nullptr; tail = nullptr; }
If you want to make your list a circular list, then you will make head
and tail
self-referencing in:
node *pushback (int newdata) {
...
if (head == nullptr) /* for circular-list 1st node initialization */
head = tail = head->prev = head->next = tail->prev = tail->next = curr;
(note: a tail
pointer is optional with a circular list as head->prev
always points to the last node in the list)
Since your question pertains to a double-linked-list and not a circular list, you simply need to set both head
and tail
equal to the new node curr
for the addition of the 1st node, e.g.
node *pushback (int newdata) {
node *curr = new node;
curr->data = newdata;
curr->next = curr->prev = nullptr;
if (head == nullptr)
head = tail = curr;
For all other nodes, there is NO iteration required (that's what a tail
pointer is for), you simply set curr->prev
to tail
, tail->next
to curr
and then update the tail
pointer to the new end node by setting tail = curr;
, e.g.
else {
curr->prev = tail;
tail->next = curr;
tail = curr;
}
return head;
}
The purpose of a double-linked-list is to allow you to iterate in both the forward and reverse direction over your nodes. For example:
void printfwd() {
node *iter = head;
while (iter != nullptr) {
std::cout << ' ' << iter->data;
iter = iter->next;
}
std::cout.put('\n');
}
void printrev() {
node *iter = tail;
while (iter != nullptr) {
std::cout << ' ' << iter->data;
iter = iter->prev;
}
std::cout.put('\n');
}
(the iteration scheme is slightly different for a circular list since you can iterate from any node in both forward and reverse direction without needed to start from head
or tail
. To insert in order for a circular list, you simply insert a new tail
node).
Don't develop bad habits. Why is “using namespace std;” considered bad practice? Currently all you have to deal with is cout
and endl
, go ahead and remove using namespace std;
and simply prefix cout
and endl
with std::
.
Putting it altogether, you would have:
#include <iostream>
class list;
class node {
public:
int data;
node *next;
node *prev;
friend class list;
};
class list {//double list
node *head;
node *tail;
public:
list() { head = nullptr; tail = nullptr; }
node *pushback (int newdata) {
node *curr = new node;
curr->data = newdata;
curr->next = curr->prev = nullptr;
if (head == nullptr)
head = tail = curr;
else {
curr->prev = tail;
tail->next = curr;
tail = curr;
}
return head;
}
void printfwd() {
node *iter = head;
while (iter != nullptr) {
std::cout << ' ' << iter->data;
iter = iter->next;
}
std::cout.put('\n');
}
void printrev() {
node *iter = tail;
while (iter != nullptr) {
std::cout << ' ' << iter->data;
iter = iter->prev;
}
std::cout.put('\n');
}
};
int main() {
list test;
for (int i = 1; i <= 10; i++)
test.pushback(i);
std::cout << "\nforward:\n";
test.printfwd();
std::cout << "\nreverse:\n";
test.printrev();
}
Example Use/Output
$ ./bin/ll_double_int
forward:
1 2 3 4 5 6 7 8 9 10
reverse:
10 9 8 7 6 5 4 3 2 1
Look things over and let me know if you have further questions.