Reversing a Doubly-Linked List with Values - c++

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).

Related

Printing elements of a linked list, but instead, it prints reversed C++

here i simply wanted to print the elements of the linked list i created but it is printing the list in REVERSE order. looks like there is a mistake in the code.help me solve it
push function adds node to linked list every time we input element to be inserted in linked list.I've passed reference of head and data. A node gets dynamically created every time push fuction called. I am using c++ here.
#include<iostream>
using namespace std;
class node{
public:
int data;
node* next;
};
//creating linked list
void push(node** head_ref,int new_data) //passing address of head and data to put in list
{
node* new_node=new node(); //new node created
new_node->data=new_data; //data inserted
new_node->next=*(head_ref);
*(head_ref)=new_node;
}
int main()
{
node* head=NULL;
int n;
cin>>n; //number of elements in linked list
for(int i=0;i<n;i++)
{
int val;
cin>>val;
push(&head,val); //push function which creates a linked list
}
//while loop for printing elements of linked list
while(head!=NULL)
{
cout<<head->data;
head=head->next;
}
return 0;
}
What you currently do is assigning each node to be the predecessor of the current head, so in the end your head will be the latest element you added , its successor the second last element, its successor the third last element etc. and thus resulting to a reversed list.
You should assign the new node as successor of the current "head", like this:
void push(node** tail_ref,int new_data) //passing address of tail and data to put in list
{
node* new_node=new node(); //new node created
new_node->data=new_data; //data inserted
(*tail_ref)->next= new_node;
*(tail_ref)=new_node;
}
Note that I renamed head_ref to tail_ref in the snippet above, which better describes what the pointer actually represents: a pointer to the current last element of the list, hence the tail of the list.
Of course, you will need to save the pointer to the first element. Otherwise you won't be able to iterate through your linked list.
Extension to Simon's answer, which is correct so far:
You already have a class 'node' – why not create a class 'list' or 'linked_list' as well:
class LinkedList
{
node* m_head = nullptr;
node* m_tail = nullptr;
};
Now you always have head and tail combined and don't need to store them separately. Note that in above example, they are both private. That's actually how you should design your class. If you don't, then you allow a user to break the list implementation from outside (someone might, for instance, just set one of these pointers to nullptr, producing a memory leak in case of head).
However, now you will have to provide appropriate means to access and modify the list:
class LinkedList
{
public:
void append(int new_data); // as Simon proposed
void prepend(int new_data); // your implementation
int head();
int tail();
void dropHead();
//void dropTail(); // is an O(n) operation with singly linked list, though!
private:
node* m_head = nullptr;
node* m_tail = nullptr;
};
The node class is very closely linked to your list, you might consider not letting it be a stand-alone class, but making it a nested class instead. There's yet quite a bit to add (e. g. how to iterate over the list). To get some hints I recommend to peek a bit into STL and get familiar with the concept of iterators.
Finally: Stop re-inventing the wheel. STL already provides fully implemented doubly (std::list) and singly (std::forward_list) linked lists. It's fine to experiment with an own implementation to get to know how the winds blow, but once you know, switch back to STL.

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.

overload operator [] linked list

template<typename T>
int& listvector<T>::operator[](int n)
{
Node*p = new Node;
p->data = NULL;
for(int i=0;i<n;i++){
p = p->next;
}
p->next = head;
head = p;
return head->data;
}
I am trying to overload a operator[] in my class listvector. This operator[] is used to add the node which is NULL to the list(n is the place that return the value of the list). For example, when in main listV[4] = new int(10), means that NULL NULL NULL NULL 10 in the list. In my code, I don't know why I can not assign NULL to 0-3 only the first one is NULL.
Okay, lets get a working function going.
The first problem is that your node structure seems to be internal, so you can't use the operator[] function to add new nodes. I deduce that because you return the value contained in a node, not the node itself. Using an index-operator to add nodes to a list really makes no sense anyway, since it requires you to create a possibly new list from nothing.
So lets concentrate on a getter version of the function, which gets the value of an existing node in position n. This is very simple:
// Function to get the data in element `n`
int operator[](size_t n) const // Make function `const` since it will not modify the internal data
{
// Start at the head of the list
Node* current = head;
// Loop as long as we don't go of the end of the list and `n` is larger than zero
// Also decrement `n` after checking its value
while (current != nullptr && n-- > 0)
{
// Make `current` point to the next node in the list
current = current->next;
}
// If `current` is a null pointer, then we have gone of the end of the list, return some default value
// Otherwise return the value of node we were looking for
return (current == nullptr ? 0 : current->data);
}
[Note that I have followed your function return type, to return an int even though it should probably be T]

C++ Circular Linked List : remove element

I am done with insertion, search in circular linked list but for removal I am getting compiler errors...
Following is my structure for nodes.
struct node
{
int p_data;
struct node* p_next;
node(node* head, int data)
{
p_next = head;
p_data = data;
}
explicit node(int data)
{
p_next = nullptr;
p_data = data;
}
};
node* remove_circular(node* head, node* target)
{
if (head == target->p_next)
{
delete head;
return nullptr;
}
auto next_pointer = target->p_next;
target->p_data = next_pointer->p_data;
target->p_next = next_pointer->p_next;
delete target->p_next;
return target;
}
and in main function I call
head = remove_circular(head, head);
head = remove_circular(head, temp);
this is to remove head element and another element that temp points to.
But I am getting errors
Anybody has any idea to remove one element from circular list??
I changed it to delete target->p_next;
but now it deletes everything in the list.
Any idea???
This is how a circular linked list works:
Each node points to the next in line, and the tail of the list points to the header node. That's the difference from a circular linked list to a regular linked list (which, in the case above, would make 37 point to a terminator null).
In the case of your list having only one object, then it should look something like this:
So, as you can see, there is no object pointing to null anywhere, yet it happens on your code with your explicit constructor (which will run if I write node n = node(12)).
I suggest you take a look at this link to have a better understanding of how your algorithm should look like.
Once you resolve your compiler error, you are still going to have algorithmic issues. I suggest you draw a circular list on paper and think about the steps required to remove an element. Consider all the cases, for example: empty list, list of 1 item, element not in the list, etc.
You need to consider several things.
1.) the case of an empty list
if(head == nullptr){//Empty list case
return nullptr;
}
2.) The target to be removed is the head node and this is the only node in the list.
if (head == target && target->p_next == head){
create a temp node with the data value of target
target = nullptr;//Since nothing points to target now it is for all intents and purposes deleted from the list but the data is still there so you can do something with it. I assume this is necessary because you return a node *.
return the temp node
}
3.) Create a loop that iterates through the entire list. You have something that would only delete the next node which works if you have a two item list and target was the second item.
auto next_pointer = head->p_next;//could not be target->p_next as this assumed
while (next_pointer->p_next != target){//This while loop traverses the list rather than just deleting the next entry.
4.)Inside you loop add a check to see if the list has been traversed and target was never found.
if (next_pointer->p_next == head){
return nullptr;
}//end IF
5.) Inside the loop add the else case which means target was in an arbitrary location in the list. Since I gave you the rest I'll leave you to get this part. It's not hard just a few lines longer than the statements above.

Delete node from linked list (recursively)

!=I am currently working on the following erase recursive bool function that thakes list and int as arguments and return true if the int was found and deleted and false if it was not found in the list. It seems to work, but the problem is that it deletes the next int number in the list, and not the current:
typedef struct E_Type * List;
struct E_Type
{
int data;
List next = 0;
};
bool erase(const List & l, int data){
List current = l;
if (current == 0)
{
return false;
}
else if (current->data == data)
{
List deleteNode = new E_Type;
deleteNode = current->next;//probably this causes the error, but how can I point it to the current without crashing the program
current->next = deleteNode->next;
delete deleteNode;
return true;
}
else if (current->data != data)
{
return erase(current->next, data);
}
}
There are two basic type of lists:
single-linked lists (each node knows its next node) and
double-linked lists (each node knows its next as well as its previous node).
If, like in your case, one has a single-linked list, you must not check the CURRENT node for equality to 'data', because at that point it is too late to change the next pointer of the last node. So you always have to check the NEXT pointer for equality, like this:
bool erase(const List & l, int data)
{
List current = l;
if (current == 0)
return false;
// special case: node to be deleted is the first one
if (current->data == data)
{
delete current;
return true;
}
if (current->next && current->next->data == data) // next exists and must be erased
{
List deleteNode = current->next; // Step 1: save ptr to next
current->next = deleteNode->next; // Step 2: reassign current->next ptr
delete deleteNode; // Step 3: delete the node
return true;
}
return erase(current->next, data);
}
Note: I spared your last 'else if' condition. The 'else' because the previous if had a return in it, and the 'if' since its condition was just the negation of the previous 'if', which - if the program comes this far - would always hold.
Regards
The only node you're considering is the current one, so you must have a provision for modifying l:
if (current->data == data)
{
l = current->next;
delete current;
return true;
}
Here are some pointers.
An iterative approach
When you're iterating over your list, maintaining a pointer to the current element is not enough. You also need to maintain a pointer to the previous element, since you will need to fix up previous->next if you delete the current element.
On top of that, deleting the first element of the list will require special handling.
A recursive approach
Write a recursive function that will take a pointer to the head of the list, find & delete the required element, and return a pointer to the new head of the list. To do this, you need to:
Define and implement the base case. Handling one-element lists seems like a natural candidate.
Define the recursion. There are two cases: either the head of the list is the element you're looking for, or it isn't. Figure out what you need to do in both cases, and take it from there.
If you have a list:
A --> B --> C --> D
And you want to delete C, you have to:
Store C in a temp variable
Change B->next=C->next
delete C.
So you need to find B to be able to modify it.
You should certainly not create any new instance of E_type.
Your condition
else if (current->data == data)
will stop on the node which has the value data. You then go on to delete the node after this node in your code.
If you want to keep the rest of the code same, then that line should be :
else if ((current->next)->data == data)
with an extra check, in case the first element is the only element in the list.
A simpler way would be to keep a pointer that points to the element before the current element, and then deleting the node which is referred by that pointer.
You will need to change the next pointer of the preceding entry. So everything is find, but you have to check current->next->data against data, not current->data.
Be sure to check for NULL-pointers in case current is the last entry in the list!
When you delete a node from a list, you need to point the previous node to the next one. Since you have a singly linked list, there are 2 options:
Maintain a pointer to previous node in your erase function. When encountering desired node, link previous node to current->next and delete current node. Needs special treatment for the first node in the list.
When you encounter desired node, copy the content of the current->next into current, then delete current->next. This way you don't need an extra parameter in your function. Needs special treatment for the last node in the list.