Removing a node from a doubly linked list - c++

I have looked at other threads here on the topic, but have no been able to use them to solve my problem.
this is the main class definition of a node in the linked list:
class node {
public:
// default constructor
node() {name = ""; prev = NULL; next = NULL;};
// default overloaded
node(string s) {name = s; prev = NULL; next = NULL;};
// item in the list
string name;
// links to prev and next node in the list
node * next, * prev;
};
the above is the node class definition, which is used in another class that generates a linked list. the linkedlist code was given to us, which we had to modify, so I know it works. I have gone through and tested the addition of new nodes in the doubly linked list to be working, and I am now working on removing nodes from this same doubly linked list.
The function to remove a node: http://pastebin.com/HAbNRM5W
^ this is the code I need help with, there is too much to retype
I was told by my instructor that the code that is the problem is the line 56, which reads:
tmp->prev = prev;
I am trying to set the link to the previous node to be the correct one. the case I am trying to work from with the similar if/else loops is whether or not the current node is the last item in the list. if it is the last item (aka curr->next = NULL), then don't set a link using curr->next and stop the loop iteration.
any help / ideas / suggestons / feedback will be greatly appreciated!
void linkedList::remove(string s)
{
bool found = false;
node * curr = getTop(), * prev = NULL;
node * tmp = new node();
while(curr != NULL)
{
// match found, delete
if(curr->name == s)
{
found = true;
// found at top
if(prev == NULL)
{
node * temp = getTop();
setTop(curr->next);
getTop()->prev = NULL;
delete(temp);
} // end if
else
{
// determine if last item in the list
if (curr->next = NULL)
{
// prev node points to next node
prev->next = curr->next;
// delete the current node
delete(curr);
} // end if
// if not last item in list, proceed as normal
else
{
// prev node points to next node
prev->next = curr->next;
// set the next node to its own name
tmp = prev->next;
// set prev-link of next node to the previous node (aka node before deleted)
tmp->prev = prev;
// delete the current node
delete(curr);
} // end else
} // end else
} // end if
// not found, advance pointers
if(!found)
{
prev = curr;
curr = curr->next;
} // end if
// found, exit loop
else curr = NULL;
} // end while
if(found)
cout << "Deleted " << s << endl;
else
cout << s << " Not Found "<< endl;
} // end remove

NULL should be replaced with nullptr
if (curr->next = NULL) { ...
That is an assignment, you want:
if (curr->next == nullptr) { ...
On line 47 I think you say: if prev == nullptr and next is not nullptr , but you use
prev->next = curr->next;
Which doesn't work since prev is nullptr.

For your code, I suggest several things. Isolate the code to find the node with the name you are looking for. The remove method SHOULD only remove a doubly linked node, provided that it is given one.
I know that your remove method takes in a string parameter, but pass that to another function and have that function return the node you are looking for.
It should look something like this:
Node *cur = find("abcd");
Node *prev = cur->prev;
prev->next = cur->next;
Node *n = cur->next;
n->next = cur->prev;
cur->next = NULL; //or nullptr
cur->prev = NULL; //or nullptr
delete cur;

Should look like:
prev->next = curr->next;
prev->next->prev = prev;
delete (curr);

I got lost in all your different conditionals. All you need to do is this:
void linkedList::remove(const std::string& s)
{
node* current = getTop(); // get head node
while (current != nullptr) // find the item you are trying to remove
{
if (current->name == s)
{
break; // when you find it, break out of the loop
}
}
if (current != nullptr) // if found, this will be non-null
{
if (current->prev) // if this is not the head node
{
current->prev->next = current->next;
}
else
{
// update head node
}
if (current->next) // if this is not the tail node
{
current->next->prev = current->prev;
}
else
{
// update tail node
}
// at this point, current is completely disconnected from the list
delete current;
}
}

Related

how to delete current linked list node without tracking previous node

I was wondering if it's possible to delete a node by value in a linked list in C++ without tracking both 'previous' and 'current' nodes.
What I did is copy next node's information into current node, link current node to next next node, and delete next node.
I've attempted something like below. However, this only works if the node to delete is not the last node. I'm unable to figure out how to accommodate for the last node.
struct Node
{
int data;
Node* next;
};
void LinkedList::deleteNode(int item)
{
Node* tmpNode = m_head;
while (tmpNode != nullptr)
{
if (tmpNode->data == item)
{
if (tmpNode->next != nullptr) // not last node
{
// this works
Node* delNode = tmpNode->next;
tmpNode->data = tmpNode->next->data;
tmpNode->next = tmpNode->next->next;
delete delNode;
}
else // last node
{
// this does not work
delete tmpNode;
tmpNode = nullptr;
}
std::cout << "deleted item " << item << '\n';
return;
}
tmpNode = tmpNode->next;
}
std::cout << "delete failed: item " << item << " not found\n";
}
When you delete a node, you have to set the next of the previous node to nullptr. Otherwise, it will stay pointing to a deleted node.
Suggestion:
At the beginning of the function, create:
Node* previousTmpNode = nullptr;
Store the address of the previous node when you scan the linked list:
Node* previousTmpNode = tmpNode ; //call right before tmpNode = tmpNode->next;
And then call:
previousTmpNode->next = nullptr; //call right after you deleted the last element.
Or, change your nodes so they point backwards (double linked-list).
As this is probably an exercise/home-work, I'll answer in a sudo(untested) manor. And it will just work for the last node.
Node* prev = nullptr;
for(auto track = m_head; track; track = track->next){
if(track->data != item)
continue;
if(!prev){
m_head = track->next;
delete track;
break;
}
prev->next = track->next;
delete track;
break;
}

Understanding why a string stored in a linked list won't print properly

I'm working on an assignment where I've been given specific member functions I need to complete for a linked-list. One of them requires me to copy a node, change its name, delete the old node and reinsert the new one back into the list. I've come up with this code
while(start != NULL)
{
if(start->id == nID)
{
start->name = newName;
holdNode = start;
removeNode(nID); //removes start from the linkedlist
addNode(holdNode->name, holdNode->id, holdNode->cost);
found = true;
break;
}
else
start = start->next;
I get the correct output in xcode, but when I run it with g++ the name field is either empty or a series of random characters. I'm guessing it has something to do with pointing to the start node and then deleting it, but I can't figure out why it will work in one place but not the other.
Xcode Output
1, testing, $9.99
g++
1, , $9.99
Any help is very much appreciated
Your code is not making any copy of a node. It merely updates the name of the existing node that it finds, then it saves a pointer to that node, removes that node from the list invalidating the pointer you just saved, then tries to use that invalid pointer to get the values to use when inserting a new node. That is where your code is failing.
Try this instead:
bool changeName(int nID, string newName)
{
Product *node = head;
while (node) //while there are items in the list
{
if (node->id == nID) //if ids match
{
double price = node->price;
removeNode(nID); //remove node from the linkedlist
addNode(newName, nID, price); //insert new node
return true;
}
node = node->next; //move to next node
}
return false;
}
Live Demo
However, this is a little inefficient, as removeNode() is likely doing the same id search all over again. A linked list is able to insert and remove a node quickly, without traversing the list multiple times. Just unlink the found node from its surrounding nodes. At the very least, you can get rid of the call to removeNode() in changeName() like this:
bool changeName(int nID, string newName)
{
Product *node = head, *prev = NULL;
while (node) //while there are items in the list
{
if (node->id == nID) //if ids match
{
double price = node->price;
//remove node from the linkedlist
//removeNode(nID);
if (prev) prev->next = node->next;
if (node == head) head = node->next;
delete node;
//insert new node
addNode(newName, nID, price);
return true;
}
prev = node;
node = node->next; //move to next node
}
return false;
}
Live Demo
Another option would be to not actually destroy the found node at all, simply relocate it as-is. Unlink it from its surrounding nodes, and then link it to the nodes at the desired new position, eg:
bool changeName(int nID, string newName)
{
Product *node = head, *prev = NULL;
while (node) //while there are items in the list
{
if (node->id == nID) //if ids match
{
//remove node from the linkedlist
if (prev) prev->next = node->next;
if (node == head) head = node->next;
//insert node in new position
Product **node2 = &head;
while ((*node2) && ((*node2)->name < newName)) {
node2 = &((*node2)->next);
}
node->name = newName;
node->next = *node2;
*node2 = node;
return true;
}
prev = node;
node = node->next; //move to next node
}
return false;
}
Live Demo

C++ Double Linked List Reverse Print

I am trying to write a reverse print function as part of a doubly linked list. Here are the relevant functions that I have written:
void PLAYER::AddNode(int addID, std::string addName){
nodePtr n = new node; //creates a new node pointer
n->next = NULL; //Make next null
n->prev = NULL; // this will set this to be the ending node
n->ID = addID; //These two lines pass the information into the node
n->name = addName; // ID# and Name Information
if(head != NULL){ // This checks to see if a list is set up.
curr = head; // Make this point to the head.
while(curr->next != NULL){ // Loops through until the NULL is found
curr = curr->next;
}
curr->next = n; //Make the currnet node point to N
n->prev = curr; //Make the previous node connect to curr
n->next = tail; // connect new node to the tail.
}
else{
head = n; //If there is no list, this makes N the first node.
}
Here is the class that prototypes the functions to be used.
class PLAYER
{
public: // Functions go inside PUBLIC
PLAYER();
void AddNode(int addID, std::string addName);
void DeleteNode(int delPlayer);
void SortNode();
void PrintList();
void InsertHead(int AddID, std::string addName);
void PrintReverse();
private: //Variables go into here
typedef struct node{
// ...
std::string name;
int ID;
node* next;
node* prev;
}* nodePtr;
nodePtr head, curr, temp, prev, test, tail;
};
And finally my attempt to create a reverse traversing function to print backwards.
void PLAYER::PrintReverse()
{
curr = head;
while(curr->next != NULL) //Get to the end of the list
{
curr = curr->next;
}
while(curr->prev != NULL) //Work backward and print out the contents
{
std::cout << curr->ID << " " << curr->name << endl;
curr = curr->prev;
}
}
What I would like to do is inside the PrintReverse() function have it initialize via the tail pointer, however I can not figure out the functions to add to PrintReverse() and to AddNode() in order to have the new nodes pointed to by tail.
This is my first question posting here, I hope I covered all my bases. Thank you for any help I can find.
EDIT:
Thank you for all your input. I am relearning data structures and yes this is some self imposed homework on my part to begin to get the logic flowing again.
I will make the changes when I get home tonight.
The following changes would need to be considered.
The PrintReverse function would not need the forward pass to obtain the tail.
void PLAYER::PrintReverse()
{
curr = tail;
while(curr != NULL) //Work backward and print out the contents
{
std::cout << curr->ID << " " << curr->name << endl;
curr = curr->prev;
}
}
There is a problem in how tail is handled in the AddNode function. See the lines where the comments contain [CHANGED] and [ADDED]:
if(head != NULL){ // This checks to see if a list is set up.
curr = head; // Make this point to the head.
while(curr->next != NULL){ // Loops through until the NULL is found
curr = curr->next;
}
curr->next = n; //Make the currnet node point to N
n->prev = curr; //Make the previous node connect to curr
n->next = NULL; // [CHANGED]: we want the last node not to have a successor.
}
else{
head = n; //If there is no list, this makes N the first node.
}
tail = n; // [ADDED]: the last node added is the new tail.
However, a simpler solution is to avoid again the forward pass, and start from tail.
if(tail != NULL){ // This checks to see if a list is set up.
tail->next = n; //Make the old tail node point to N
n->prev = tail;
n->next = NULL;
}
else{
head = n; //If there is no list, this makes N the first node.
}
tail = n; // The last node added is the new tail.

Search Function in Linked List - C++ [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
My aim is to produce a function that searches for a number already in the list and print that it has been found.
My initial idea was to follow my remove function which searches through the list until it finds a number (to then delete).
This seemed the logical way to code the search function. If this isn't correct how would I modify it to search through my list and display that a number has been found?
I have nodes *head, *current and *temp as well as node pointer next and number as the data type in a class on a .h file.
Thank you.
NOTE - I used my remove() function under the search() function.
#include <iostream>
#include <string>
#include <fstream>
#include "LinkedList.h"
using namespace SDI;
int main()
{
LinkedList menu;
menu.insert(5);
menu.insert(4);
menu.insert(2);
menu.insert(3);
menu.insert(8);
menu.remove(4);
menu.reverse();
menu.display();
menu.search(2);
system("pause");
};
LinkedList::LinkedList()
{
head = NULL;
current = NULL;
temp = NULL;
};
LinkedList::~LinkedList()
{
};
void LinkedList::insert(int add) //insert function, data is stored in add from function body
{
Node* newnode = new Node; //definition of add node, make new node and make node* point to it
newnode->next = NULL; //point and set up to last node in the list (nothing)
newnode->number = add; //adds data to list
if (head != NULL) //if head is pointing to object then we have list
{
current = head; //make current pointer point to head
while (current->next != NULL) //check to see if end at list, is it the last node?
{
current = current->next; //advances current pointer to end of list
}
current->next = newnode; //adds new node next to value already stored
}
else
{
head = newnode; //if we don't have element in list
}
};
void LinkedList::remove(int remove) //remove function, data is stored in remove from function body
{
Node* remove1 = NULL; //searches through for same value in remove and deletes
temp = head;
current = head;
while (current != NULL && current->number != remove) //check if current node is one we want to delete...if not advance current pointer to next one
{
temp = current; //keep temp pointer one step behind
current = current->next; //advance to next node, traverse list till at the end
}
if (current == NULL) //pass through whole list and value not found
{
std::cout << "N/A\n";
delete remove1; //removes spare number floating around in memory
}
else
{
remove1 = current; //pointing to value we want to delete
current = current->next; //advances current pointer to next node
temp->next = current; //stops hole that occurs in list, patches this up
if (remove1 == head) //if pointer is pointing to front of list
{
head = head->next; //advance the head to next
temp = NULL;
}
delete remove1;
}
};
void LinkedList::search(int searchNum)
{
Node* searchnumber = nullptr;
temp = head;
current = head;
while (current != NULL && current->number != searchNum)
{
temp = current;
current = current->next;
}
if (current != NULL)
{
searchnumber = current;
current = current->next;
std::cout << "-" << searchnumber << " Found";
}
else
{
std::cout << "N/A";
}
};
void LinkedList::display()
{
current = head; //point to start of list
while (current != NULL) //while it points to something in list
{
std::cout << current->number; //display list starting from start
current = current->next; //advance to next pointer
}
};
void LinkedList::reverse()
{
Node *new_head = nullptr; //create new head as we want it to start from last element
for (current = head; current;) //same as display, ask it to go through list from head then outside loop assign to new head and switch sides
{
temp = current; //keep temp pointer one step behind
current = current->next; //goes through each element in the list
temp->next = new_head; //scrolls through backwards from new head
new_head = temp;
}
head = new_head; //assign head to new head
};
Your search algorithm seems to be wrong. Change it to :
if (current != NULL) // (current == NULL) is wrong because it means the value wasn't found
{
searchnumber = current;
current = current->next;
std::cout << "-" << searchnumber->number << " Found"; // here searchnumber is the node's address. You need to print its value, so use searchnumber->number
}
And you don't need to remove nodes till you find the desired value.
You can just use your search algorithm to find if a number already in the list. If that's what you want.
While a list is unordered a comparison of search algorithms doesn't have any sense. Simply iterate over all nodes one by one and apply match criteria.

add element after specifiec element in linked list and delete first element

I tried hard to solve this problem but only managed to partially solve it.
My problem in this method is that I need to add an element after another element:
Example: add 5 1
5 is an element in the linked list but I want to add 1 after 5.
Example: let linked list contains these elements : 2 3 7
I call method to add 1 after 3, add 3 1, so the result assume to be 2 3 1 7, but with my method the result is 2 1 3 7, which is my problem.
Second problem is that I can't deal with the first element:
Example: add 2 1
It acts as if the first element does not exist:
void addNodeAtPos(link *head, int pos,int addelement)
{
link prev=NULL;
link curr =*head;
link newNode = (link)malloc(sizeof(node));
newNode->data = addelement;
while(curr->next != NULL )
{
prev = curr;
curr = curr->next;
if(curr->data == pos)
{
newNode->next = curr;
prev->next = newNode;
break;
}
}
}
My problem here is that I can't remove the first element:
void deletenode(link *head,int s){
bool found = false;
node *curr = *head, *prev=NULL;
while(curr != NULL){
// match found, delete
if(curr->data == s){
found = true;
// found at top
if(prev == NULL){
link temp = *head;
curr->next= prev;
delete(temp);
// found in list - not top
}else{
prev->next = curr->next;
delete(curr);
} }
// not found, advance pointers
if(!found){
prev = curr;
curr = curr->next; }
// found, exit loop
else curr = NULL; }
}
Here's the solution to the first problem
if(curr->data == pos)
{
// tempNode = curr->next;
// improvement as suggested by #Rerito
newNode->next = curr->next;
curr->next = newNode;
break;
}
It appears you are using non-circular doubly linked lists. Thus, both ends of the list are marked with NULL. Now, it seems to me that you use C++ in a very C-esque fashion ... (NULL would'nt be used in C++, there is the nullptr keyword).
I will deal with your issues assuming you are using C instead of C++.
// Note that I pass a link **ptr, NOT a link *ptr ...
void addNodeAtPos(link **head, int pos, int addelement) {
// I am assuming head will be a valid pointer, if not, please add the appropriate checks.
link *newNode = NULL, *cur = *head;
if (NULL == (newNode = malloc(sizeof(link)))
return;
newNode->data = addelement;
while (cur != NULL) {
if (cur->data == pos || NULL == cur->next) {
newNode->next = cur->next;
newNode->prev = cur; // remove this line if there is no prev pointer.
cur->next = newNode;
if (NULL != newNode->next) { // remove this if clause if there is no prev pointer
newNode->next->prev = newNode;
}
break;
}
cur = cur->next;
}
}
You did not specify what you should do if the "position" is not found, I assumed that you just add the element at the end of the list in that case.
Now, considering your issue removing the first element :
void deleteNode(link **head, int el)
{
// I assume you wont pass a `NULL` ptr as #head
link *cur = *head, *prev = NULL;
while (cur != NULL) {
if (cur->data == el) {
next = cur->next;
prev = cur->prev;
free(cur);
if (NULL != next)
next->prev = prev;
if (NULL != prev)
prev->next = next;
else
*head = next;
break;
}
cur = cur->next;
}
}
Why do you need to pass a link **head instead of a link *head ? Because when you are removing the head of the list, you must make sure it won't be accessed anymore and thus you need to update the head pointer you use elsewhere. This is what is made in the *head = next; statement in the above function.
If you are using singly linked list (only a pointer to the next element, not the previous), the solution becomes the following :
void deleteNode(link **head, int el)
{
// I assume you wont pass a `NULL` ptr as #head
link *cur = *head, *prev = NULL, *next = NULL;
while (cur != NULL) {
if (cur->data == el) {
if (NULL != prev)
prev->next = cur->next;
else
*head = cur->next;
free(cur);
break;
}
prev = cur;
cur = cur->next;
}
}