c++ doubly linked list search and remove - c++

I'm having trouble looking for a node in my list and then deleting it. I've tried so many ways but this is my code so far. I don't know if the problem is in my insert or maybe in my display function? I"m barely learning this.
void removeStudent(int id)
{
node *trash = NULL;
node *current = head;
while ( current!= NULL)
{
if ( current->data.id == id)
{
trash = current;
current->prev->next = current->next;
current->next->prev = current->prev;
delete trash;
}
}
}
this is my insert function
void push(student s)
{
node *tmp = new node;
tmp->data = s;
tmp->next = head;
tmp->prev = NULL;
if (head == NULL)
{
head = tmp;
tail = tmp;
}
else
{
head->prev = tmp;
head = tmp;
}
}
and this is my display function
void display()
{
node *current = head;
while (current!=NULL)
{
cout << current->data.name << endl;
cout << current->data.GPA << endl;
cout << current->data.id << endl;
cout << current->data.university << endl;
current = current->next;
}
}

Try the following. I suppose that your class contains also data member tail.
void removeStudent( int id )
{
node *current = head;
while ( current != NULL && current->data.id != id ) current = current->next;
if ( current != NULL )
{
if ( current->prev != NULL ) current->prev->next = current->next;
else head = current->next;
if ( current->next != NULL ) current->next->prev = current->prev;
else tail = current->prev;
delete current;
}
}
If your class has no data member tail then you have to remove statement
else tail = current->prev;
from the function body.

current = current->next; at the end of the loop?

node *trash = new node;
trash = current;
memory leak, because you are loosing the memory allocate by newin first line and you do not keep a pointer to that memory. You assign the pointer trash the current one.
You should just do that:
node *trash = NULL; // declare a pointer and set it to NULL
HINT:
When dealing with list, in order to test your code use a paper and a pencil, create a small list and run in the paper the cases of modifying (here deleting) a node from the start and the end of the list.
Also, what happens when the list has only one node and what happens when it is empty.

Related

Adding a node to the end of a linked list but not able to access the last node

I have been having troubles adding a new node at the end of a singly linked-list.
When I print the list out, I don't seem to be able to access the last node.
if (head == nullptr)
{
head = newNode;
}
else
{
record* current = head;
while (current->next != nullptr)
{
current = current->next;
}
current->next = newNode;
}
//Writing data to record.
newNode->name = name;
newNode->highscore = highscore;
newNode->initials = initials;
newNode->plays = plays;
newNode->revenue = revenue;
record* test = head;
while (test->next != nullptr)
{
cout << test->name << endl;
test = test->next;
}
You need to add nullptr after adding a new node to the list. Also, you should print the list till you reach nullptr
if (head == nullptr)
{
head = newNode;
}
else
{
record* current = head;
while (current->next != nullptr)
{
current = current->next;
}
current->next = newNode;
newNode->next = nullptr;
}
//Writing data to record.
newNode->name = name;
newNode->highscore = highscore;
newNode->initials = initials;
newNode->plays = plays;
newNode->revenue = revenue;
record* test = head;
while (test != nullptr)
{
cout << test->name << endl;
test = test->next;
}
While the other answer is good and necessary, I think it can be handled in a simpler manner. Here's an example Node.
struct Node {
int data;
Node* next = nullptr;
Node(int val) : data(val) {}
}
By default-initializing the pointer to nullptr, you don't have to constantly worry about it throughout your list class implementation. Every declared Node will automatically set the pointer to nullptr for you.

Deleting node in linked list causes output of seemingly random nubmers

I am trying to delete a node from a linked list using this function:
void del_node(int del_data)
{
node* temp = NULL;
node* trail = NULL;
node* del_ptr = NULL;
temp = head;
trail = head;
while (temp != NULL && temp->data != del_data)
{
trail = temp;
temp = temp->next;
}
if (temp != NULL) {
del_ptr = temp;
temp = temp->next;
trail->next = temp;
delete(del_ptr);
}
}
It seems like it deletes it fine until i print the linked list using this:
void print()
{
node* temp = NULL;
temp = head;
while (temp != NULL)
{
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
and it starts outputting seemingly random numbers, can anybody help me with this, really confused as this code comes from a tutorial.
Your algorithm doesn't manage the head pointer correctly whatsoever. Any changes that ultimately should modify the head pointer don't, and that's a huge problem. A pointer to pointer algorithm not only solves this problem, it also delivers a considerably more succinct solution:
void del_node(int del_data)
{
struct node **pp = &head;
while (*pp && (*pp)->data != del_data)
pp = &(*pp)->next;
if (*pp)
{
node *tmp = *pp;
*pp = tmp->next;
delete tmp;
}
}
This will work for any list condition including:
An empty list. i.e. head is null.
A single-node list. If the value matches head->data it will properly delete and reset the node pointer.
A multi-node list. The first matching node will be removed, and it will properly fix up the head node pointer if that was the matching location.
All of the above, in cases where there is no matching node, the list remains unchanged.
Fulfilling all of that in such a short algorithm + implementation is beneficial.
I'll comment on your code inline:
void del_node(int del_data)
{
node* temp = NULL;
node* trail = NULL;
node* del_ptr = NULL;
temp = head;
trail = head;
// This is fine, but recommend you use nullptr instead of NULL.
// This will find the first instance of data matches del_data,
// But if you are trying to delete all instances of del_data,
// You'll need to do this a little differently.
while (temp != NULL && temp->data != del_data)
{
trail = temp;
temp = temp->next;
}
// This if is fine, but see previous comment about using nullptr
// instead of NULL.
if (temp != NULL) {
del_ptr = temp;
temp = temp->next;
// Problematic: What if trail is null?
trail->next = temp;
delete(del_ptr);
}
}
Your code isn't bad. I wouldn't have written exactly like this, but I'm going to replace your if-statement:
if (temp != nullptr) {
// If trail is nullptr, then we're deleting from the head
if (trail == nullptr) {
head = temp->next;
}
else {
trail->next = temp->next;
}
delete(temp);
}
There's no need for the temporary. Just point around temp as you see in the if-else block and then delete temp.

Unable to create or return Reversed Linked list

Here using the function returnReverseLinkedList I am returning the reversed linked list of the given linked list. But the problem with this approach is that i lose the original linked list. So I make another fucntion called createReversedLinkedList to make a copy of the original linked list and reverse the copy and maintain possession of both.
unfortunately createReversedLinkedList is giving Runtime error.
obviously my end goal is to check if the given linked list is palindrome or not. This issue is just a stepping stone.
Could someone tell me why?
//Check if a linked list is a palindrome
#include <iostream>
using namespace std;
class node
{
public:
int data;
node *next;
node(int data)
{
this->data = data;
this->next = NULL;
}
};
node *returnReverseLinkedList(node *head)
{
// Will Lose original Linked List
if (head == NULL)
return NULL;
else if (head != NULL && head->next == NULL)
return head;
node *prev = NULL;
node *curr = head;
node *tempNext = head->next;
while (tempNext != NULL)
{
curr->next = prev;
prev = curr;
curr = tempNext;
tempNext = tempNext->next;
}
curr->next = prev;
return curr;
}
node *createReversedLinkedList(node *head)
{
if (head == NULL)
return NULL;
else if (head != NULL && head->next == NULL)
return NULL;
else
{
node *temp = head;
node *newHead = NULL;
node *newTail = NULL;
while (temp != NULL)
{
node *newNode = new node(temp->data);
if (newHead == NULL)
{
newHead = newNode;
newTail = newNode;
}
else
{
newTail->next = newNode;
newTail = newNode;
}
}
return returnReverseLinkedList(newHead);
}
}
bool check_palindrome(node *head)
{
node *original = head;
node *reverse = returnReverseLinkedList(head);
while (original->next != NULL || reverse->next != NULL)
{
if (original->data != reverse->data)
return false;
cout << "debug 2" << endl;
original = original->next;
reverse = reverse->next;
}
return true;
}
// #include "solution.h"
node *takeinput()
{
int data;
cin >> data;
node *head = NULL, *tail = NULL;
while (data != -1)
{
node *newnode = new node(data);
if (head == NULL)
{
head = newnode;
tail = newnode;
}
else
{
tail->next = newnode;
tail = newnode;
}
cin >> data;
}
return head;
}
void print(node *head)
{
node *temp = head;
while (temp != NULL)
{
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
int main()
{
node *head = takeinput();
node *revese2 = createReversedLinkedList(head);
print(revese2);
// bool ans = check_palindrome(head);
// if (ans)
// cout << "true";
// else
// cout << "false";
// return 0;
}
As asked by the OP, building a reversed linked is simply done by building as you would a stack (e.g LIFO) rather than duplicating the same original forward chain. For example:
node *createReversedLinkedList(const node *head)
{
node *newHead = NULL;
for (; head; head = head->next)
{
node *p = new node(head->data)
p->next = newHead;
newHead = p;
}
return newHead;
}
Note we're not hanging our copied nodes on the tail of the new list; they're hanging on the head of the new list, and becoming the new head with each addition. That's it. There is no need to craft an identical list, then reverse it; you can reverse it while building the copy to begin with.
A note on the remainder of your code. You have a dreadful memory leak, even if you fix the reversal generation as I've shown above. In your check_palindrome function, you never free the dynamic reversed copy (and in fact, you can't because you discard the original pointer referring to its head after the first traversal:
bool check_palindrome(node *head)
{
node *original = head;
node *reverse = returnReverseLinkedList(head); // only reference to reversed copy
while (original->next != NULL || reverse->next != NULL)
{
if (original->data != reverse->data)
return false; // completely leaked entire reversed copy
original = original->next;
reverse = reverse->next; // lost original list head
}
return true;
}
The most obvious method for combating that dreadful leak is to remember the original list and use a different pointer to iterate, and don't leave the function until the copy is freed.
bool check_palindrome(const node *head)
{
bool result = true;
node *reverse = returnReverseLinkedList(head);
for (node *p = reverse; p; p = p->next, head = head->next)
{
if (p->data != head->data)
{
result = false;
break;
}
}
while (reverse)
{
node *tmp = reverse;
reverse = reverse->next;
delete tmp;
}
return result;
}

Singly Linked List Infinite Loop

It's been a week since i started learning about linked list and i only managed to learn about singly linked list. So today i implemented the linked list which i learned in c++ and while i tried to run it the code goes into an infinite loop of some random numbers. I tried debugging the code but i coudn't find whats so ever is wrong with the code. The code is below. Help is appreciated.Thanks
#include <iostream>
using namespace std;
struct node{
int data;
node * next;
};
class singly{
private:
node * head,*tail;
public:
singly(){
head=NULL;
tail=NULL;
}
void createNode(int value){
node * temp = new node;
temp->data=value;
temp->next=NULL;
if(head==NULL){
head=temp;
tail=temp;
temp=NULL;
}
else{
tail->next=temp;
tail=temp;
}
}
void display(){
node * temp = new node;
head=temp;
while(temp!=NULL){
cout << temp->data << "\t" << endl;
temp->next=temp;
}
}
void insert_end(int value){
node*newnode = new node;
node*temp = new node;
newnode->data=value;
newnode->next=NULL;
temp=head;
while(temp->next!=NULL){
temp = temp->next;
}
temp->next=newnode;
}
void delete_node(){
node*current = new node;
node*previous = new node;
current = head;
while(current->next!=NULL){
previous=current;
current=current->next;
}
tail=previous;
previous->next=NULL;
delete current;
}
};
int main(){
singly lists;
lists.createNode(32);
lists.createNode(654);
lists.createNode(34);
lists.createNode(234);
cout<<"\n--------------------------------------------------\n";
cout<<"---------------Displaying All nodes---------------";
cout<<"\n--------------------------------------------------\n";
lists.display();
cout<<"\n--------------------------------------------------\n";
cout<<"-----------------Inserting At End-----------------";
cout<<"\n--------------------------------------------------\n";
lists.createNode(55);
lists.display();
cout<<"\n--------------------------------------------------\n";
cout<<"-----------------Deleing At End-------------------";
cout<<"\n--------------------------------------------------\n";
lists.delete_node();
lists.display();
}
The member function display does not make sense.
It overwtites the data member head with uninitialized newly created temp.
node * temp = new node;
head=temp;
so the function invokes undefined behavior.
The function can look like
void display()
{
for ( node * temp = head; temp != nullptr; temp = temp->next )
{
cout << temp->data << "\t";
}
}
Or it is better to define it the following way
std::ostream & display( std::ostream &os = std::cout )
{
for ( node * temp = head; temp != nullptr; temp = temp->next )
{
os << temp->data << "\t";
}
return os;
}
The data member insert_end is also wrong. It does not take into account that head and tail can be equalto nullptr and does not change them.
The function can be defined the following way
void insert_end(int value)
{
node *newnode = new node { value, nullptr };
if ( tail == nullptr )
{
head = tail = newnode;
}
else
{
tail = tail->next = newnode;
}
}
The member function delete_node firstly does not make sense for a singly-linked list and again is wrong and invokes undefined behavior. The function should remove the first node from the list.
Nevertheless if you want to remove the last node from the list then the function can look like
void delete_node()
{
if ( head != nullptr )
{
tail = nullptr;
node *current = head;
while ( current->next )
{
tail = current;
current = current->next;
}
if ( tail == nullptr )
{
head = tail;
}
else
{
tail->next = nullptr;
}
delete current;
}
}
For starters, display() is wrong. You want the update to be temp = temp->next; and it can also be initialized as node * temp = head hence not requiring the second line.
Your delete_node() can be re-written to:
if (head->next == NULL) // handles the case that it consists of 1 element
{
delete head;
head = NULL;
}
else
{
node *nextToEnd = head;
node *end = head->next;
while (end->next != NULL)
{
nextToEnd = end;
end = end->next;
}
delete end;
nextToEnd->next = NULL;
}
As stated in the comments, review the use of the new keyword

C++ Pointers, Linked List Confusion

I am trying to build a linked list in C++. My understanding is that the code I have created should create a node and then progressively link 4 more onto the end. Unfortunately, while I would expect to see the cout results as "12 123 1234 12345" I'm seeing "12 12 12 12" and in my main I am unable to traverse the list - it just crashes.
I have the following code:
struct listNode {
int val;
listNode* next;
};
int nodeCount = 0;
listNode* addToEnd(listNode* node) {
listNode* newNode = new listNode;
newNode->val = ++nodeCount;
newNode->next = NULL;
if (node == NULL) {
return newNode;
}
listNode* current = node;
cout<<"\n\n";
do {
if (current->next == NULL) {
current->next = newNode;
}
cout<<current->val<<"\n";
current = current->next;
} while (current->next != NULL);
cout<<current->val<<endl;
}
int main()
{
listNode* first = addToEnd(NULL);
addToEnd(first);
addToEnd(first);
addToEnd(first);
addToEnd(first);
cout<<"Third: "<<first->next->next->val;
}
Any help is appreciated, as I am at wit's end!
It is obvious that function addToEnd is wrong
listNode* addToEnd(listNode* node) {
listNode* newNode = new listNode;
newNode->val = ++nodeCount;
newNode->next = NULL;
if (node == NULL) {
return newNode;
}
listNode* current = node;
cout<<"\n\n";
do {
if (current->next == NULL) {
current->next = newNode;
}
cout<<current->val<<"\n";
current = current->next;
} while (current->next != NULL);
cout<<current->val<<endl;
}
Let's assume that the list already contains two nodes and consider the do-while loop inside the function. At first current_next != null so the following statement is executed
current = current->next;
Now current points to the second node. Its data member next is equal to NULL. So the condition of the loop
} while (current->next != NULL);
will be false and no iteration will be repeated. So we added nothing.
Also the function returns nothing if node is not equal to NULL.
Rewrite the function the following way
listNode* addToEnd( listNode* node )
{
listNode* newNode = new listNode { ++nodeCount, NULL };
if ( node == NULL) return newNode;
listNode* current = node;
while ( current->next != NULL ) current = current->next;
current->next = newNode;
return newNode;
// or
//return node;
}
Take into account that this statement
cout<<"Third: "<<first->next->next->val;
outputs only the value of the third node.
If you want to output all the list you should write
for ( listNode *current = first; current; current = current->next )
{
std::cout << current->val << ' ';
}
std::cout << std::endl;
By the way using my function you could write in main for example the following way:)
listNode* first;
addToEnd( addToEnd( addToEnd( addToEnd( first = addToEnd( NULL ) ) ) ) );
Use a for loop to get you to the last node instead of a while, and then assign the new node OUTSIDE of the loop. Trying to do it inside will result in an infinite loop (and make the code harder to read):
listNode* current;
for(current = node; current->next != NULL; current = current->next) ;
current->next = newNode;
You're also forgetting to return newNode at the end of the function.
You're falling off the end of a function with non-void return type. The fact that you don't use the return value does not make that ok.
6.6.3 in the Standard says that:
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.
There is no return statement just in case the if condition that checks if(node==null) fails..
Is it against the rules to use recursive functions in your question?
Why not do...
void addToEnd(listNode* node){
if(node == NULL){
*node = new listNode;
node->next = NULL;
node->val = ++nodeCount;
}else{
addToEnd(node->next);
}
return;
}
int main(){
listNode* first = NULL;
addToEnd(first); // 1
addToEnd(first); // 2
addToEnd(first); // 3
addToEnd(first); // 4
addToEnd(first); // Linked list is now 5 long
}
This is how I would have coded adding five nodes to a linked list that holds a node count. If anyone has advice it is welcome.
#include <iostream>
#include <cstdlib>
using namespace std;
struct listNode{
int val;
listNode* next;
};
listNode* addToEnd(listNode*, int);
int main()
{
listNode* first = NULL;
listNode* temp;
int nodeCount = 1;
for(int i = 0; i < 5; i++){
first = addToEnd(first, nodeCount);
nodeCount++;
}
temp = first;
while(temp){
cout << temp->val << ' ';
temp = temp->next;
}
temp = first;
//Deallocate memory
while(temp){ //could do memory deallocation while displaying
nodeToDelete = temp; //the value of nodeCount but wanted to illustrate
//both methods individually
temp = temp->next;
delete nodeToDelete;
}
first = NULL; //eliminate hanging pointer
return 0;
}
listNode* addToEnd(listNode* node, int nodeCount)
{
listNode* newNode = new (nothrow) listNode;
listNode* current = node;
if(newNode){
newNode->val = nodeCount;
newNode->next = NULL;
if (node == NULL)
node = newNode;
else{
while (current->next != NULL)
current = current->next;
current->next = newNode;
}
}
else
cout << "error allocationg memory" << endl;
return node;
}