linked list insertion, pointer confusion - c++

I've looked around the forums but cant seem to find an answer to this very general question. The class below is a basic singly linked list with pushBack written the standard way.
class linkedList {
private:
typedef struct node {
int data;
struct node *next;
node(int d):data(d), next(NULL){}
}*nodePtr;
nodePtr head, temp, curr;
public:
linkedList():head(NULL), temp(NULL), curr(NULL){}
void pushBack(int d) {
temp = new node(d);
curr = head;
if (curr != NULL) {
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = temp;
} else head = temp;
}
void printAll() {
curr = head;
cout << "list:" << endl;
while (curr) {
cout << curr->data << " ";
curr = curr->next;
}
cout << endl;
}
};
but why cant my pushBack function be written like this?
void pushBack(int d) {
temp = new node(d);
curr = head;
while (curr != NULL) {
curr = curr->next;
}
curr = temp;
}
It should iterate through the list until curr == NULL and then set curr = temp. If the list is empty then it doesnt enter the loop and head will be set to the new node by setting temp to curr (which its self is set to head).
The logic makes perfect sense to me so it must be something else I'm missing.
Thank you for the help!

your function would fail for the first insertion or pushback, i.e, when the head pointer is null to begin with. when you assign head to curr like this:
curr = head;
curr now points to head and not vice versa .When curr is then assigned temp( i.e. when the first node isnserted into this linked list) , you have only reassigned the pointer curr with the location held by temp. Now all you have is a pointer curr pointing to the same location as temp, both of which pointers are not connected to head pointer at all!
a modified version of your code that would work is:
void pushBack(int d)
{
temp = new node(d);
curr = head;
if(curr!=NULL)
{
while (curr != NULL)
{
curr = curr->next;
}
curr = temp;
}
else head=temp;
}

Related

node not getting deleted if it's the head [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 last year.
Improve this question
This is my code, the important part at least. Whenever I run it and try to delete the head node, it won't work (the output will be a large negative number). It will work for all other nodes.
Is it something with my code, or you just can't replace the head?
node* displayList(node* head) {
node* curr = head;
while (curr != NULL) {
cout << curr->data << " ";
curr = curr->next;
}
return NULL;
}
node* deleteVal(node* head, int val) {
node* cur, * prev;
if (head == NULL)
return head;
if (head->data == val) {
cur = head->next;
delete head;
head = cur;
return head;
}
cur = head;
prev = NULL;
while (cur->data != val && cur != NULL) {
prev = cur;
cur = cur->next;
}
if (cur == NULL) {
return head;
}
prev->next = cur->next;
return head;
}
int main() {
node* head1 = initNode(), * head2=initNode(), * head3 = initNode();
int val;
head1 = input();
head2 = input();
head3 = input();
conca(head1, head2, head3);
cout << "the concatated list is: ";
displayList(head1);
cout << endl<<"enter the value you want to delete: ";
cin >> val;
deleteVal(head1, val);
cout << "the new list is: ";
displayList(head1);
return 0;
}
For starters the condition of the while loop
while (cur->data != val && cur != NULL) {
prev = cur;
cur = cur->next;
}
must be changed like
while ( cur != nullptr && cur->data != val) {
prev = cur;
cur = cur->next;
}
Also you need indeed to delete the found node like
prev->next = cur->next;
delete cur;
return head;
And in main you have to reassign the pointer to the head node like
head1 = deleteVal(head1, val);
With shown updates the function can look the following way
node* deleteVal(node* head, int val) {
node* cur, * prev;
if (head == NULL)
return head;
if (head->data == val) {
cur = head->next;
delete head;
head = cur;
return head;
}
cur = head;
prev = NULL;
while ( cur != NULL && cur->data != val ) {
prev = cur;
cur = cur->next;
}
if (cur == NULL) {
return head;
}
prev->next = cur->next;
delete cur;
return head;
}
And in main write
head1 = deleteVal(head1, val);
deleteVal() is coded wrong.
When NOT removing the head node, your while loop will exhibit undefined behavior if val is not found in the list. In that situation, cur will become NULL after the last node is checked, and then the loop will try to access cur->data one more time, which is UB.
You need to swap the conditions of the while statement so that cur is checked for NULL before its data member is accessed:
while (cur != NULL && cur->data != val)
Also, if the while loop does find val in the remaining nodes, you are simply unlinking the found node from the list, but you are not actually delete'ing that node, thus you are leaking its memory.
Try this instead:
node* deleteVal(node* head, int val) {
node *cur, *prev;
if (head == NULL)
return head;
if (head->data == val) {
cur = head->next;
delete head;
return cur;
}
// we know the head node doesn't match, no need to
// test it again, so start the loop on the 2nd node...
cur = head->next;
prev = head;
while (cur != NULL && cur->data != val) {
prev = cur;
cur = cur->next;
}
if (cur != NULL) {
prev->next = cur->next;
delete cur;
}
return head;
}
Now, that being said, there are other issues with the code shown.
main() is ignoring the return value of deleteVal(). So, if the node pointed to by head1 were actually removed/deleted from the list, main() has no way of knowing that, and thus ends up passing a now-invalid node* pointer to displayList() afterwards. So, you need to assign the return value of deleteVal() back to head1 to reflect the new list state:
head1 = deleteVal(head1, val);
This is why it is not a good design choice to return a new list pointer by return value (unless you mark it as nodiscard in C++17 and later), as it is too easy to ignore. A better design choice is to pass in the caller's variable by reference/pointer instead, so the function can update the caller's variable directly when needed.
Also, main() is leaking the head1, head2, and head3 nodes when calling input(). You are creating new nodes with initNode(), and then reassigning the pointers to point at new nodes created by input(), thus you lose access to the original nodes from initNode().
In fact, even after calling deleteVal(), you are not freeing any remaining nodes before exiting the program. While it is true that the OS will reclaim all used memory when the program exits, it is best practice to explicitly free anything you allocate.
Also, your deleteVal() is needlessly complex, it can be greatly simplified.
Also, there is no good reason for displayList() to return anything at all.
With that said, try something more like this instead:
void displayList(node* head) {
node* curr = head;
while (curr != NULL) {
cout << curr->data << " ";
curr = curr->next;
}
cout << endl;
}
void deleteVal(node* &head, int val) {
node *cur = head, **prev = &head;
while (cur != NULL) {
if (cur->data == val) {
*prev = cur->next;
delete cur;
return;
}
prev = &(cur->next);
cur = cur->next;
}
}
void deleteList(node* &head) {
node *cur = head, *next;
head = NULL;
while (cur != NULL) {
next = cur->next;
delete cur;
cur = next;
}
}
int input() { // <-- return int, not node* !
...
return ...; // <-- just the user's entered value, not a node wrapping the value
}
int main() {
node* head1 = initNode(), *head2 = initNode(), *head3 = initNode();
head1->data = input();
head2->data = input();
head3->data = input();
conca(head1, head2, head3);
cout << "the concatenated list is: ";
displayList(head1);
cout << "enter the value you want to delete: ";
int val;
cin >> val;
deleteVal(head1, val);
cout << "the new list is: ";
displayList(head1);
deleteList(head1);
return 0;
}

Doubly Linked List Node Insertion in the end

I am trying to create a function for adding at the end for doubly linklist. I can't pinpoint out why it does not print out anything.
There was no error coming out when I build the program.
I am making sure
1. new node check if the head has any value first
created following pointer that is previous current
I connected previous node to new node and new node point to previous node while new node point out to nullptr as next.
#include "pch.h"
#include <iostream>
using namespace std;
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; head->next = tail; tail->prev = head;}
node* pushback(int newdata) {
node* curr = new node;
curr->data = newdata;
curr->next = nullptr;
if (head == nullptr) {
head = curr;
return head;
}
node*precurr = head;
while (precurr->next != nullptr){
precurr = precurr->next;
}
precurr->next = curr;
curr->prev = precurr;
return head;
}
void print() {
while (head->next != nullptr) {
cout << head->data << " " << endl;
head = head->next;
}
}
};
int main()
{
list test;
test.pushback(1);
test.pushback(2);
test.pushback(3);
test.pushback(4);
test.pushback(5);
test.pushback(6);
test.print();
return 0;
}
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.

Unusual Segmentation Fault

I was trying to delete alternate nodes in a linklist. I observed a strange behaviour.
void delete_alternate_node_LinkedList(Node *head) {
Node *prev = head;
Node *curr = head->next;
while (prev != NULL and curr != NULL) {
prev->next = curr->next;
free(curr);
prev = prev->next;
if (prev != NULL) {
curr = prev->next;
}
}
}
This code works fine except the head being nullptr when I use free to delicate or intentionally keep a memory leak but if I change the line free(curr) with delete curr, I get a segmentation fault.
Can anyone explain me the reason?
Here are the boilerplate codes
class Node {
public:
int data;
Node * next;
Node(int data){
this -> data = data;
this -> next = NULL;
}
~Node() {
if(next) {
delete next;
}
}
};
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;
}
Your destructor has a problem
Let's assume
A->B->C->D->nullptr
Now when you delete B it invokes destructor (if you use free it won't).
it will delete recursively C (which in turn delete D) and ..... till the end
so in next iteration you are holding on to a dangling pointer (C) and getting the segfault when you are trying to derefence it.

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.

Sorting a Singly Linked List With Pointers

I am trying to sort a singly linked list using bubble sort by manipulating ONLY the pointers, no keys.
The following gets stuck in the for loop and loops infinitely. I don't understand why this is. Can anybody explain to me why the end of the list is not being found?
Node* sort_list(Node* head)
{
Node * temp;
Node * curr;
for(bool didSwap = true; didSwap; ) {
didSwap = false;
for(curr = head; curr->next != NULL; curr = curr->next) {
if(curr->key > curr->next->key) {
temp = curr;
curr = curr->next;
curr->next = temp;
didSwap = true;
}
cout << curr->next->key << endl;
}
}
return head;
}
If I change the code so that the keys (data) are swapped, then the function works properly but for some reason I am not able make it work by manipulating only pointers.
Logical Error, you are creating an infinite loop with following code -
temp = curr;
curr = curr->next;
curr->next = temp;
I,e next_of_current is pointing to current, so curr->next will always be curr and never will be NULL;
Next you should use previous pointer to fix your list because your list can be traversed in a single direction. So, Think -
If A->B->C->NULL; and you make C and B swap then the new list will still point to A->B and next iteration will be wrong ... because you are not modifying your previous next.
So, another implementation may be -
Node* sort_list(Node* head) {
Node * curr;
Node * prev;
for(bool didSwap = true; didSwap; ) {
didSwap = false;
prev = head;
for(curr = head; curr->next != NULL; curr = curr->next) {
if(curr->key > curr->next->key) {
if (head == curr) {
head = curr->next;
curr->next = head->next;
head->next = curr;
prev = head;
} else {
prev->next = curr->next;
curr->next = prev->next->next;
prev->next->next = curr
}
didSwap = true;
} else if (head != curr) {
prev = prev->next;
}
//cout << curr->next->key << endl; // <- this may cause crash if curr->next now points to NULL; (i,e last element)
}
}
return head;
}
Hope this helps, regards.
You have following problem:
Let you have list with three members: ptr1->ptr2->ptr3. Before swap you have following pointer set: curr=ptr1; curr->next=ptr2; curr->next->next=ptr3. When you perform swap you receive curr=ptr2; curr->next=ptr1; curr->next->next=ptr2.
E.g. you lost ptr3. You need to change code of inner loop with following:
temp = curr;
temp->next = curr->next->next; // Save ptr3
curr = curr->next;
curr->next = temp;
didSwap = true;
The field you want to swap is the value. However, if you swap the node, the next field will change, the question becomes a little more complex, you need keep the next field right. In a word, change value is a simple and good method.
node *sorted_list(node *head) {
node *index1,*index2;
for(index1=head;index1->next!=NULL;index1=index1->next) {
for(index2=index1->next;index2!=NULL;index2=index2->next) {
if(index1->data>index2->data) {
int temp=index1->data;
index1->data=index2->data;
index2->data=temp;
}
}
}
return head;
}