c++ linked list not working - c++

I am trying to make a linked list but am running into problems because I am deleting my nodes twice. The problem only arises when a node is passed into a function (if it is passed by reference everything is fine) which leads me to believe that the object being passed into the function is being copied in such a way that the pointers are pointing to nodes from the original list not the new list. I tried to get around this by overloading the = operator but this didn't work either. An explanation of what I'm doing wrong would be great.
Thanks for the help
#include <iostream>
struct node{
node(int n){
if (n == 1){
data = 1;
next = NULL;
}
if (n == 2){
data = 2;
next = new node(1);
next -> next = NULL;
}
}
~node(){
std::cout << data << std::endl;
if (next != NULL) delete next;
}
void operator=(node a){
next = NULL;
}
int data;
node* next;
};
void func2(node v){
}
int main(){
node v(2);
if (v.next -> next == NULL) std::cout << "true\n";
func2(v);
return 0;
}

Your suspicions are correct, but therein lies the problem; when you pass the node into func2, you're only copying the first node, not the entire list. The copy constructor will copy the first node, and the pointer in the first node (which points to the original second node), so when v goes out of scope in func2 it gets deleted once, then gets deleted again when it goes out of scope of main. You'll need to write the copy constructor to do a "deep copy," traversing the entire list and copying every node into a new address.
Remember also that the copy constructor should return *this in most cases, this is in the C++ FAQ and in the book "Effective C++" by Scott Meyers. Thus the signature should be:
node& operator=(const node& node);
If you're going to overload the assignment operator, you should probably also define a copy constructor. Good job explaining the problem, by the way.
Edit: The code would look like this. I apologize that I haven't tested this; I'm on my tablet and editing this is painful...
#include <iostream>
struct node{
node(const node& toCopy) : data(toCopy.data)
{
if(toCopy.next != null) {
next = new node(toCopy);
}
}
node(int n){
if (n == 1){
data = 1;
next = NULL;
}
if (n == 2){
data = 2;
next = new node(1);
next -> next = NULL;
}
}
node& operator=(const node& toCopy) {
if(&toCopy != this) {
data = toCopy.data;
if(next != NULL) {
next = new node(toCopy);
}
}
return *this;
}
~node(){
std::cout << data << std::endl;
if (next != NULL) delete next;
}
int data;
node* next;
};
void func2(node v){
}
int main(){
node v(2);
if (v.next -> next == NULL) std::cout << "true\n";
func2(v);
return 0;
}

Related

Linked List: How to implement Destructor, Copy Constructor, and Copy Assignment Operator? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
Improve this question
This is my C++ code:
#include <iostream>
using namespace std;
typedef struct Node
{
int data;
Node* next;
}Node;
class LinkedList
{
private:
Node* first;
Node* last;
public:
LinkedList() {first = last = NULL;};
LinkedList(int A[], int num);
~LinkedList();
void Display();
void Merge(LinkedList& b);
};
// Create Linked List using Array
LinkedList::LinkedList(int A[], int n)
{
Node* t = new Node;
if (t == NULL)
{
cout << "Failed allocating memory!" << endl;
exit(1);
}
t->data = A[0];
t->next = NULL;
first = last = t;
for (int i = 1; i < n; i++)
{
t = new Node;
if (t == NULL)
{
cout << "Failed allocating memory!" << endl;
exit(1);
}
t->data = A[i];
t->next = NULL;
last->next = t;
last = t;
}
}
// Deleting all Node in Linked List
LinkedList::~LinkedList()
{
Node* p = first;
Node* tmp;
while (p != NULL)
{
tmp = p;
p = p->next;
delete tmp;
}
}
// Displaying Linked List
void LinkedList::Display()
{
Node* tmp;
for (tmp = first; tmp != NULL; tmp = tmp->next)
cout << tmp->data << " ";
cout << endl;
}
// Merge two linked list
void LinkedList::Merge(LinkedList& b)
{
// Store first pointer of Second Linked List
Node* second = b.first;
Node* third = NULL, *tmp = NULL;
// We find first Node outside loop, smaller number, so Third pointer will store the first Node
// Then, we can only use tmp pointer for repeating process inside While loop
if (first->data < second->data)
{
third = tmp = first;
first = first->next;
tmp->next = NULL;
}
else
{
third = tmp = second;
second = second->next;
tmp->next = NULL;
}
// Use while loop for repeating process until First or Second hit NULL
while (first != NULL && second != NULL)
{
// If first Node data is smaller than second Node data
if (first->data < second->data)
{
tmp->next = first;
tmp = first;
first = first->next;
tmp->next = NULL;
}
// If first Node data is greater than second Node data
else
{
tmp->next = second;
tmp = second;
second = second->next;
tmp->next = NULL;
}
}
// Handle remaining Node that hasn't pointed by Last after while loop
if (first != NULL)
tmp->next = first;
else
tmp->next = second;
// Change first to what Third pointing at, which is First Node
first = third;
// Change last pointer from old first linked list to new last Node, after Merge
Node* p = first;
while (p->next != NULL)
{
p = p->next;
}
last = p;
// Destroy second linked list because every Node it's now connect with first linked list
// This also prevent from Double free()
b.last = NULL;
b.first = NULL;
}
int main()
{
int arr1[] = {4, 8, 12, 14, 15, 20, 26, 28, 30};
int arr2[] = {2, 6, 10, 16, 18, 22, 24};
int size1 = sizeof(arr1) / sizeof(arr1[0]);
int size2 = sizeof(arr2) / sizeof(arr2[0]);
LinkedList l1(arr1, size1);
LinkedList l2(arr2, size2);
l1.Display();
l2.Display();
// Merge two linked list, pass l2 as reference
l1.Merge(l2);
l1.Display();
return 0;
}
I'm beginner on C++ and in this code, I practice how to Merge two linked list. This actually works perfectly. I've successfully Merge the two Linked List in sorted order.
But, there's people said that I should've follow the Rule of Three on C++. Which implement: Destructor, Copy Constructor, and Copy Assignment Operator.
I've watched many videos about that. I do understand that is basically handle Shallow Copy especially when we don't want two different object point to the same address of memory. But, for my problem is, I still don't know how to Implement it on a Class that working on a Linked List just like my code above.
Someone said, in my main(), this code: l1.Merge(l2); is somehow incorrect because I don't have explicit Copy Constructor.
And if you look at my Merge() function, in Last line, if I didn't to this: b.last = NULL; and b.first = NULL; , which simply destroy pointer of Second Linked list, the Compiler give me warning: Double free() detected.
So, I think my question is:
How can this code: l1.Merge(l2); is have something to do with Copy Constructor?
Is Double free() happened because I don't implement the Rule of Three? If yes, how to address them?
How to write the Rule of Three based on my code? When or How to use them?
Based on this Code, is there something wrong? Do I still need the Rule of Three if my Program only want to Merge Linked List?
Thank You. I hope someone can explain to me like I'm 10 years old. and hope someone can write me some Code.
But, for my problem is, I still don't know how to Implement [Rule of Three] on a Class that working on a Linked List just like my code above.
You simply implement the copy constructor and copy assignment operator to iterate the input list, making a copy of each node and inserting them into your target list. You already have a working destructor. In the case of the copy assignment operator, you can usually use the copy-swap idiom to implement it using the copy constructor to avoid repeating yourself.
Someone said, in my main(), this code: l1.Merge(l2); is somehow incorrect because I don't have explicit Copy Constructor.
Then you were told wrong. Your Merge() code has nothing to do with a copy constructor.
And if you look at my Merge() function, in Last line, if I didn't to this: b.last = NULL; and b.first = NULL;, which simply destroy pointer of Second Linked list, the Compiler give me warning: Double free() detected.
Correct. Since you are moving the nodes from the input list to the target list, you need to reset the input list so it doesn't point at the moved nodes anymore. Otherwise, the destructor of the input list will try to free them, as will the destructor of the target list.
How can this code: l1.Merge(l2); is have something to do with Copy Constructor?
It doesn't have anything to do with it.
Is Double free() happened because I don't implement the Rule of Three?
Not in your particular example, as you are not performing any copy operations. But, in general, not implementing the Rule of Three can lead to double frees, yes.
How to write the Rule of Three based on my code?
See the code below.
Do I still need the Rule of Three if my Program only want to Merge Linked List?
No. Only when you want to make copies of lists.
With that said, here is an implementation that includes the Rule of Three:
#include <iostream>
#include <utility>
struct Node
{
int data;
Node *next;
};
class LinkedList
{
private:
Node *first;
Node *last;
public:
LinkedList();
LinkedList(const LinkedList &src);
LinkedList(int A[], int num);
~LinkedList();
LinkedList& operator=(const LinkedList &rhs);
void Display() const;
void Merge(LinkedList &b);
};
// Create Linked List using default values
LinkedList::LinkedList()
: first(NULL), last(NULL)
{
}
// Create Linked List using Array
LinkedList::LinkedList(int A[], int n)
: first(NULL), last(NULL)
{
Node **p = &first;
for (int i = 0; i < n; ++i)
{
Node *t = new Node;
t->data = A[i];
t->next = NULL;
*p = t;
p = &(t->next);
last = t;
}
}
// Create Linked List by copying another Linked List
LinkedList::LinkedList(const LinkedList &src)
: first(NULL), last(NULL)
{
Node **p = &first;
for (Node *tmp = src.first; tmp; tmp = tmp->next)
{
Node* t = new Node;
t->data = tmp->data;
t->next = NULL;
*p = t;
p = &(t->next);
last = t;
}
}
// Deleting all Node in Linked List
LinkedList::~LinkedList()
{
Node *p = first;
while (p)
{
Node *tmp = p;
p = p->next;
delete tmp;
}
}
// Update Linked List by copying another Linked List
LinkedList& LinkedList::operator=(const LinkedList &rhs)
{
if (&rhs != this)
{
LinkedList tmp(rhs);
std::swap(tmp.first, first);
std::swap(tmp.last, last);
}
return *this;
}
// Displaying Linked List
void LinkedList::Display() const
{
for (Node *tmp = first; tmp; tmp = tmp->next)
std::cout << tmp->data << " ";
std::cout << std::endl;
}
// Merge two linked list
void LinkedList::Merge(LinkedList& b)
{
if ((&b == this) || (!b.first))
return;
if (!first)
{
first = b.first; b.first = NULL;
last = b.last; b.last = NULL;
return;
}
// Store first pointer of Second Linked List
Node *second = b.first;
Node *third, **tmp = &third;
// We find first Node outside loop, smaller number, so Third pointer will store the first Node
// Then, we can only use tmp pointer for repeating process inside While loop
// Use while loop for repeating process until First or Second hit NULL
do
{
// If first Node data is smaller than second Node data
if (first->data < second->data)
{
*tmp = first;
tmp = &(first->next);
first = first->next;
}
// If first Node data is greater than second Node data
else
{
*tmp = second;
tmp = &(second->next);
second = second->next;
}
*tmp = NULL;
}
while (first && second);
// Handle remaining Node that hasn't pointed by Last after while loop
*tmp = (first) ? first : second;
// Change first to what Third pointing at, which is First Node
first = third;
// Change last pointer from old first linked list to new last Node, after Merge
Node *p = first;
while (p->next)
{
p = p->next;
}
last = p;
// Destroy second linked list because every Node it's now connect with first linked list
// This also prevent from Double free()
b.first = b.last = NULL;
}
int main()
{
int arr1[] = {4, 8, 12, 14, 15, 20, 26, 28, 30};
int arr2[] = {2, 6, 10, 16, 18, 22, 24};
int size1 = sizeof(arr1) / sizeof(arr1[0]);
int size2 = sizeof(arr2) / sizeof(arr2[0]);
LinkedList l1(arr1, size1);
LinkedList l2(arr2, size2);
LinkedList l3(l1);
LinkedList l4;
l1.Display();
l2.Display();
l3.Display();
l4.Display();
// Merge two linked list, pass l2 as reference
l3.Merge(l2);
l4 = l3;
l1.Display();
l2.Display();
l3.Display();
l4.Display();
return 0;
}
Demo
There are several questionable practices applied in this code, and there is also a bug.
First, the bug. When you create a list, it news all its nodes and keeps track of them using pointers. When you assign a list to another, you literally copy the pointer values. Not only have you now lost the nodes of the assigned list (because you overwrote them) and got a memory leak (because now there's no pointer pointing to the allocated nodes), you also now have the same pointers on two different lists, pointing to the same nodes. When the lists are destroyed, both of them try to delete their nodes, and you end up freeing the same memory twice. Yuk.
The solution to this bug is to implement the assignment operator.
Then, the questionable practices:
using namespace std; (Why is "using namespace std;" considered bad practice?)
You're assigning the members of LinkedList in the constructor body, instead of passing the values directly to their constructor in the initialization list. (What is this weird colon-member (" : ") syntax in the constructor?)
Declaring an array parameter (int[]) is declaring a pointer. Just be aware of it.
new cannot return NULL! It's useless to check its return value. If it can't allocate, it will simply throw an exception.
NULL is the inappropriate constant to use. You can use nullptr, it's the C++ equivalent of NULL, except it's type safe.
Manual memory management with new and delete is tricky to get right (as you figured out yourself). You might be interested in using std::unique_ptr or std::shared_ptr to alleviate the burden. They would have caught the bug.
Now, please: do not write in C++ like it's C with classes. I understand that you may not have encountered all of the features I presented here, but anyway now you know about them :)

Counting occurrence in singly linked list by nodes

I am writing a simple app that gets a list and saves the objects as nodes in a singly linked list and we can add(), remove(), copy(), etc. each node depending on the given data set. each node has a char value which is our data and an int count which counts the occurrence of the related char.
e.g. for a list like
a, a, b, b, c, a
there would be three nodes (since there are three different characters) which are:
[a,3,*next] -> [b,2,*next] -> [c,1,*next] -> nullptr
bool isAvailable() checks if the data is already in the list or not.
Q: When inserting a data there are two options:
The data has not been entered: so we have to create a newNodewith the given data, count=1and *next=NULL.
The data is already entered: so we have to count++ the node that has the same data.
I know if the given data is available or not, but how can I point to the node with same data?
Here's the code:
#include "stdafx.h"
#include<iostream>
using namespace std;
class Snode
{
public:
char data;
int count;
Snode *next;
Snode(char d, int c)
{
data = d;
count = c;
next = NULL;
}
};
class set
{
private:
Snode *head;
public:
set()
{
head = NULL;
tail = NULL;
}
~set();
void insert(char value);
bool isAvailable(char value);
};
set::~set()
{
Snode *t = head;
while (t != NULL)
{
head = head->next;
delete t;
}
}
bool set::isAvailable(char value)
{
Snode *floatingNode = new Snode(char d, int c);
while(floatingNode != NULL)
{
return (value == floatingNode);
floatingNode->next = floatingNode;
}
}
void set::insert(char value)
{
Snode *newNode = new Snode(char d, int c);
data = value;
if (head == NULL)
{
newNode->next = NULL;
head = newNode;
newNode->count++;
}
else
{
if(isAvailable)
{
//IDK what should i do here +_+
}
else
{
tail->next= newNode;
newNode->next = NULL;
tail = newNode;
}
}
}
I know if the given data is available or not, but how can I point to the node with same data?
You'll need to start at the head of the list and iterate along the list by following the next pointers until you find the node with the same data value. Once you've done that, you have your pointer to the node with the same data.
Some other notes for you:
bool set::isAvailable(char value)
{
Snode *floatingNode = new Snode(char d, int c);
while(floatingNode != NULL)
{
return (value == floatingNode);
floatingNode->next = floatingNode;
}
}
Why is this function allocating a new Snode? There's no reason for it to do that, just initialize the floatingNode pointer to point to head instead.
This function always returns after looking at only the first node in the linked list -- which is not the behavior you want. Instead, it should return true only if (value == floatingNode); otherwise it should stay inside the while-loop so that it can go on to look at the subsequent nodes as well. Only after it drops out of the while-loop (because floatingNode finally becomes NULL) should it return false.
If you were to modify isAvailable() slightly so that instead of returning true or false, it returned either floatingPointer or NULL, you'd have your mechanism for finding a pointer to the node with the matching data.
e.g.:
// Should return either a pointer to the Snode with data==value,
// or NULL if no such Snode is present in the list
Snode * set::getNodeWithValueOrNullIfNotFound(char value) const
{
[...]
}
void set::insert(char value)
{
Snode * theNode = getNodeWithValueOrNullIfNotFound(value);
if (theNode != NULL)
{
theNode->count++;
}
else
{
[create a new Snode and insert it]
}
}
You had a lot of problems in your code, lets see what are they:
First of all, Snode doesn't need to be a class, rather you can go with a simple strcut; since we need everything public.(not a mistake, but good practice)
You could simple initialize count = 1 and next = nullptr, so that no need of initializing them throw constructor. The only element that need to be initialized through constructor is Snod's data.
Since c++11 you can use keyword nullptr instead of NULL, which denotes the pointer literal.
Member function bool set::isAvailable(char value) will not work as you think. Here you have unnecessarily created a new Snode and cheacking whether it points to nullptr which doesn't allow you to even enter the loop. BTW what you have written in the loop also wrong. What do you mean by return (value == floatingNode); ? floatingNode is a Snode by type; not a char.
Hear is the correct implementation. Since we don't wanna overwrite the head, will create a Node* pointer and assign head to it. Then iterate through list until you find a match. If not found, we will reach the end of the isAvailable() and return false.
inline bool isAvailable(const char& value)
{
Node *findPos = head;
while(findPos != nullptr)
{
if(findPos -> data == value) return true;
else findPos = findPos->next_node;
}
return false;
}
In void set::insert(char value), your logic is correct, but implementation is wrong. Following is the correct implementation.(Hope the comments will help you to understand.
void insert(const char& value)
{
if(head == nullptr) // first case
{
Node *newNode = new Node(value);
newNode->next_node = head;
head = newNode;
}
else if(isAvailable(value)) // if node available
{
Node *temp = head;
while(temp->data != value) // find the node
temp = temp->next_node;
temp->count += 1; // and count it by 1
}
else // all new nodes
{
Node *temp = head;
while(temp->next_node != nullptr) // to find the null point (end of list)
temp = temp->next_node;
temp = temp->next_node = new Node(value); // create a node and assign there
}
}
Your destructor will not delete all what you created. It will be UB, since your are deleting newly created Snode t ( i.e, Snode *t = head;). The correct implementation is as bellow.(un-comment the debugging msg to understand.)
~set()
{
Node* temp = head;
while( temp != nullptr )
{
Node* next = temp->next_node;
//std::cout << "deleting \t" << temp->data << std::endl;
delete temp;
temp = next;
}
head = nullptr;
}
Last but not least, the naming (set) what you have here and what the code exactly doing are both different. This looks more like a simple linked list with no duplicates. This is however okay, in order to play around with pointers and list.
To make the code or iteration more efficient, you could do something like follows. In the isAvailable(), in case of value match/ if you found a node, you could simply increment its count as well. Then in insert(), you can think of, if node is not available part.
Hope this was helpful. See a DEMO
#include <iostream>
// since you wanna have all of Node in public, declare as struct
struct Node
{
char data;
int count = 1;
Node* next_node = nullptr;
Node(const char& a) // create a constrcor which will initilize data
: data(a) {} // at the time of Node creation
};
class set
{
private:
Node *head; // need only head, if it's a simple list
public:
set() :head(nullptr) {} // constructor set it to nullptr
~set()
{
Node* temp = head;
while( temp != nullptr )
{
Node* next = temp->next_node;
//std::cout << "deleting \t" << temp->data << std::endl;
delete temp;
temp = next;
}
head = nullptr;
}
inline bool isAvailable(const char& value)
{
Node *findPos = head;
while(findPos != nullptr)
{
if(findPos -> data == value) return true;
else findPos = findPos->next_node;
}
return false;
}
void insert(const char& value)
{
if(head == nullptr) // first case
{
Node *newNode = new Node(value);
newNode->next_node = head;
head = newNode;
}
else if(isAvailable(value)) // if node available
{
Node *temp = head;
while(temp->data != value) // find the node
temp = temp->next_node;
temp->count += 1; // and count it by 1
}
else // all new nodes
{
Node *temp = head;
while(temp->next_node != nullptr) // to find the null point (end of list)
temp = temp->next_node;
temp = temp->next_node = new Node(value);
}
}
void print() const // just to print
{
Node *temp = head;
while(temp != nullptr)
{
std::cout << temp->data << " " << temp->count << "\n";
temp = temp->next_node;
}
}
};
int main()
{
::set mySet;
mySet.insert('a');
mySet.insert('a');
mySet.insert('b');
mySet.insert('b');
mySet.insert('c');
mySet.insert('a');
mySet.print();
return 0;
}

segmentation fault only after accessing struct in linked list for a second time

Sorry for the unclear title, I really don't know how to describe this issue. I'm in my first year of computer science so I really don't know much about C++ yet. However, trying to look up this issue did not help.
The issue:
In the main function, the "printRawData" friend function is called twice. The function is supposed to print each element of the linked list stored by the the class "LinkedList". It works the first time, but the second time I get a segmentation fault. I really have no idea what I'm doing wrong. My T.A. said he thinks that the struct's string variable "element_name" is being corrupted when accessed.
Sorry for the messy code, if I'm not explaining my issue well, or if I'm breaking any kind of stackoverflow etiquette. I appreciate any help I get.
//Note: C++ 11 is needed, due to to_string use
#include <iostream>
#include <string>
using namespace std;
struct Node {
string element_name;
int element_count;
Node* next;
};
class LinkedList{
private:
Node* first;
public:
LinkedList();
~LinkedList();
bool isEmpty();
void AddData(string name, int count);
friend void printRawData(LinkedList l);
};
//where the error occurs
void printRawData(LinkedList l){
Node* n = l.first;
while (n != NULL) { //iterates through the linked list and prints each element
cout << n->element_name << " : " << n->element_count << endl;
n = n->next;
}
}
LinkedList::LinkedList(){
first = NULL;
}
LinkedList::~LinkedList(){
Node* n = first;
while (n != NULL) {
Node* temp = n;
n = temp->next;
delete temp;
}
}
bool LinkedList::isEmpty(){
return first == NULL;
}
void LinkedList::AddData(string name, int count){
Node* newnode = new Node;
newnode->element_name = name;
newnode->element_count = count;
newnode->next = NULL;
Node* n = first;
//if the linked list is empty
if(n == NULL){
first = newnode;
return;
}
//if there's only one element in the linked list,
//if the name of first element comes before the name of new element,
//first element's pointer is to the new element.
//otherwise, the new node becomes the first and points to the previous first
//element.
if (n->next == NULL){
if (n->element_name < newnode->element_name){
n->next = newnode;
return;
} else {
newnode->next = first;
first = newnode;
return;
}
}
//if the first element's name comes after the new element's name,
//have the new element replace the first and point to it.
if (n->element_name > newnode->element_name){
newnode->next = first;
first = newnode;
return;
}
//iterating through linked list until the next element's name comes after
//the one we're inserting, then inserting before it.
while (n->next != NULL) {
if (n->next->element_name > newnode->element_name){
newnode->next = n->next;
n->next = newnode;
return;
}
n = n->next;
}
//since no element name in the linked list comes after the new element,
//the node is put at the back of the linked list
n->next = newnode;
}
main(){
LinkedList stack;
stack.AddData("Fish", 12);
stack.AddData("Dog", 18);
stack.AddData("Cat", 6);
printRawData(stack);
printRawData(stack);
}
The function void printRawData(LinkedList l) passes the parameter by value, so it gets a copy of the LinkedList object.
However, the copy contains a copy of the first pointer, but doesn't copy any of the nodes. So when this copy is destroyed, the LinkedList destructor will delete all the nodes.
And then the original is damaged.
You might want to pass a reference instead of creating a copy.
This is also the reason why the std::list has copy constructors and assignment operators that perform a "deep copy", where the nodes are also copied (not just the list head).

C++ Add node to beginning of linked list fails after 1 time. Trouble with other methods too

So I have a linked list program semi working. I am just having some trouble with certain methods.... The ones I have documented bekiw are working except for delete from end which is acting weird.
I am using NetBeans on Mavericks with G++ as my compiler and C++11
Here is a zip of all of the program files
Here is a list of the methods I am trying to make:
//working
int size() const;
/kind of
void addToStart(Node *);
//working
void addToEnd(Node *);
//working
void printList();
//working
bool removeFromStart();
//kind of working
bool removeFromEnd();
//Still working on these
void removeNodeFromList(int);
void removeNodeFromList(string);
For now, I have to run removeFromEnd() twice in order for it to work. Meaning, I run it once at the beginning of the program and it does nothing, but every subsequent time, it actually does the deleting.
For addToStart() it works if I only run it once. I.E:
I can run it once at the beginning of the program and print out the list
I can run it once AFTER using addToEnd, but if I try it a second time, and I try to print out the list, it just keeps spitting out the value I tried to add.
addToEnd() works perfectly find if I just keep running that, but it fails if I:
Start out by using addToEnd() to add items, then use addToStart() ONCE and THEN try to use addToEnd() again. When I print out the list, it only prints out two objects and each of those is a copy of the last value I tried to insert.
void LinkedList::addToEnd(Node* ne)
{
Node** q = &myHead;
while (*q)
{
q = &(*q)->next;
}
*q = new Node(ne->itemName, ne->itemNo);
}
void LinkedList::printList()
{
Node* p = myHead;
while (p != NULL)
{
cout << p->itemNo << " " << p->itemName;
cout << endl;
p = p->next;
}
cout << endl << endl;
}
bool LinkedList::removeFromStart()
{
if (myHead == NULL)
{
cout << "List is already empty";
}
else
{
myHead = myHead->next;
}
}
bool LinkedList::removeFromEnd()
{
if (myHead == NULL)
return false;
//Empty the list if there's only one element
if (myHead->next == NULL)
{
delete myHead;
myHead = NULL;
myTail = NULL;
return true;
}
// Find the last item in the list
Node *temp = myHead;
while (temp->next != myTail)
{
temp = temp->next;
}
delete myTail;
temp->next = NULL;
myTail = temp;
return true;
}
Also, still trying to figure out the remove ones
void LinkedList::removeNodeFromList(int i) {
//Save the values
Node* p = myHead;
Node* temp = myHead->next;
while (p) {
if (p->itemNo == i) {
p=temp;
} else {
p = p->next;
}
}
}
You have a tail pointer, so why are you iterating through the list to find the end? Additionally, why are you passing the node by pointer?
void LinkedList::addToEnd(Node ne)
{
if (myHead == nullptr) // empty list
{
myHead = myTail = new Node(ne);
myTail->next = nullptr;
}
else
{
myTail->next = new Node(ne); // assuming Node has an accessible copy constructor
myTail = myTail->next;
}
}
The removeFromStart function has a memory leak:
bool LinkedList::removeFromStart()
{
if (myHead == nullptr)
{
cout << "List is already empty";
return false;
}
Node* temp = myHead;
myHead = myHead->next;
if (myTail == temp) // if there is only 1 element in the list, head == tail
{
myTail = myhead;
}
delete temp;
return true;
}
Presumably, removeFromEnd should be removing the tail:
bool LinkedList::removeFromEnd()
{
if (myTail == nullptr)
return false;
// unless you have a doubly-linked list, loop to find 1 before the tail
Node* temp = nullptr;
for (temp = myHead; temp && temp->next != myTail; temp = temp->next);
if (myHead == temp) // when there is only 1 element in the list, head == tail
{
delete temp->next;
myHead = nullptr;
myTail = nullptr;
}
else
{
delete temp->next;
temp->next = nullptr;
myTail = temp;
}
return true;
}
And yes, you are using new (in your addtoEnd function), so you must use delete (not free!).
Side note: You can write the remove code better by using std::unique_ptr (you can actually improve the code overall by using it everywhere) which will make your code for each about 4 lines long. I'll leave that for you to implement.
You have a member called myTail that you seem to be using 'sometimes'
In addFromEnd you do not update myTail.
In one of the remove functions you do not update myTail even though you might change it
But in RemoveFromTail you are trying to use it.
There is no reason that myTail will contain a valid value and when you try to use it you can get an error (cause the node may have been deleted) or an unexpected result because it just points somewhere in the list.
You should either lose it (since you can easily figure out the tail. it is the node where next==NULL) or take care to maintain it in every call that changes the List (only if the tail is affected obviously)
Since you are using c++11 a few suggestions:
use nullptr instead of NULL
use std::unique_ptr for Next. It will take care of 'deleting' nodes you are no longer using for you.
use a copy ctor for Node. If you ever make a change to Node you will not need to revisit this part of the code just the constructors.
If you are holding copies of objects in the list. Then it is much clearer if your interface will accept const Node& instead of Node*. To me Node* makes it seem like you are going to use the ptr the user supplied.

C++ Linked List assignment: trouble with insertion and deletion

I am working on a linked list implementation in C++. I am making progress but am having trouble getting the insertion functionality and deletion functionality to work correctly. Below is list object in the C++ header file:
#ifndef linkList_H
#define linkList_h
//
// Create an object to represent a Node in the linked list object
// (For now, the objects to be put in the list will be integers)
//
struct Node
{
Node() : sentinel(0) {}
int number;
Node* next;
Node* prev;
Node* sentinel;
};
//
// Create an object to keep track of all parts in the list
//
class List
{
public:
//
// Contstructor intializes all member data
//
List() : m_listSize(0), m_listHead(0) {}
//
// methods to return size of list and list head
//
Node* getListHead() const { return m_listHead; }
unsigned getListSize() const { return m_listSize; }
//
// method for adding and inserting a new node to the linked list,
// retrieving and deleting a specified node in the list
//
void addNode(int num);
void insertNode(Node* current);
void deleteNode(Node* current);
Node* retrieveNode(unsigned position);
private:
//
// member data consists of an unsigned integer representing
// the list size and a pointer to a Node object representing head
//
Node* m_listHead;
unsigned m_listSize;
};
#endif
And here is the implementation (.cpp) file:
#include "linkList.h"
#include <iostream>
using namespace std;
//
// Adds a new node to the linked list
//
void List::addNode(int num)
{
Node *newNode = new Node;
newNode->number = num;
newNode->next = m_listHead;
m_listHead = newNode;
++m_listSize;
}
//
// NOTWORKING: Inserts a node which has already been set to front
// of the list
//
void List::insertNode(Node* current)
{
// check to see if current node already at
// head of list
if(current == m_listHead)
return;
current->next = m_listHead;
if(m_listHead != 0)
m_listHead->prev = current;
m_listHead = current;
current->prev = 0;
}
//
// NOTWORKING: Deletes a node from a specified position in linked list
//
void List::deleteNode(Node* current)
{
current->prev->next = current->next;
current->next->prev = current->prev;
}
//
// Retrieves a specified node from the list
//
Node* List::retrieveNode(unsigned position)
{
if(position > (m_listSize-1) || position < 0)
{
cout << "Can't access node; out of list bounds";
cout << endl;
cout << endl;
exit(EXIT_FAILURE);
}
Node* current = m_listHead;
unsigned pos = 0;
while(current != 0 && pos != position)
{
current = current->next;
++pos;
}
return current;
}
After running a brief test program in the client C++ code, here is the resulting output:
Number of nodes: 10
Elements in each node:
9 8 7 6 5 4 3 2 1 0
Insertion of node 5 at the list head:
4 9 8 7 6 5 4 9 8 7
Deletion of node 5 from the linked list
As you can see, the insertion is not simply moving node 5 to head of list, but is overwriting other nodes beginning at the third position. The pseudo code I tried to implement came from the MIT algorithms book:
LIST-INSERT(L, x)
next[x] <- head[L]
if head[L] != NIL
then prev[head[L]] <- x
head[L] <- x
prev[x] <- NIL
Also the deletion implementation is just crashing when the method is called. Not sure why; but here is the corresponding pseudo-code:
LIST-DELET'
next[prev[x]] <- next[x]
prev[next[x]] <- prev[x]
To be honest, I am not sure how the previous, next and sentinel pointers are actually working in memory. I know what they should be doing in a practical sense, but looking at the debugger it appears these pointers are not pointing to anything in the case of deletion:
(*current).prev 0xcdcdcdcd {number=??? next=??? prev=??? ...} Node *
number CXX0030: Error: expression cannot be evaluated
next CXX0030: Error: expression cannot be evaluated
prev CXX0030: Error: expression cannot be evaluated
sentinel CXX0030: Error: expression cannot be evaluated
Any help would be greatly appreciated!!
You have got an error in addNode(). Until you fix that, you can't expect insertNode to work.
Also, I think your design is quite silly. For example a method named "insertNode" should insert a new item at arbitrary position, but your method insertNode does a pretty different thing, so you should rename it. Also addNode should be renamed. Also as glowcoder wrote, why are there so many sentinels? I am affraid your class design is bad as a whole.
The actual error is that you forgot to set prev attribute of the old head. It should point to the new head.
void List::addNode(int num)
{
Node *newNode = new Node;
newNode->number = num;
newNode->next = m_listHead;
if(m_listHead) m_listHead->prev = newNode;
m_listHead = newNode;
++m_listSize;
}
Similarly, you have got another error in deleteNode(). It doesn't work when deleting last item from list.
void List::deleteNode(Node* current)
{
m_listSize--;
if(current == m_listHead) m_listHead = current->next;
if(current->prev) current->prev->next = current->next;
if(current->next) current->next->prev = current->prev;
}
Now you can fix your so-called insertNode:
void List::insertNode(Node* current)
{
int value = current->number;
deleteNode(current);
addNode(value);
}
Please note that I wrote everything here without compiling and testing in C++ compiler. Maybe there are some bugs, but still I hope it helps you at least a little bit.
In deleteNode, you are not handling the cases where current->next and/or current->prev is null. Also, you are not updating the list head if current happens to be the head.
You should do something like this:
node* next=current->next;
node* prev=current->prev;
if (next!=null) next->prev=prev;
if (prev!=null) prev->next=next;
if (m_listhead==current) m_list_head=next;
(Warning: I have not actually tested the code above - but I think it illustrates my idea well enough)
I am not sure what exactly your InsertNode method does, so I can't offer any help there.
OK.
As #Al Kepp points out, your "add node" is buggy. Look at Al's code and fix that.
The "insert" that you are doing does not appear to be a normal list insert. Rather it seems to be a "move to the front" operation.
Notwithstanding that, you need to delete the node from its current place in the list before you add it to the beginning of the list.
Update
I think you have misunderstood how insert should work. It should insert a new node, not one that is already in the list.
See below for a bare-bones example.
#include <iostream>
// List Node Object
//
struct Node
{
Node(int n=0);
int nData;
Node* pPrev;
Node* pNext;
};
Node::Node(int n)
: nData(n)
, pPrev(NULL)
, pNext(NULL)
{
}
//
// List object
//
class CList
{
public:
//
// Contstructor
//
CList();
//
// methods to inspect list
//
Node* Head() const;
unsigned Size() const;
Node* Get(unsigned nPos) const;
void Print(std::ostream &os=std::cout) const;
//
// methods to modify list
//
void Insert(int nData);
void Insert(Node *pNew);
void Delete(unsigned nPos);
void Delete(Node *pDel);
private:
//
// Internal data
//
Node* m_pHead;
unsigned m_nSize;
};
/////////////////////////////////////////////////////////////////////////////////
CList::CList()
: m_pHead(NULL)
, m_nSize(0)
{
}
Node *CList::Head() const
{
return m_pHead;
}
unsigned CList::Size() const
{
return m_nSize;
}
void CList::Insert(int nData)
{
Insert(new Node(nData));
}
void CList::Insert(Node *pNew)
{
pNew->pNext = m_pHead;
if (m_pHead)
m_pHead->pPrev = pNew;
pNew->pPrev = NULL;
m_pHead = pNew;
++m_nSize;
}
void CList::Delete(unsigned nPos)
{
Delete(Get(nPos));
}
void CList::Delete(Node *pDel)
{
if (pDel == m_pHead)
{
// delete first
m_pHead = pDel->pNext;
if (m_pHead)
m_pHead->pPrev = NULL;
}
else
{
// delete subsequent
pDel->pPrev->pNext = pDel->pNext;
if (pDel->pNext)
pDel->pNext->pPrev = pDel->pPrev;
}
delete pDel;
--m_nSize;
}
Node* CList::Get(unsigned nPos) const
{
unsigned nCount(0);
for (Node *p=m_pHead; p; p = p->pNext)
if (nCount++ == nPos)
return p;
throw std::out_of_range("No such node");
}
void CList::Print(std::ostream &os) const
{
const char szArrow[] = " --> ";
os << szArrow;
for (Node *p=m_pHead; p; p = p->pNext)
os << p->nData << szArrow;
os << "NIL\n";
}
int main()
{
CList l;
l.Print();
for (int i=0; i<10; i++)
l.Insert((i+1)*10);
l.Print();
l.Delete(3);
l.Delete(7);
l.Print();
try
{
l.Delete(33);
}
catch(std::exception &e)
{
std::cerr << "Failed to delete 33: " << e.what() << '\n';
}
l.Print();
return 0;
}