I tried to search the topic but all the threads I found used while loops.
However I would like to do this recursively:
template <typename S>
struct node {
S data;
node<S> * next;
};
this is the function I invoke in the destructor (pass the head as parameter) of the linked list:
void destroy(node<T> * n) {
if(n->next != NULL){
destroy(n->next);
}
delete n;
}
Unfortunately the result is a segmentation fault.
Can someone help me?
Edit: complete code
#include <iostream>
using namespace std;
template <typename T>
class List {
private:
template <typename S>
struct node {
S data;
node<S> * next;
};
node<T> * first;
node<T> * get_last_p() {
if(first != NULL){
return get_last(first);
}
return NULL;
}
node<T> * get_last(node<T> * n) {
if(n->next != NULL) {
return get_last(n->next);
} else {
return n;
}
return NULL;
}
void destroy(node<T> * n) {
if(n->next != NULL){
destroy(n->next);
}
delete n;
}
public:
List() {first->next = 0;}
~List() {destroy(first);}
void add(T element) {
node<T> * new_element = new node<T>;
new_element->data = element;
if(first == NULL){
first = new_element;
} else {
get_last_p()->next = new_element;
}
}
T get_last() {
return get_last_p()->data;
}
T get_first() {
return first->data;
}
};
From what I can see, in List's constructor, first is not initialized, and is then immediately accessed. That is undefined behavior.
Even if first was somehow initialized to null in an unreliable way, and that first->next = 0; wouldn't crash somehow, you'd also fail in your destructor's destroy, since destroy assumes its original argument is not null.
I assume you meant to
List() : first{ new node{} } { first->next = nullptr; }
If first is not meant to hold a value, then you're going to have to refactor your code to first initialize first to null - there's no working around that - and handle the case where first is null explicitely in all your code. You cannot assign first->next of a null, invalid or undefined pointer.
Related
I have written a linked list(which is aimed for the data type int) implementation.
Seems to be working fine except when I try to sort the list in such a way that all the odd
elements should come after all the even elements with the original order of the even and odd numbers preserved.
Upon debugging in MS Visual Studio, I found out that in the oddevenSort() function, the for loop seems to be going on infinitely...as if somehow the tail->next was not being updated to nullptr.
I can't seem to grasp where the error lies in my logic.
#include<iostream>
template<class T>
class SLL_Node
{
public:
T info;
SLL_Node* next;
SLL_Node();
SLL_Node(T el, SLL_Node<T>* n = nullptr);
};
template<class T>
class SLL
{
private:
SLL_Node<T>* head, * tail;
size_t size;
public:
SLL();
~SLL();
bool isEmpty() const;
size_t get_size() const;
void add_to_head(T el);
void add_to_tail(T el);
void delete_at(size_t); //delete at a certain index. Index starting from 1. Throws an error //message if index out of bounds or list empty.
void display()const; //the logic is for mostly primitive data types and not user defined data //types (including classes)
void oddevenSort();
};
template<class T>
bool SLL<T>::isEmpty() const
{
if (tail == nullptr)
return true;
else
return false;
}
template<class T>
SLL_Node<T>::SLL_Node() : next{ nullptr }
{}
template<class T>
SLL_Node<T>::SLL_Node(T el, SLL_Node<T>* n) : info{ el }, next{ n }
{}
template<class T>
SLL<T>::SLL()
{
size = 0;
head = tail = nullptr;
}
template<class T>
void SLL<T>::add_to_tail(T el)
{
++size;
if (!isEmpty())
{
tail->next = new SLL_Node<T>(el);
tail = tail->next;
}
else
head = tail = new SLL_Node<T>(el);
}
template<class T>
void SLL<T>::add_to_head(T el)
{
head = new SLL_Node<T>(el, head);
if (tail == nullptr) //if empty
{
tail = head;
}
++size;
}
template<class T>
void SLL<T>::display()const
{
std::cout << '\n';
for (SLL_Node<T>* tmp{ head }; tmp != nullptr; tmp = tmp->next)
{
std::cout << tmp->info << "->";
}
std::cout << "NULL\n";
}
template<class T>
void SLL<T>::delete_at(size_t index)
{
if (index >= 1 && index <= size) //bound checking
{
if (!isEmpty()) //we dont need is empty since size takes care of that but still adding it for clarity
{
if (head == tail && index == 1) //if list only has one node and we delete head node
{
delete head;
head = tail = nullptr;
}
//otherwise if list more than one node
else if (index == 1) //if deleting head node
{
SLL_Node<T>* tmp{ head };
head = head->next;
delete tmp;
tmp = nullptr;
}
else //deleting other nodes
{
SLL_Node<T>* tmp{ head->next }, * pred{ head };
for (size_t i{ 2 }; i < index; ++i)
{
tmp = tmp->next;
pred = pred->next;
}
pred->next = tmp->next;
if (tmp == tail)
{
tail = pred;
}
delete tmp;
tmp = nullptr;
}
}
}
else
{
std::cout<<"\nError! Either the list is empty or the index entered is out of bounds!\n";
}
}
template<class T>
void SLL<T>::oddevenSort()
{
SLL_Node<T>* t=head;
size_t count{1};
for (; t != nullptr; t = t->next)
{
if (((t->info) % 2) != 0)
{
add_to_tail(t->info);
delete_at(count);
}
++count;
}
}
main:
int main()
{
SLL<int> a;
a.add_to_head(1);
a.add_to_head(2);
a.add_to_tail(3);
a.add_to_tail(4);
a.add_to_head(6);
a.add_to_tail(7);
a.add_to_head(5);
a.display();
//a.oddevenSort();
a.display();
return 0;
}
Consider an example input for oddevenSort a(1)->b(2) ->c(3)->null
on 1st iteration t is pointing to a(1) new node is created with
data 1 which is appended at the end of list like
b(2)->c(3)->d(1)->null.
On 2nd iteration t will point to node b(2) and no changes done
on list.
On 3rd iteration t will point to node c(3) new node is created
with data 3 which is appended at the end of list like
b(2)->d(1)->e(3)->null.
on 4th iteration t will point to d(1) which creates new node at the end of list. Iteration goes on and on recursively without breaking the loop.
Every time you need not to delete and create the new node. You can segregate even and odd nodes and make final list.
Here is the updated snippet
template<class T>
void SLL<T>::oddevenSort()
{
SLL_Node <T>tempOddHeader;
SLL_Node <T> *tempOddPtr = &tempOddHeader;
SLL_Node <T> tempEvenHeader;
SLL_Node <T> *tempEvenPtr = &tempEvenHeader;
SLL_Node<T>* t = head;
tempOddHeader.next = nullptr;
tempEvenHeader.next = nullptr;
while(t)
{
if (((t->info) % 2) != 0) {
//append to the odd list
tempOddPtr->next = t;
tempOddPtr = tempOddPtr->next;
t = t->next;
tempOddPtr->next = nullptr;
}
else {
//append to the even list
tempEvenPtr->next = t;
tempEvenPtr = tempEvenPtr->next;
t = t->next;
tempEvenPtr->next = nullptr;
}
}
tempEvenPtr->next = tempOddHeader.next;
head = tempEvenHeader.next;
tail = tempOddPtr;
}
i'm trying to make a template class for a list in c++. This is something new for me, and i'm stuck. I've written add(T item) method and write() to write whole list on console, but i have random int as output.
Here's my code, if someone could tell me where my mistake is, i will be grateful.
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
template <class T> class Node {
private:
Node<T> * next;
Node<T> * prev;
T key;
public:
Node(T k) {
next =(Node *) malloc(sizeof(Node));
prev = (Node *)malloc(sizeof(Node));
key = k;
}
Node(T * n, T * p, T k) {
next = n;
prev = p;
key = k;
}
void setNext(Node<T> * n) {
next = n;
}
void setPrev(Node<T> * p) {
prev = p;
}
void show() {
cout << "key= " << key << endl;
}
};
template <class T> class List {
private:
Node<T> * head;
Node<T> * tail;
public:
List() {
head = nullptr;
tail = nullptr;
cout << "list created!" << endl;
}
void add(T item) {
Node<T> node(item);
node.setNext(head);
node.setPrev(nullptr);
if (head != nullptr)
head = &node;
else {
tail = &node;
}
head = &node;
}
void write() {
head->show();
}
};
int main()
{
List<int> lista;
lista.add(8);
lista.write();
return 0;
}
Your add() add a pointer to a Note<T> (node) in the list; but node is destroied when add() ends the execution.
So, when you call write(), that use show(), the pointed memory in undefined (random value).
To avoid this problem, you have to allocate the variable (with new! avoid malloc() with classes), so
Node<T> * nodePnt = new Node<T>(item);
and
head = nodePnt;
tail = nodePnt;
But remeber to delete it.
I need to print out the number of nodes in a linked list. My teacher said that the linked list keeps track of its data and "knows" how many nodes are in it. So, I should not need a while loop to determine the size of the linked list. I have trouble figuring out a way other than a while loop to print out the size.
this is the linked list:
template <class T>
class LinkedList
{
private:
struct ListNode
{
T data ;
struct ListNode * next;
};
ListNode *head;
public:
LinkedList() { head = nullptr; }
~LinkedList();
// Linked list operations
void insertNode(T);
bool deleteNode(T);
void displayList() const;
};
/////////// Implementation portion of linked list with template //////////////
// displayList: print all list data
template <class T>
void LinkedList<T>::displayList() const
{
ListNode * ptr = head;
while (ptr != nullptr)
{
cout << ptr->data << endl;
ptr = ptr->next;
}
}
// insertNode: add a node in list order
template <class T>
void LinkedList<T>::insertNode(T newValue)
{
ListNode *newNode;
ListNode *pCur;
ListNode *pPre = NULL;
newNode = new ListNode;
newNode->data = newValue;
newNode->next = nullptr;
if (head == nullptr)
{
head = newNode;
}
else
{
pCur = head;
pPre = nullptr;
while (pCur != nullptr && pCur->data < newValue)
{
pPre = pCur;
pCur = pCur->next;
}
if (pPre == nullptr)
{
head = newNode;
newNode->next = pCur;
}
else
{
pPre->next = newNode;
newNode->next = pCur;
}
}
}
// deleteNode: delete a node if found
template <class T>
bool LinkedList<T>::deleteNode(T toBeDeleted)
{
ListNode *pCur;
ListNode *pPre;
if (!head)
return true;
pCur = head;
pPre = NULL;
while (pCur != NULL && pCur->data < toBeDeleted)
{
pPre = pCur;
pCur = pCur->next;
}
if (pCur != NULL && pCur->data == toBeDeleted)
{
if (pPre)
pPre->next = pCur->next;
else
head = pCur->next;
delete pCur;
return true;
}
return false;
}
// destructor, delete all nodes
template <class T>
LinkedList<T>::~LinkedList()
{
ListNode *ptr = head;
while (ptr != NULL)
{
head = head->next;
delete ptr;
ptr = head;
}
}
Using the code you've defined, the size of the list is not stored by the list directly. Further to this, the main advantage of linked list is that each node does not know about the rest of the list, and storing the size would defeat the purpose of this.
However, you may have misunderstood what was asked of you in terms of not using a while loop. Each node knows that it's length is 1+(the length of it's tail), and so the more suitable implementation for getting the length of a linked list is recursion, not iteration.
Here is an example of a very simple LinkedList class, that implements the simple methods using recursion. As you can see, the code uses no iteration, only making a check for it's own data, then calling the same method for the next node. Although recursion in procedural languages is less efficient in most cases, for structures like this there is no doubting it is elegant.
#include <iostream>
template<class T>
class LinkedList
{
private:
T data;
LinkedList* next;
public:
LinkedList()
: LinkedList(T()) {
}
LinkedList(T value)
: data(value), next(nullptr) {
}
~LinkedList() {
delete next;
}
void insertNode(T newValue) {
if (!next) {
next = new LinkedList(newValue);
return;
}
next->insertNode(newValue);
}
void displayList() const {
std::cout << data << std::endl;
if (next) {
next->displayList();
}
}
T& at(int N) {
if (N == 0) {
return this->data;
}
return next->at(N-1);
}
int size() {
if (!next) {
return 1;
}
return 1+next->size();
}
};
int main(int argc, char const *argv[])
{
LinkedList<int>* test = new LinkedList<int>(0);
for (int i = 1; i < 10; ++i) {
test->insertNode(i);
}
std::cout << "List of length: " << test->size() << std::endl;
test->displayList();
return 0;
}
You'll notice I haven't included deleteNode, that's because writing it for the oversimplified class above is not possible for the case where the list only has one node. One possible way to implement this is to have a wrapper class, much like you in the original code, that is a pointer to the start of a linked list. See here.
I'm trying to implement those two functions "extract" and "deleteList" and I somehow can't get my head around it.
I know how a linked-list works but I'm new to programming and just can't figure out an algorithm. Could I ask for some tips?
I want the extract function to return a list of values that fulfill the predicate and remove those from the original list (thats why its passed as a reference).
I want the delete function to delete all those that are the same as those passed from the argument
My code looks like this:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
struct Node
{
T data;
Node* next;
};
template <typename T>
void showList(const Node<T>* head)
{
while(head->next != NULL)
{
cout<<head->data<<" ";
head = head->next;
}
cout<<endl;
}
template <typename T>
Node<T>* arrayToList(const T tab[], size_t size)
{
Node<T> *prev;
for(int i = size-1; i>=0 ; i--){
Node<T> *p = new Node<T>;
p->data = tab[i];
p->next = prev;
prev = p;
}
return prev;
}
template<typename T>
Node<T>* extract(Node<T>*& head, bool (*predicate)(const T&))
{
}
template <typename T>
void deleteList(Node<T>*& head)
{
//delete passed in
}
bool isEven(const int& n)
{
return n%2 == 0;
}
bool isLong(const string& s)
{
return s.size() >=5;
}
Thank you guys!
Firstly, your showList() has a potential problem with dereferencing
a null pointer. So it should look like this:
template <typename T>
void showList(const Node<T>* head)
{
while(head != NULL)
{
cout << head->data << " ";
head = head->next;
}
cout << endl;
}
Secondly, it is more convenient to have auxiliary functions like
pushToList() and popFromList():
template <typename T>
void pushToList(Node<T>*& head, const T &element)
{
Node<T> *p = new Node<T>;
p->data = element;
p->next = head;
head = p;
}
template <typename T>
T popFromList(Node<T>*& head)
{
T value;
if (head != NULL) {
Node<T>* tmp = head;
value = head->data;
head = head->next;
delete tmp;
}
return value;
}
You can rewrite your arrayToList() using pushToList():
template <typename T>
Node<T>* arrayToList(const T tab[], size_t size)
{
Node<T>* head = NULL;
for(int i = size - 1; i >= 0; i--){
pushToList(head, tab[i]);
}
return head;
}
And implement deleteList() using popFromList():
template <typename T>
void deleteList(Node<T>*& head)
{
while (head != NULL) {
popFromList(head);
}
}
exctract() also can be implemented in terms of push and pop. You
just create two temporary lists and push to them according to the
predicate:
template<typename T>
Node<T>* extract(Node<T>*& head, bool (*predicate)(const T&))
{
Node<T> *extracted = NULL;
Node<T> *rest = NULL;
while (head != NULL) {
T value = popFromList(head);
if (predicate(value)) {
pushToList(extracted, value);
} else {
pushToList(rest, value);
}
}
reverseList(extracted);
reverseList(rest);
head = rest;
return extracted;
}
One problem is after the main work the temporary lists are
reversed. So we need the reverseList() function, which can be also
implemented in terms of push and pop:
template <typename T>
void reverseList(Node<T>*& head)
{
Node<T> *result = NULL;
while(head != NULL) {
pushToList(result, popFromList(head));
}
head = result;
}
It is not a very effective implementation but I think it does the work.
I've put the complete source code here.
MySinglyLinkedList.h:
#include <iostream>
template<class T> class LinkedList;
template<class T>
class LinkedNode {
public:
LinkedNode(T new_data):data(new_data) {; }
private:
friend class LinkedList<T>;
LinkedNode<T> *next;
T data;
};
template<class T>
class LinkedList {
public:
LinkedList();
~LinkedList();
void PushNode(T new_data);
void Delete(LinkedNode<T> *pnode);
void Show();
private:
LinkedNode<T> *head; //Head pointer
LinkedNode<T> *tail; //Tail pointer
int length; //Length of the list
};
//Initialize an empty list when creating it
template<class T>
LinkedList<T>::LinkedList()
{
head = tail = NULL;
length = 0;
}
//delete all the nodes when deconstructing the object
template<class T>
LinkedList<T>::~LinkedList()
{
LinkedNode<T> *ptr = head;
while (ptr)
{
LinkedNode<T> *ptr_del = ptr;
ptr = ptr->next;
Delete(ptr_del);
}
}
//Add one node to the tail of the list
template<class T>
void LinkedList<T>::PushNode(T new_data)
{
LinkedNode<T> *pnew_node = new LinkedNode<T>(new_data);
pnew_node->next = NULL;
if (!length) {
head = tail = pnew_node;
length++;
} else {
tail->next = pnew_node;
tail = pnew_node;
length++;
}
}
//Delete the node pointed by pnode
template<class T>
void LinkedList<T>::Delete(LinkedNode<T> *pnode)
{
LinkedNode<T> *ptr = head;
if (pnode==head) {
head = pnode->next;
} else {
while(ptr->next != pnode)
{
ptr = ptr->next;
}
ptr->next = pnode->next;
}
if(pnode == tail)
tail = ptr;
delete pnode;
length--;
}
template<class T>
void LinkedList<T>::Show() //Print all the contents in the list
{
LinkedNode<T> *pnode = head;
while(pnode)
{
std::cout << pnode->data << std::endl;
pnode = pnode->next;
}
std::cout << "In total: " << length << std::endl;
}
The main function is as follows:
#include "MySinglyLinkedList.h"
#include <cstdlib>
#include <ctime>
using namespace std;
int main(int argc, char *argv[])
{
//list_len is the length of the list
int list_len = 5;
srand(time(0));
if (argc > 1)
list_len = atoi(argv[1]);
LinkedList<int> test_list; //Create the first list: test_list
for (int i = 0; i < list_len; i++)
{
//The elements in the list are random integers
int cur_data = rand()%list_len;
test_list.PushNode(cur_data);
}
test_list.Show();
LinkedList<int> test2 = test_list; //Create the second list: test2
test2.Show();
return 0;
}
Since I didn't define any copy constructor here, the default copy constructor will be called and do the bit copy when creating the second list, and as a result test_list and test2 will point to the same linked list. Therefore the first node of the list will be deleted twice when the two object are deconstructed. But the fact is nothing wrong happened when I compiled the program using GCC, and it ran successfully in Linux. I don't understand why no error occurred.
Deleting a pointer that has already been deleted causes undefined behavior, so anything can happen. If you want to make sure nothing happens, set the pointer to null after deletion. Deleting null does nothing, and it won't cause an error.
See: c++ delete (wikipedia)
As per Diego's answer, the C++ standard permits deleting NULL pointers. The compiler has no way to know what value your pointer will contain when you delete it the second time (i.e. it might be NULL), therefore it has no choice but to allow it in order to comply with the standard.