Deep copy and deconstructor sentinel linked list using Nodes - c++

I am currently making a linked List program using Nodes(not that i know of any other way) and I have come upon a problem about creating a deep copy and getting rid of all my Nodes and Sentinels with my ~List(). Deleting the Nodes is not a problem, but the sentinels are since the first one is not assigned a index value.
List::~List()
{
for(size_t i=0; i<size; i++)
{
_setCurrentIndex(i);
if(current && curent->next == NULL)
{
Node *temp = current->next;
delete temp;
delete current;
}
else
{
Node *old = current;
current = current->next;
delete old;
}
}
}
List::List(const List & orig)
{
for(size_t i=0; i<size; i++)
{
if(i==0)
{
Node *copyFront = new Node; //the first sentinel
copyFront->data = orig.front->data; //front is defined in private in list.h
copyFront->prev = NULL; // it is defined as a Node (same for rear)
}
else if(0<=i && i<size) //put in i<size b/c 0<=i would always be true
{
_setCurrentIndex(i) //sets what current is and currentIndex which pts to diff Nodes
Node *copy = new Node;
copy->data = current->data;
copy->next = current->next;
current = current->next;
}
else if(i+1 == size)
{
Node *copyRear = new Node; //making the last sentinel, but it has to be
copyRear->data = orig.rear->data; //after data Node
copyRear->next = NULL;
}
}
}
I am seeking advice and comments on this code on how to proceed next or what to change if something is dreadfully wrong!

Linked lists are templates allowing any type of variable to sit in them. In my honest opinion, you'd be best off using the std::list which requires the #include <list> header file.
Of course, if you really want the experience of writing a linked list class yourself then the following code makes a deep copy of a list:
List::List( const List& other) {
if( other.head_ != nullptr) {
head_ = new Node( other.head_->item_); // copy first node
assert( head_ != nullptr); // ensure that the memory was allocated correctly
// copy the rest of the list
Node* pnew = head_;
// loop through the list until you reach the end (i.e. a node that's nullptr)
for( Node* porig( other.head_->next_); porig != nullptr; porig = porig->next_) {
// assign the next node in the destination list to the next node in the paramter's list
pnew->next_ = new Node( porig->item_);
assert( pnew->next_ != nullptr); // ensure that the memory was allocated correctly
pnew = pnew->next_; // move onto the newly created node in the destination list
}
}
else
// if the parameter is empty then the destination list will be empty as well
head_ = nullptr;
}
As for the destructor, you just need to run through the list deleting the nodes as you go:
List::~List() {
while( head_ != nullptr) { // keep looping until the list gets to the end
// make a second pointer to the node you are about to delete (so you don't lose track of it)
Node* pn( head_);
// move the head_ onto the next node essentially "removing" the first node from your list
head_ = head_->next_;
// delete the node that you've just "removed" from your list
delete pn;
}
}
I've tried to make the comments clear up anything that might be unclear.

Related

Pointer in deleting a node in single linked list

In place of *head_ref = temp->next;, why can't I assign it as *head_ref = *head_ref->next?
Why should I use temp? Aren't they pointing to the same place?
class Node{
public:
int data;
Node* next;
};
void deleteNode(Node** head_ref, int key){
Node* temp = *head_ref;
Node* prev = NULL;
if(temp!=NULL && temp->data==key){
*head_ref = temp->next;
delete temp;
return;
}
else{
while(temp!=NULL && *head_ref->data!=key){
prev = temp;
temp = temp->next;
}
}
Your code does not compile, *head_ref->data should be (*head_ref)->data.
The reason why you should use temp is that you want to modify *head_ref only if the element you want to delete is the head element. If you delete any other element of the list, the head pointer must stay the same.
But your code is wrong anyway. You're doing things in the wrong order. You must first find the element you want to delete, and then handle the deletion.
Your code handles the deletion first and then finds the element to delete which is absurd.
You want this:
void deleteNode(Node** head_ref, int key) {
Node* current = *head_ref;
Node* previous = NULL;
// find element to delete
while (current && current->data != key)
{
previous = current;
current = current->next;
}
// if current is NULL here then the element has not been found
if (current != NULL)
{
// element found,
// current points to element found
// previous points to previous element or NULL if current is head
if (previous == NULL)
{
// deleting head element -> we need to update head_ref
*head_ref = current->next;
}
else
{
// deleting any other element -> update next pointer of previous element
previous->next = current->next;
}
delete current;
}
}
That being said, this is rather C code than C++ code. You should use standard containers rather than making your own, or at least use C++ idioms such as constructors.

C++ Deep Copying Linked List

First of all, this is part of an assignment I'm currently trying to figure out. I'm trying to create a copy constructor that deep copies a given LinkedList.
I have coded the LinkedList methods already.
Here's the necessary parts of the LinkedList.h file.
LinkedList.h
private:
struct node {
Val data;
node* next = nullptr;
};
typedef struct node* nodePtr;
nodePtr head = nullptr;
nodePtr current = nullptr;
nodePtr temp = nullptr;
};
The parameters are given: "LinkedList::LinkedList(const LinkedList & ll)"
ll is the linked list to be copied.
I first tested if there is a head in the linked list, if not then that means the linked list is empty.
Then I copied the head from the old list to the new list.
I then set the new current to the head in preparation for the while loop.
Inside the while loop, I am copying the data of the current node as well as the pointer to the next node.
At the end I set the next pointer to nullptr to signify the end of the new list.
LinkedList.cpp
LinkedList::LinkedList(const LinkedList & ll){
if (ll.head == nullptr) {
return;
}
head = ll.head;
current = head;
while (ll.current->next != nullptr) {
current->data = ll.current->data;
current->next = ll.current->next;
}
current->next = nullptr;
}
I'm not sure if this is deep copying or not.
I also know that ll.current's starting position is not at the head.
I tried ll.current = ll.head. However, since it is given that this function is const. I can't set it like that.
There is also another function given:
LinkedList & LinkedList::operator=(const LinkedList & ll)
{
}
That I suspect may be needed. I'm hoping it optional that I use this.
You need to allocate new memory or new list elements as you add them, change your code to do the following:
// LinkedList.cpp
LinkedList::LinkedList(const LinkedList & ll)
{
if (ll.head == nullptr)
return;
// Create a temp variable since ll.current doesn't move/change.
node* tmp = ll.head;
// Allocate a new node in memory.
head = new node;
// Copy over the value.
head->data = tmp->data;
// Set the 'next' value to null (the loop will fill this in).
head->next = nullptr;
// Point 'current' to 'head'.
current = head;
// Move to next item in ll's list.
tmp = tmp->next;
while (tmp != nullptr)
{
// Allocate new memory for a new 'node'.
current->next = new node;
// Point to this new 'node'.
current = current->next;
// Copy over the data.
current->data = tmp->data;
// By default set the 'next' to null.
current->next = nullptr;
// Move along ll's list.
tmp = tmp->next;
}
}
Also, in your class get rid of typedef node* nodePtr. There is no need for that, it's cleaner to simply use node* for head, current and temp. Lastly, don't forget in your class' destructor to clear out dynamically allocated memory:
LinkedList::~LinkedList()
{
current = head;
while(current != nullptr)
{
current = current->next;
delete head;
head = current;
}
}
This cannot work, as you never allocate new list elements for the actual list object (using the 'new' operator), but only reuse existing ones. Just think about what happens, if ll has more elements than the actual list?

How do I Implement the Big Three Correctly: Singly Linked List (C++)

I am writing a program that contains a singly linked list to hold a shopping list. Each node has the item name, quantity, and quantity description (i.e. dozen for eggs). Everything works find in the program except the destructor. I can't seem to find what is wrong with it though.
The driver will execute to the end where the code is return 0;, then the destructor is called and stops on the line delete current; with the message:
"Unhandled exception at 0x0FC7A9E8 (msvcr120d.dll) in Project 14.exe: 0xC0000005: Access violation reading location 0xFEEEFEE2.".
I've posted the implementation for the big three functions below. The default constructor initializes both pointers (first, last) as null and the nodeCount as 0.
I can't seem to find the problem. Any help?
List::List(const List& b)
{
Node* newNodePtr = new Node;
Node* nodeCopy = b.first;
newNodePtr = nodeCopy;
first = newNodePtr;
last = newNodePtr;
nodeCount++;
nodeCopy = nodeCopy->getNext();
while (last != b.last)
{
Node* newNode = new Node;
newNode = nodeCopy;
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
nodeCount++;
nodeCopy = nodeCopy->getNext();
}
}
List::~List()
{
Node* current = first;
while (current != nullptr)
{
Node* _next = current->getNext();
delete current;
current = _next;
}
first = nullptr;
last = nullptr;
}
List& List::operator=(const List& rho)
{
Node* current = first;
while (current != nullptr)
{
Node* _next = current->getNext();
delete current;
current = _next;
}
first = nullptr;
last = nullptr;
Node* newNodePtr = new Node;
Node* nodeCopy = rho.first;
newNodePtr = nodeCopy;
first = newNodePtr;
last = newNodePtr;
nodeCount++;
nodeCopy = nodeCopy->getNext();
while (last != rho.last)
{
Node* newNode = new Node;
newNode = nodeCopy;
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
nodeCount++;
nodeCopy = nodeCopy->getNext();
}
return *this;
}
EDIT: I've also added my push_back function as it is written:
void List::push_back(Node* newNode)
{
if (first == nullptr)
{
first = newNode;
last = newNode;
}
else
{
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
}
nodeCount++;
}
Alright I think I've figured in out. This code seems to work and it fits the driver provided by my professor. Below I've included the big three functions and all of the other functions they call:
List::List(const List& b)
{
this->copyList(b);
}
List::~List()
{
this->clearList();
}
List& List::operator=(const List& rho)
{
this->clearList();
this->copyList(rho);
return *this;
}
void List::clearList()
{
Node* current = first;
while (current != nullptr)
{
current = pop_front();
delete current;
current = first;
}
first = nullptr;
last = nullptr;
}
void List::copyList(const List& b)
{
first = nullptr;
last = nullptr;
nodeCount = 0;
Node *headNode = b.getFirst();
while (headNode != nullptr)
{
string des = headNode->getDescription();
string qNa = headNode->getQuantityName();
int qNu = headNode->getQuantityNumber();
Node* newNode = new Node(qNu, qNa, des);
push_back(newNode);
headNode = headNode->getNext();
}
}
Node* List::pop_front()
{
Node* saveFirst = first;
first = first->getNext();
nodeCount--;
return saveFirst;
}
void List::push_back(Node* newNode)
{
if (nodeCount == 0)
{
first = newNode;
last = newNode;
}
else
{
Node* currentNode = last;
currentNode->setNext(newNode);
last = newNode;
}
nodeCount++;
}
This may not solve your exact issue, but if you have the functions you mentioned, then pseudo-code to a copy constructor would look like this.
List::List(const List& b)
{
Node *headNode = b.getHeadNode();
while (headNode != NULL)
{
push_back(headNode->getDataFromNode());
headNode = headNode->getNextNode();
}
}
So basically, that in a nutshell is the entire copy constructor. You're basically starting from the first node in the list, getting the data from that node, and calling your push_back() to add the new data. I'm assuming that push_back() does all of the tricky work of creating a node, adding the data to it, and placing it correctly at the back of the list.
Note how small, compact, and intuitive this implementation is. We know that all we need to do to create a copy of a linked list is to first make sure the list is empty (which it is for a new object), and keep adding the items from the old list to the new list. Since push_back() adds an item to a list (and has all the complexity of creating a node and linking it to the end node), we use it in a smart way to create our copy.
Note that you also need an assignment operator to go along with the copy constructor. The assignment operator in the example is simply calling clear() (if you have such a function) to remove all the nodes before proceeding.
Remember that all of this requires that your push_back() function works flawlessly. It should know how to properly handle inserting at the end of an empty and non-empty list.
Edit:
If your driver code does create the new node before the push_back (therefore push_back doesn't allocate the new node), then the alternative code can be used:
List::List(const List& b)
{
Node *headNode = b.getHeadNode();
while (headNode != NULL)
{
Node *newNode = new Node(headNode->getDataFromNode());
push_back(newNode);
headNode = headNode->getNextNode();
}
}
In the alternate version, I'm assuming that the constructor for the new node can be created with the data as an argument. I personally don't like the design of having push_back() not do all of the work of creating the node, but that's another issue.
It will depend at least in part on what first is pointing to when you call the destructor.
Your code is not copying the contents of the nodes. Instead it is only manipulating pointers so, as dyp pointed out, you have a leak with:
Node* newNodePtr = new Node;
Node* nodeCopy = b.first;
newNodePtr = nodeCopy;
You might want to read up on the copy-swap idiom.
What is the copy-and-swap idiom?

C++ constructor that deep copies a linked list

I am a noob to C++ and programming in general and am trying to make a constructor which will duplicate a linked list. The idea is that I can use
Individual* copyOfList = new Individual(originalList->getFirstBit());
to make a deep copy of the original list.
But my cose below seems not to be doing a deep copy. When I edit the copyOfList the originalList is affected as well. And I don't understand linked lists enough to make it deep copy. Can someone help me please.
Individual::Individual(BinaryNode * copyHead)
{
head = copyHead;
NodePtr last = NULL;
NodePtr temp = NULL;
curr = head;
while (curr != NULL)
{
temp = new BinaryNode(curr->data, NULL);
if (last != NULL)
{
last->next = temp;
}
last = temp;
if (head == NULL)
{
head = temp;
}
curr = curr->next;
}
}
Here is the BinaryNode code
class BinaryNode
{
public:
BinaryNode();
BinaryNode(bool the_data, BinaryNode *next_link);
bool data;
BinaryNode *next;
private:
};
This is the original list code. I think the order I populated it is adding to the head.
if(the_length > 0)
{
srand(time(NULL));
int randnumber;
NodePtr temp = new BinaryNode;
for(int i = 0; i < the_length; i++)
{
randnumber=(rand() % 2);
temp = new BinaryNode(randnumber,head);
head = temp;
}
}
head = copyHead;
With the above statement, head is pointing to the same memory location where copyHead is pointing to. Loop is not entered on an empty list. But in the loop -
if (head == NULL)
{
head = temp;
}
This can never be the case on an linked list to be copied that has childs. So, you are never updating the head of the linked list and instead it is still pointing to the starting node of the linked list to be copied. Try -
Individual::Individual(BinaryNode * copyHead)
{
if (NULL == copyHead)
{
// Empty list
return;
}
head = new BinaryNode(copyHead->data, NULL);
curr = head;
copyHead = copyHead->next;
while (NULL != copyHead)
{
// Copy the child node
curr->next = new BinaryNode(copyHead->data, NULL);
// Iterate to the next child element to be copied from.
copyHead = copyHead->next;
// Iterate to the next child element to be copied to.
curr = curr->next;
}
}
Hope it helps !
I'm assuming Individual is a class in your code and basically it's holding the head possition of the list. I mean:
class Individual{
private:
void* head;// may be anything*
public:
void* getHead()
{
return head;
}
// all the methods
}
Now c++ provide a special type of constructor i.e. Copy Constructor. If you don't define a one compiler provide a default copy of copy constructor which do a shallow copy of a object. To define your custom copy constructor:
Firstly add a new method in BinaryNode:
void link(BinaryNode& b)
{
b.next=this;
}
Individual::Individual(const Individual& args)
{
void* copyHead = args.getHead()
if ( copyHead==nullptr)
{
// Empty list
return;
}
head = new BinaryNode(copyHead->data, NULL);
curr = head->next;
copyHead = copyHead->next;
temp = head;
while (NULL != copyHead)
{
// Copied the child node
curr = new BinaryNode(copyHead->data, NULL);
curr.link(temp);
temp = curr;
// Iterate to the next child element to be copied from.
copyHead = copyHead->next;
// Iterate to the next child element to be copied to.
curr = curr->next;
}
}
Now as you want to to a deep copy You have to implement a code that will copy the whole list starting from the head pointer.

Problem printing out linked-list

I am trying to create my own datatype that is like a vector or an array.
I am having troubles with my print function; When I go to print the list, it only prints the last item in the list.
// LinkedListClass.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
class Node
{
public:
int value;
Node* next;
Node::Node(int val)
{
value = val;
};
};
class List
{
public:
Node* firstNode;
Node* currentNode;
int size;
List::List()
{
firstNode = NULL;
currentNode = firstNode;
size = 0;
};
void push(Node* node)
{
if(firstNode == NULL)
{
firstNode = node;
firstNode->next = currentNode;
size++;
}
else
{
currentNode = node;
currentNode = currentNode->next;
size++;
}
};
void print()
{
if(firstNode != NULL)
{
Node* printNode = firstNode;
while(printNode->next != NULL)
{
std::cout << "List Item " << printNode->value << std::endl;
printNode = printNode->next;
}
}
};
};
int _tmain(int argc, _TCHAR* argv[])
{
List ll = List();
for(int i = 0; i < 10; ++i)
{
Node val = Node(i);
ll.push(&val);
}
std::cout << ll.firstNode->value << std::endl;
ll.print();
std::cout << "Size " << ll.size << std::endl;
std::cin.ignore();
return 0;
}
/* Output
9
Size 10
*/
I know this is nowhere near completed, but if you have any other pointers (lol), please feel free to suggest.
There are three important errors:
push() --- fixed
void push(Node* node)
{
if(firstNode == NULL)
{
firstNode = node;
currentNode = node;
// firstNode->next = currentNode; --> this does nothing useful!
size++;
}
else
{
currentNode->next = node;
currentNode = node;
//currentNode = node; -|
//currentNode = currentNode->next; -|----> why? what? Do explain.
size++;
}
}
I think by assigning firstNode->next = currentNode; you expected the next time currentNode was updated, it would update firstNode->next as well.
It doesn't work that way.
firstNode->next = currentNode; implies that the address stored in currentNode is now in firstNode->next. So next time you store something in currentNode = node; you're not storing it in firstNode->next. So you have a broken linked list --- which is why your output didn't go very far.
Also, this is really bad. By setting currentNode=node before setting the current node's next pointer to node, you've broken the list again. You should first point currentNode->next to node and then set the currentNode as node (node being the node which you're pushing onto your list).
Node val = Node(i);
The scope of val is only within that iteration of your loop. Once you loop around, it's off the stack and doesn't exist anymore. But you've copied the pointer of val to your list --- so now with the right push method, you're just adding a dangling pointer.
Node *val = new Node(i);
ll.push(val);
You need to put it on the heap so it stays on till you don't need it anymore.
... which leads us to your destructor!
Since you've allocated a node, you'll need to deallocate it. So do that in your destructor --- traverse your list and deallocate all those nodes.
The following lead to undefined behavior:
Node val = Node(i);
ll.push(&val); // take address of temporary
...
firstNode = node; // store address of temporary here
...
ll.print(); // temporary `val` was destroyed, but all nodes are point to it
You could change your code as follows:
Node* val = new Node(i);
ll.push( val );
And don't forget to delete all nodes later.
Your push() method is incorrect. The first time you push a node, it correctly assigns it to firstNode, but every subsequent push() just sets currentNode to the new node, and then sets currentNode to NULL -- you're not actually adding anything to your list.
I think it bears mentioning that pointers are not reference-by-name in C++. For instance, setting firstNode->next = currentNode doesn't make currentNode the next element in the list; it just makes firstNode->next point to the same address that currentNode does (in this case, NULL).
I'm not going to write the code for you, but here's how your push() function should work. The key is that you should be setting the 'next' field of an existing node to your new node, rather than currentNode to the new node:
In the case where firstNode is NULL,
set firstNode to the new node and
set firstNode->next to NULL (since
it has no next element). You can
also set currentNode = firstNode
here for convenience.
In the case where firstNode is not
NULL, we need to walk from firstNode
down the chain until we find a node
whose next field is NULL, then set
its next field to the new node.
Alternatively, we can use that
currentNode pointer to access the
last element in list and do the same
thing, being sure to set currentNode
to point to the new node when we're
done.
You basically have part 1 done, but part 2 still needs to be implemented. Feel free to ask for clarification/give criticism. :)
try it like Node* val=new Node(i)
previously u were storing the temporary variable. so no store the ndoe in dynamic memory so seprate memory can be given.
when u were creating the node it is create for temparary purpose
&temporary address were stored so when u traverse back the temporary memory had been released u will find there some garbage. value.