How to implement erase method in a doubly linked list - c++

I have to write a doubly linked list, I am trying to implement the erase(Type obj) method which takes an argument obj and traverses through the list and deletes every node which has the element obj.
The problem I am facing is, I am iterating through the linked list from front and when I find the node which has the obj element, I change the next/previous pointers of the the nodes before and after the node with obj element. However I am not DELETING the node with obj itself, as far as I know c++ has no garbage collection so the node which had obj is still somewhere hanging in the air. How do I delete that?
My erase()
template <typename Type>
int Double_list<Type>::erase( Type const &obj ) {
if (empty()){
return 0;
}
if (size() == 1 && head()->retrieve() == obj){
list_head = nullptr;
list_tail = nullptr;
list_size--;
return 1;
}
//Counter to hold the number of items deleted
int count = 0;
//Iterating through the linked list
for (Double_node<Type> *ptr = head(); ptr != nullptr; ptr = ptr->next()){
if (ptr->retrieve() == obj){
ptr->previous_node->next_node = ptr->next();
ptr->next()->previous_node = ptr->previous();
count++;
// delete ptr; // This stops me from iterating through the for loop
list_size--;
}
}
return count;
}

While you traversing your list, you are doing it using pointer to nodes which is of type Double_node<Type> *, which means that it was allocated somewhere and could be deleted with simple delete ptr, but since you are using it also to get next element in list you have to be careful and remember it prematurely, so it should be something like:
Double_node<Type> *ptr_next = 0;
for (Double_node<Type> *ptr = head(); ptr != nullptr; ptr = ptr_next) {
ptr_next = ptr->next ();
if (ptr->retrieve() == obj){
if (ptr->previous_node)
ptr->previous_node->next_node = ptr->next();
ptr->next()->previous_node = ptr->previous();
count++;
list_size--;
delete ptr;
}
I believe that should do the trick.

There is a syntax when deleting pointers and objects called delete.
example code:
obj *temp = getCurrentNode();
//set pointers in nodes to the correct places
delete temp; //This releases all of the memory used in the object back to the OS
temp = NULL;//good practice to set this to null, since it points to non-allocated memory
//but exiting the function will make the pointer 'temp' useless, anyway.

Related

Passing a linked list without memory leak in C++

In many occasions, we need to modify a linked list drastically so we will sometimes create another linked list and pass it to the old one. For example,
struct node { //let's say we have a linked list storing integers
int data;
node* next;
};
and suppose we already have a linked list storing integers 1,2,3.
node* head; //suppose we already store 1,2,3 in this linked list
node* new_head ; //suppose we make a temporary linked list storing 4,5,6,7
head = new_head; //modifying the original linked list
My Question
If I delete head (the old linked list) before the assignment then the whole program will crash.
Conversely, if I do not delete it, then there will be a memory leak.
Therefore, I am looking for a way to modify the linked list without memory leak.
My attempt
I tried to make a helper function similar to strcpy to do my work.
void passing_node(node*& head1, node* head2){ //copy head2 and paste to head1
node* ptr1 = head1;
for (node* ptr2 = head; ptr2 != nullptr; ptr2 = ptr2->next)
{
if (ptr1 == nullptr){
ptr1 = new node;
}
ptr1->data = ptr2->data;
ptr1 = ptr1->next;
}
}
// note that we assume that the linked list head2 is always longer than head1.
However, I still got a crash in the program and I cannot think of any other way to modify this. Any help would be appreciated.
Easier way to avoid memory leak is to avoid raw owning pointers.
You might use std::unique_ptr (or rewrite your own version):
struct node {
int data = 0;
std::unique_ptr<node> next;
};
You can move nodes.
You can no longer copy nodes (with possible double free issue).
so deep_copy might look like:
std::unique_ptr<Node> deep_copy(const Node* node)
{
if (node == nullptr) return nullptr;
auto res = std::make_unique<Node>();
res->data = node->data;
res->next = deep_copy(node->next.get());
return res;
}
I would suggest preallocating the linked list so it's easy to delete every node in one call. The nodes would then just reference somewhere inside this preallocated memory. For example:
struct Node
{
int value;
Node* next;
};
struct LinkedList
{
Node* elements;
Node* first;
Node* last;
Node* free_list;
LinkedList(size_t size)
{
first = nullptr;
last = nullptr;
elements = new Node[size]{0};
free_list = elements;
for (size_t i = 0; i < size-1; ++i)
free_list[i].next = &free_list[i+1];
free_list[count-1].next = nullptr;
}
~LinkedList()
{
delete[] elements;
}
void Add(int value)
{
if (free_list == nullptr)
// Reallocate or raise error.
// Take node from free_list and update free_list to
// point to the next node in its list.
// Update last node to the new node.
// Update the first node if it's the first to be added.
}
void Free(Node* node)
{
// Search for the node and update the previous and
// next's pointers.
// Update first or last if the node is either of them.
// Add the node to the last place in the free_list
}
};
From here you'll have many strategies to add or remove nodes. As long as you make sure to only add nodes to the allocated elements array, you'll never have any memory leak. Before adding, you must check if the array have the capacity to add one more node. If it doesn't, you either have to raise an error, or reallocate a new the LinkedList, copy over all values, and delete the old one.
It becomes a bit more complicated when the array becomes fragmented. You can use a 'free list' to keep track of the deleted nodes. Basically, a LinkedList of all nodes that are deleted.
Just take notice that my code is incomplete. The basic approach is to create an allocator of some sort from which you can allocate a bulk, use segments of it, and then delete in bulk.

Reversing a Doubly-Linked List with Values

I am currently unable to get a reverse function of a doubly linked list to properly work for an assignment, I've read up the other threads and searched on google but usually the difference is my problem passes in a constant and that it returns a "dlist". The professor has provided a "code tester" and it says that my code when doing "reverse(reverse(dlist c))" it's not equal to itself being "c". [Reversing it twice does not equal itself].
The dlist class is:
class dlist {
public:
dlist() { }
int sizeOfDlist =0; // To keep track of size
struct node {
int value;
node* next;
node* prev;
};
node* head() const { return _head; } // _head = beginning of list
node* tail() const { return _tail; } // _tails = end of list
node* _head = nullptr;
node* _tail = nullptr;
And here's the reverse function:
dlist reverse(const dlist& l){
if(l._head == nullptr||l._tail ==nullptr){ // Checks if l list is empty
dlist newRList;
return newRList;//return a blank list;
}
if(l.head()!=nullptr){
dlist::node* temp;
dlist::node* ptr1 = l._head;
dlist::node* previous = nullptr;
while(ptr1 != nullptr){
temp = ptr1->next;
ptr1->next = previous;
previous = ptr1;
ptr1 = temp;
}
dlist newRList;
newRList._head = previous;
return newRList;
}
else //if something passes by, return original list
return l;
}
Each dlist node has a pointer pointing towards the previous node and a pointer pointing towards the next node. The dlist node also contains an int value.
What I tried to implement was creating a list that starts at original list's "tail" or end. The list would then go backwards and swap the "next" and "prev" pointers as it goes along. What am I doing wrong?
Solved: By using a push_front function which adds a value to the front of a list and pushing everything else behind it, I was able to grab the values from the given constant dlist, and push_front all of the values into "newRList" which reverses the order.
Thanks to user4581301 and Basya Perlman for helping me out, here's the new reverse function:
dlist reverse(const dlist& l){
if(l._head == nullptr||l._tail ==nullptr){ // Checks if l list is empty
dlist newRList;
return newRList;//return a blank list;
}
if(l.head()!=nullptr){
dlist newRList;
for(int n=0; n<l.size(); n++){ // Size function checks the size of the doubly linked list
newRList.push_front(l.valueGetter(n)); // Value Getter is a function that grabs the value at a specific [iteration], push_front pushes said value into the front of the list.
}
return newRList;
}
else //if something passes by, return original list
return l;
}
Your reverse function looks like it is set up to return a new dlist. It returns an object, not a pointer or a reference.
Also, your parameter is a const dlist, yet you are trying to reverse it in-place, and then point a new pointer to the head of the list and return that. Then the tester is comparing the returned list to the original list; but the original list, which was meant to be const, but which was modified? I am a bit confused, so perhaps the computer running your program is too :-)
From the function definition, it looks as though the idea is to create a new list by copying the elements into the new list in reverse order, and leave the original list unchanged. In your comment, you have a push_back and a push_front function; you can loop forward through your existing list and push_front a copy of each element into the new list, to reverse it (whether you need to explicitly make a copy or not depends on the definition of the push_front function, which I do not have).

Pointers and reference issue

I'm creating something similar to structure list. At the beginning of main I declare a null pointer. Then I call insert() function a couple of times, passing reference to that pointer, to add new elements.
However, something seems to be wrong. I can't display the list's element, std::cout just breaks the program, even though it compiler without a warning.
#include <iostream>
struct node {
node *p, *left, *right;
int key;
};
void insert(node *&root, const int key)
{
node newElement = {};
newElement.key = key;
node *y = NULL;
std::cout << root->key; // this line
while(root)
{
if(key == root->key) exit(EXIT_FAILURE);
y = root;
root = (key < root->key) ? root->left : root->right;
}
newElement.p = y;
if(!y) root = &newElement;
else if(key < y->key) y->left = &newElement;
else y->right = &newElement;
}
int main()
{
node *root = NULL;
insert(root, 5);
std::cout << root->key; // works perfectly if I delete cout in insert()
insert(root, 2);
std::cout << root->key; // program breaks before this line
return 0;
}
As you can see, I create new structure element in insert function and save it inside the root pointer. In the first call, while loop isn't even initiated so it works, and I'm able to display root's element in the main function.
But in the second call, while loop already works, and I get the problem I described.
There's something wrong with root->key syntax because it doesn't work even if I place this in the first call.
What's wrong, and what's the reason?
Also, I've always seen inserting new list's elements through pointers like this:
node newElement = new node();
newElement->key = 5;
root->next = newElement;
Is this code equal to:
node newElement = {};
newElement.key = 5;
root->next = &newElement;
? It would be a bit cleaner, and there wouldn't be need to delete memory.
The problem is because you are passing a pointer to a local variable out of a function. Dereferencing such pointers is undefined behavior. You should allocate newElement with new.
This code
node newElement = {};
creates a local variable newElement. Once the function is over, the scope of newElement ends, and its memory gets destroyed. However, you are passing the pointer to that destroyed memory to outside the function. All references to that memory become invalid as soon as the function exits.
This code, on the other hand
node *newElement = new node(); // Don't forget the asterisk
allocates an object on free store. Such objects remain available until you delete them explicitly. That's why you can use them after the function creating them has exited. Of course since newElement is a pointer, you need to use -> to access its members.
The key thing you need to learn here is the difference between stack allocated objects and heap allocated objects. In your insert function your node newElement = {} is stack allocated, which means that its life time is determined by the enclosing scope. In this case that means that when the function exits your object is destroyed. That's not what you want. You want the root of your tree to stored in your node *root pointer. To do that you need to allocate memory from the heap. In C++ that is normally done with the new operator. That allows you to pass the pointer from one function to another without having its life time determined by the scope that it's in. This also means you need to be careful about managing the life time of heap allocated objects.
Well you have got one problem with your Also comment. The second may be cleaner but it is wrong. You have to new memory and delete it. Otherwise you end up with pointers to objects which no longer exist. That's exactly the problem that new solves.
Another problem
void insert(node *&root, const int key)
{
node newElement = {};
newElement.key = key;
node *y = NULL;
std::cout << root->key; // this line
On the first insert root is still NULL, so this code will crash the program.
It's already been explained that you would have to allocate objects dynamically (with new), however doing so is fraught with perils (memory leaks).
There are two (simple) solutions:
Have an ownership scheme.
Use an arena to put your nodes, and keep references to them.
1 Ownership scheme
In C and C++, there are two forms of obtaining memory where to store an object: automatic storage and dynamic storage. Automatic is what you use when you declare a variable within your function, for example, however such objects only live for the duration of the function (and thus you have issues when using them afterward because the memory is probably overwritten by something else). Therefore you often must use dynamic memory allocation.
The issue with dynamic memory allocation is that you have to explicitly give it back to the system, lest it leaks. In C this is pretty difficult and requires rigor. In C++ though it's made easier by the use of smart pointers. So let's use those!
struct Node {
Node(Node* p, int k): parent(p), key(k) {}
Node* parent;
std::unique_ptr<Node> left, right;
int key;
};
// Note: I added a *constructor* to the type to initialize `parent` and `key`
// without proper initialization they would have some garbage value.
Note the different declaration of parent and left ? A parent owns its children (unique_ptr) whereas a child just refers to its parent.
void insert(std::unique_ptr<Node>& root, const int key)
{
if (root.get() == nullptr) {
root.reset(new Node{nullptr, key});
return;
}
Node* parent = root.get();
Node* y = nullptr;
while(parent)
{
if(key == parent->key) exit(EXIT_FAILURE);
y = parent;
parent = (key < parent->key) ? parent->left.get() : parent->right.get();
}
if (key < y->key) { y->left.reset(new Node{y, key}); }
else { y->right.reset(new Node{y, key}); }
}
In case you don't know what unique_ptr is, the get() it just contains an object allocated with new and the get() method returns a pointer to that object. You can also reset its content (in which case it properly disposes of the object it already contained, if any).
I would note I am not too sure about your algorithm, but hey, it's yours :)
2 Arena
If this dealing with memory got your head all mushy, that's pretty normal at first, and that's why sometimes arenas might be easier to use. The idea of using an arena is pretty general; instead of bothering with memory ownership on a piece by piece basis you use "something" to hold onto the memory and then only manipulate references (or pointers) to the pieces. You just have to keep in mind that those references/pointers are only ever alive as long as the arena is.
struct Node {
Node(): parent(nullptr), left(nullptr), right(nullptr), key(0) {}
Node* parent;
Node* left;
Node* right;
int key;
};
void insert(std::list<Node>& arena, Node *&root, const int key)
{
arena.push_back(Node{}); // add a new node
Node& newElement = arena.back(); // get a reference to it.
newElement.key = key;
Node *y = NULL;
while(root)
{
if(key == root->key) exit(EXIT_FAILURE);
y = root;
root = (key < root->key) ? root->left : root->right;
}
newElement.p = y;
if(!y) root = &newElement;
else if(key < y->key) y->left = &newElement;
else y->right = &newElement;
}
Just remember two things:
as soon as your arena dies, all your references/pointers are pointing into the ether, and bad things happen should you try to use them
if you ever only push things into the arena, it'll grow until it consumes all available memory and your program crashes; at some point you need cleanup!

Removing a node from a linked list in c++

I'm trying to learn C++ and there is a small confusion I have.
The text which I am learning from tells me that if I want to delete a node of type const T& I should first create a new pointer of that node type, then delete it using the inbuilt C++ delete[]. However, what happens if I just set the link from the to-be-deleted node's previous element to the to-be-deleted node's next element? Something like:
*p = node.previous;
p-> next = node.next;
Or will this cause a memory leak?
I'm confused because I read somewhere else to never, ever delete pointers willy-nilly, but the example code I am working with has something along the lines of:
Node<T> *p = node-to-be-deleted;
delete p;
What is the best way to delete the node?
Assuming your node looks like this:
struct Node
{
Node* previous;
Node* next;
SomeType data;
};
Then:
*p = node.previous;
p-> next = node.next;
Then YES. This will cause a memory leak.
It also leaves p->next->prev pointing at the wrong node.
I'm confused because I read somewhere else to never, ever delete pointers willy-nilly, but the example code I am working with has something along the lines of:
Yes the best way is to "never delete pointers". But this has to go along with some context. You should not be deleting pointers manually because pointers should be managed by an objects that control their lifespan. The simplest of these objects are smart pointers or containers. But for this situation that would be overkill (as you are creating the container).
As you are creating the container (a list) you will need to do the management yourself (Note C++ already has a couple of lost types std::list for a list of values of type t or boost::ptr_list for a list of pointers to T). But it is a good exercise to try and do it yourself.
Here is an example on code review of a beginner making a list and the comments it generated:
http://codereview.stackexchange.com: Linked list in C++
I hope this helps in explains on how to create and delete objects.
Node* p = new Node; // This is how you allocate a node
delete p; // This is how you delete it
The delete[] operator should be used on dynamically allocated arrays:
Node* nodelist = new Node[ 4 ]; // nodelist is now a (dynamically allocated) array with 4 items.
delete[] nodelist; // Will delete all 4 elements (which is actually just one chunk of memory)
Deleting a Node directly only makes sense if Node implements a destructor to update the previous and next pointers of the surrounding Node instances, eg:
Node::~Node()
{
if (previous) previous->next = next;
if (next) next->previous = previous;
}
Node *p = node-to-be-deleted;
delete p;
Otherwise, you have to update the Node pointers before then deleting the Node in question, eg:
Node *p = node-to-be-deleted;
if (p->previous) p->previous->next = p->next;
if (p->next) p->next->previous = p->previous;
delete p;
With that said, the best approach is to no implement a linked list manually to begin with. In C++, use a std::list container instead, and let it handle these details for you.
void deleteNode( Node * p )
{
Node * temp = p->next;
p->data = p->next->data;
p->next = temp->next;
free(temp);
}
Heres something i did a few months ago.
template <class T>
T LinkedList<T>::remove(int pos)
{
if (pos < 1 || pos > size)
{
throw pos;
}
ListNode * temp;
if (pos == 1)
{
temp=head;
head = head->next;
}
else
{
int i=1;
ListNode * prev = head;
while(i<pos-1)
{
i++;
prev=prev->next;
}
temp = prev->next;
prev->next = (prev->next)->next;
}
--size;
return temp->item;
}

Coding a function to copy a linked-list in C++

I need to implement an auxilliary function, named copyList, having one parameter, a pointer to a ListNode. This function needs to return a pointer to the first node of a copy of original linked list. So, in other words, I need to code a function in C++ that takes a header node of a linked list and copies that entire linked list, returning a pointer to the new header node. I need help implementing this function and this is what I have right now.
Listnode *SortedList::copyList(Listnode *L) {
Listnode *current = L; //holds the current node
Listnode *copy = new Listnode;
copy->next = NULL;
//traverses the list
while (current != NULL) {
*(copy->student) = *(current->student);
*(copy->next) = *(current->next);
copy = copy->next;
current = current->next;
}
return copy;
}
Also, this is the Listnode structure I am working with:
struct Listnode {
Student *student;
Listnode *next;
};
Note: another factor I am running into with this function is the idea of returning a pointer to a local variable.
The first question you need to ask yourself is what the copy semantics are. In particular, you're using a Student* as node contents. What does copying node contents mean? Should we copy the pointer so that the two lists will point to (share) the same student instances, or should you perform a deep copy?
struct Listnode {
Student *student; // a pointer? shouldn't this be a `Student` object?
Listnode *next;
};
The next question you should ask yourself is how you will allocate the nodes for the second list. Currently, you only allocate 1 node in the copy.
I think you code should look more like:
Listnode *SortedList::copyList(Listnode *L) {
Listnode *current = L;
// Assume the list contains at least 1 student.
Listnode *copy = new Listnode;
copy->student = new Student(*current->student);
copy->next = NULL;
// Keep track of first element of the copy.
Listnode *const head = copy;
// 1st element already copied.
current = current->next;
while (current != NULL) {
// Allocate the next node and advance `copy` to the element being copied.
copy = copy->next = new Listnode;
// Copy the node contents; don't share references to students.
copy->student = new Student(*current->student);
// No next element (yet).
copy->next = NULL;
// Advance 'current' to the next element
current = current->next;
}
// Return pointer to first (not last) element.
return head;
}
If you prefer sharing student instances between the two lists, you can use
copy->student = current->student;
instead of
copy->student = new Student(*current->student);
This is an excellent question since you've done the bulk of the work yourself, far better than most "please do my homework for me" questions.
A couple of points.
First, what happens if you pass in an empty list? You probably want to catch that up front and just return an empty list to the caller.
Second, you only allocate the first node in the copy list, you need to do one per node in the original list.
Something like (pseudo-code (but C++-like) for homework, sorry):
# Detect empty list early.
if current == NULL:
return NULL;
# Do first node as special case, maintain pointer to last element
# for appending, and start with second original node.
copy = new node()
last = copy
copy->payload = current->payload
current = current->next
# While more nodes to copy.
while current != NULL:
# Create a new node, tracking last.
last->next = new node()
last = last->next
# Transfer payload and advance pointer in original list.
last->payload = current->payload
current = current->next
# Need to terminate new list and return address of its first node
last->next = NULL
return copy
And, while you're correct that you shouldn't return a pointer to a local stack variable, that's not what you're doing. The variable you're returning points to heap-allocated memory, which will survive function exit.
I have been trying to do the same thing. My requirements were:
1. Each node is a very basic and simple class (I moved away from the struct model).
2. I want to create a deep copy, and not just a pointer to the old linked list.
The way that I chose to do this is with the following C++ code:
template <class T>
Node <T> * copy(Node <T> * rhs)
{
Node <T> * current = new Node<T>();
Node <T> * pHead = current;
for (Node <T> * p = rhs; p; p = p->pNext)
{
Node <T> * prev = current;
prev->data = p->data;
if (p->pNext != NULL)
{
Node <T> * next = new Node<T>();
prev->pNext = next;
current = next;
}
else
{
prev->pNext = NULL;
}
}
return pHead;
}
This works well, with no errors. Because the "head" is a special case, there is a need for my implementation of a "current" pointer.
The statement
copy->next = current->next
is wrong. You should do
Create the first node copy here
copy->student = current->student;
copy->next = NULL;
while(current->next!=NULL)
{
Create new node TEMP here
copy->next = TEMP;
TEMP->student = current->student;
TEMP->next = NULL;
copy = TEMP;
}
Since you need a copy of the linked list, you need to create a new node in the loop while traversing through the original list.
Listnode *startCopyNode = copy;
while (current != NULL) {
*(copy->student) = *(current->student);
copy->next = new Listnode;
copy = copy->next;
current = current->next;
}
copy->next = NULL;
return startCopyNode;
Remember to delete the nodes of linked list.
#pat, I guess you will get a seg_fault, because you create memory only once. You need to create memory(basically call 'new') for each and every node. Find out, where you need to use the 'new' keyword, to create memory for all the nodes.
Once you are done with this, you need to link it to the previous node, since its a singly linked list, you need to maintain a pointer to the previous node. If you want to learn and should be able to remember all life, don't see any of the code mentioned above. Try to think the above mentioned factors and try to come up with your own code.
As others have pointed out, you need to call new for each node in the original list to allocate space for a copy, then copy the old node to the new one and update the pointer in the copied node.
another factor I am running into with this function is the idea of returning a pointer to a local variable.
You are not returning a pointer to a local variable; when you called new, you allocated memory on the heap and are returning a pointer to that (which of course means that you need to remember to call delete to free it when you are done with the new list, from outside the function).