C++ linked list append method - c++

I am writing a linked list template class in C++ as an exercise for myself to help me get back into C++ programming. I've got the following class definition:
template <typename T>
class List
{
public:
List();
~List();
void append(T data);
void display();
int count();
private:
struct Node
{
T data;
Node *next;
} *head;
};
I have two versions of the append method - one that works and one that doesn't. I can't figure out what the difference, in terms of the operations performed, is, and why the second one doesn't work. Here's the one that works:
template <typename T>
void List<T>::append(T data)
{
if (head == NULL)
{
head = new Node;
head->data = data;
head->next = NULL;
}
else
{
Node *p = head, *q;
while (p != NULL)
{
q = p;
p = p->next;
}
p = new Node;
p->data = data;
p->next = NULL;
q->next = p;
}
}
And here's the one that doesn't seem to actually add any elements to the list:
template <typename T>
void List<T>::append(T data)
{
Node *p = head, *q = head;
while (p != NULL)
{
q = p;
p = p->next;
}
p = new Node;
p->data = data;
p->next = NULL;
if (q != NULL)
{
q->next = p;
}
}
Any ideas as to why the second version doesn't add any elements? I've been trying it with type T as int.
P.S. Neither version gives any errors or warnings during compilation, nor during runtime.

The second method only handles the case where the list is non-empty.
When the list is empty, the line q->next = p; is never reached, so the new element is leaked with no pointer existing to it after p goes out of scope.
What you want, if you would like to eliminate the special case for empty list, is a Node **, like thus:
template <typename T>
void List<T>::append(T data)
{
Node** q = &head; /* head acts as the first Node::next link */
/* invariant: q points to some Node::next field (or head, which acts like one) */
while (*q)
q = &(*q)->next;
/* invariant: q points to the Node::next field at the end of the chain, which is currently NULL */
*q = new Node { data, nullptr };
}

In the first version you change the head, in the second - you don't.
Simpler would be:
template <typename T>
void List<T>::append(T data)
{
p = new Node;
p->data = data;
p->next = head;
head = p;
}
That would also be more logical because entering an item to a linked list shouldn't take O(n) as it does for you...
If you absolutely have to add to the end, do this:
template <typename T>
void List<T>::append(T data)
{
p = new Node;
p->data = data;
p->next = NULL;
if (tail)
tail->next = p;
else // first time
tail = head = p;
}

Related

insertion at the end of linked list function not working

I don't know where I am wrong, when I debugged the code I found out that the 'new node' address is 'new node' address, basically the new node is referring to itself
void insertend(struct node *parent, int item)
{
while (parent->addr != NULL)
parent = parent->addr;
struct node new_node;
new_node.a = item;
parent->addr = &new_node;
parent->addr->addr = NULL;
}
void insertend(struct node *parent, int item)
{
while (parent->addr != NULL)
parent = parent->addr;
struct node new_node;
new_node.a = item;
parent->addr = &new_node;
parent->addr->addr = NULL;
}
The lifetime of new_node is limited to the function. Once that function returns, it is no longer valid.
In order to circumvent this, it is necessary to dynamically allocate memory for new_node. Of course, as already pointed out, this means explicitly deallocating the memory eventually.
Note: as this is C++ rather than C, we do not need to add struct to the front of the type in use, and NULL is better spelled nullptr.
void insertend(node *parent, int item)
{
while (parent->addr != nullptr)
parent = parent->addr;
node *new_node = new node;
new_node.a = item;
parent->addr = new_node;
parent->addr->addr = nullptr;
}
As C++ structs are just classes with default public access, it's also worth noting we could implement this as a member function. Something like:
template <typename T>
struct Node {
T value;
Node<T> *next;
void append(T val) {
Node<T> * temp = this;
while (temp->next != nullptr) {
temp = temp->next;
}
temp->next = new Node<T>;
temp->next->value = val;
temp->next->next = nullptr;
}
};
int main() {
auto n = Node<int>();
n.value = 27;
n.append(42);
n.append(34);
for (Node<int> *t = &n; t != nullptr; t = t->next) {
std::cout << t->value << std::endl;
}
return 0;
}
The next step would be implementing a constructor and destructor.
One more thing to keep in mind is that getting to the end of a list this way is O(n) time complexity. Doing it over and over again is costly. If you have Node::append return a pointer to the new Node, then you can call append on that.
template <typename T>
struct Node {
T value;
Node<T> *next;
Node<T> *append(T val) {
Node<T> * temp = this;
while (temp->next != nullptr) {
temp = temp->next;
}
temp->next = new Node<T>;
temp->next->value = val;
temp->next->next = nullptr;
return temp->next;
}
};
int main() {
auto n = Node<int>();
n.value = 27;
auto n2 = n.append(42);
n2 = n2->append(34);
n2 = n2->append(15);
for (Node<int> *t = &n; t != nullptr; t = t->next) {
std::cout << t->value << std::endl;
}
return 0;
}
I’m not sure what the addr refers to, but I would assume it points to the next node in the linked list. In your code, there needs to be another node to use as a temporary node, because the parent node keeps being reassigned, and the previous items are lost. We also need to check that the parent node is not NULL.
void insertend(struct node **parent, int item)
{
node *new_node = new node;
new_node->a = item;
new_node->addr = NULL;
if(*parent == NULL)
{
*parent = new_node;
}
else
{
node *temp = *parent;
while (temp->addr != NULL)
temp = temp->addr;
temp->addr = new_node;
}
}
The if statement checks whether the parent node is NULL. If true, then we assign parent node to new_node. If false, then we go to the end of the list and assign the next node in the list to new_node.
As Igor correctly pointed out, your new node gets destroyed when the function finishes. So, in this context you could just allocate memory. You can use the new operator for this. However, you would need to explicitly free the memory, eventually.
void insertend(struct node *parent, int item)
{
while (parent->addr != NULL)
parent = parent->addr;
struct * new_node = new node;
new_node->a = item;
parent->addr = new_node;
parent->addr->addr = NULL;
}

Why am I getting "segmentation fault" when i try to run copy constructor?

The copy function executes when I quit the function and then prints the current contents of the circular linked list. What i'm trying to do is to have an object named other point to the original list before it is deleted (which is the original list before the program quits). Then i assign the data from the original list into the nodes of the new list. Since the first or head node is the largest I used while ( p->info != p->next->info ) as the condition to copy all of the contents before the node and then if (p->info == other.first->info) to identify the head node and then make that nodes data or info equal to the head of the node of the copied list.
'''
//Copy constructor function
template <class T>
void CLList<T> :: copy ( const CLList<T> & other )
{
if ( other.first == NULL )
first = NULL;
else
{
first = new node<T>;
first->info = other.first->info;
node<T> *p = other.first->next;
node<T> *r = first;
while ( p->info != p->next->info )
{
r->next = new node<T>;
r->next->info = p->info;
if (p->info == other.first->info)
{
r->next = new node<T>;
r->next->info = other.first->info;
}
// Node
template <class T>
struct node
{
T info;
node *next;
};
This code is all wrong for a copy constructor. The while loop is not actually iterating the input list at all, it is just copying the 1st node over and over. Also, the loop should not be comparing info values at all.
This code needs to be completely rewritten. Try something more like this instead:
template <class T>
CLList<T>::CCList(const CLList<T> &other)
: first(NULL)
{
node<T> *p = other.first;
node<T> **r = &first;
while (p)
{
*r = new node<T>;
(*r)->info = p->info;
(*r)->next = NULL;
r = &((*r)->next);
p = p->next;
// for a circular list only:
if (p == other.first) break;
}
// for a circular list only:
if (r != &first) *r = first;
}

My program stops working from a while loop when using dynamic allocated lists

I've been studying about dynamic allocated lists (the stuff we are discussing right now in class is pretty outdated tbh) and I can't seem to access the next node in a list. (I've just began learning this topic)
The problem is that the while loop that I'm using for going through the list never stops. I'm definitely missing something and I don't understand what.
struct node {
int info;
node* next;
};
int main()
{
node *p, *prim=NULL;
p = prim;
node* t;
t = new node;
p->next = t;
while (p != NULL)
{
cout << "test";
p = p -> next;
}
return 0;
}
Here is the code.
Why does my program not output anything and also tells me "it exited with code -1073741819" instead of 0?
Thanks.
////edit: I forgot to tell you that I've tried this way too
struct node {
int info;
node* next;
};
int main()
{
node *p, *prim=NULL;
p = prim;
node* t;
t = new node;
prim->next = t;
while (p != NULL)
{
cout << "test";
p = p -> next;
}
return 0;
}
Let's analyze your code:
node *p, *prim=NULL; // prim is NULL, p not yet initnialized
p = prim; // p now equal to NULL as well
node* t;
t = new node; // t is allocated
p->next = t; // NULL->next = t
So you're crashing on a null pointer when you try to dereference p->next for assignment.
It looks like you're trying to setup a basic linked list and loop through it. Your loop looks good, but you forgot the part where you actually setup the list!
That could look like:
struct node {
int info;
node* next;
//Add a quick constructor to make creating new nodes easy
node(int i) : info(i), next(nullptr) { }
};
int main()
{
//Start out with 3 itmes
// This list will look like:
// head -> 5 -> 3 -> 1
node *head = new node(5);
head->next = new node(3);
head->next->next = new node(1);
//Loop through the list and print each value
for(node *p = head; p; p = p->next) {
std::cout << p->info << std::endl;
}
//Don't forget to delete the memory you allocated to prevent a leak!
for(node *p = head; p;) {
node *temp = p->next;
delete p;
p = temp;
}
return 0;
}
See it run: https://ideone.com/WngD3b

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;
}

Memory leak, Node list C++

I have this code from an old exam, which I want fix from its faults. Everytime I call insertlast/insertfirst I allocate new memory for my list which I can't seem to free. I have run it with valgrind and gets a new leak everytime i call insertfirst/insertlast. Valgrind also complains on my loop where I try to free the memory. I get something like this:
Invalid free() / delete / delete[] / realloc()
==4548== at 0x4C2C2BC: operator delete(void*)(in/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4548== by 0x4007D6: main (code6.cpp:67)
==4548== Address 0xffefffea0 is on thread 1's stack
Here's the code:
#include <iostream>
using namespace std;
template <class T>
struct Node {
Node() : data(T()), next(nullptr) {}
Node(T d, Node * p) : data(d), next(p) {}
T data;
Node<T> * next;
};
template <class T>
void insertlast (Node<T> * p, T data) {
if (p == nullptr) {
p = new Node<T>(data, nullptr);
} else {
while (p -> next != nullptr) {
p = p -> next;
}
p -> next = new Node<T>(data, nullptr);
}
}
template <class T>
void insertfirst (Node<T> * & p, const T & data) {
Node<T> * tmp = new Node<T>(data, p);
p = tmp;
}
template <class T>
void printNode(Node<T> *& node) {
cout << node->data << " -> ";
while (node->next != nullptr) {
node = node->next;
cout << node->data << " -> ";
}
cout << endl;
}
template <class T>
void printNode2(Node<T> & node) {
cout << node.data << " -> ";
while (node.next != nullptr) {
node = *node.next;
cout << node.data << " -> ";
}
cout << endl;
}
int main() {
Node<int> node;
Node<int> * temp = &node;
Node<int> * ref = &node;
insertlast(ref, 5);
insertfirst(ref, 3);
insertlast(ref, 6);
insertfirst(ref, 2);
//printNode(ref);
//printNode2(node);
while(ref->next != nullptr){
temp = ref->next;
delete ref;
ref = temp;
}
return 0;
}
It would be cool if you could help me out to find out what's wrong with the code. My guess is that it's something fishy with the pointer reference in insertfirst, but I can't figure it out.
Thanks in advance!
When you call insertfirst the first time, you will create a new node whose next pointer is the statically allocated node object from main, then when you loop over the list to delete the nodes in it, you will try to delete this node object.
What you should do, if you want a sentinel object at the head of the list, is to make its next pointer point to the new node, and the new node should have its next pointer set to p->next. So something like
template <class T>
void insertfirst (Node<T> * head, const T & data) {
Node<T> * tmp = new Node<T>(data, head->next);
head->next = tmp;
}
Finally a hint on how to debug these things: Draw it on paper! After every operation, draw it out on paper, and problems like this would have been very easy to spot.
In your insertlast function, if the argument p is NULL, then your code will leak an instance of Node. You create the new instance and assign its address to p, but once you leave the function, p goes out of scope and you no longer have a pointer to the newly created instance.
I suppose you wanted to pass a reference to p like in your insertfirst function, but forgot to put the & char in there.
There is inconsistent interface of these two functions
template <class T>
void insertlast (Node<T> * p, T data) {
if (p == nullptr) {
p = new Node<T>(data, nullptr);
} else {
while (p -> next != nullptr) {
p = p -> next;
}
p -> next = new Node<T>(data, nullptr);
}
}
template <class T>
void insertfirst (Node<T> * & p, const T & data) {
Node<T> * tmp = new Node<T>(data, p);
p = tmp;
}
In the first function parameters are declared like Node<T> * p, T data while in the second function they are declared like Node<T> * & p, const T & data
In the first function you also shall pass the first parameter by reference. Otherwise p is a local variable of the function and changes of it will not influence to the original argument.
In can be defined the following way
template <class T>
void insertlast ( Node<T> * &p, const T &data )
{
if ( p == nullptr )
{
p = new Node<T>(data, nullptr);
}
else
{
Node<T> *tmp = p;
while ( tmp->next != nullptr )
{
tmp = tmp->next;
}
tmp->next = new Node<T>( data, nullptr );
}
}
Also you have to initialize the first node in main to nullptr
Node<int> *node = nullptr;
It is a bad idea to define the first node of the list in the stack. It is simply an invalid design.