So I want to copy a whole linked list classes, I have trouble figuring it out how to do so,
class list{
public:
list(const list &t);
private:
struct Node{
int x;
Node *next;
}*p;
I started with something like this:
list::list(const list &t){
Node* q;
q=new Node;
while (p!=NULL){
q->x= p->x;}
}
but I'm not sure if I am on the right track or what. I also have trouble how should I test such a copy constructor? For example I have list l1, then i insert couple integers into a list and then how I can copy it?
In your example it never will work if you initialized p or will work forever if p != NULL. You must allocate new nodes while traversing through t list:
p = NULL;
Node* copy = l.p;
Node* insert = p;
Node* inserted_el = NULL;
while (copy){
insert = new Node();
insert->x = copy->x;
insert->next = NULL;
if (inserted_el) {
inserted_el->next = insert; //copy memory pointer to next element
} else {
p = insert; //copy memory pointer to list head
}
copy = copy->next;
inserted_el = insert;
}
This is basic idea. Also don't forget to implement assign operator and destructor.
Usage:
list t1;
//insert nodes
list t2(t1);
The biggest trouble in your code is that you do not duplicate each node of the list while you need to do so.
Here is the code of the ctor:
list::list(const list &t)
{
p = NULL; // Init the head of the list this is vital important.
// Loop over the elements of the passed list if any.
Node *pt = t.p;
Node *last_local_element = NULL;
while (pt != NULL)
{
// Allocate a new node and set the fields there.
Node *q = new Node;
q->x= pt->x;
q->next = NULL;
// Add new node to the local list.
if (last_local_element != NULL) {
last_local_element->next = q;
} else {
p = q;
}
last_local_element = q;
// Shift the loop variable along the passed list.
pt = pt->next;
}
}
There are 2 most often cases when the copy ctor is called:
list my_list1;
list my_list2(my_listl); // Explicit call.
list my_list3 = my_listl; // Assignment in the definition statement.
With your design of class, you need to be careful with memory management. This is the code:
list::list(const list& t) {
Node* n = t.p;
Node* m = p;
while (n) {
if (!m) {
m = new Node(); // Allocate memory.
if (!p) p = m;
}
m->x = n->x;
m = m->next;
n = n->next;
}
if (m) { // Original list is longer, delete the rest of the list.
Node * tmp = m;
m = m->next;
delete tmp;
}
}
Related
I am trying to learn constructors in c++. I am working on a list that I defined. I managed to get the copy constructor working, but I have problems with the array transfer constructor. Any help will be appreciated. Thanks!
The array transfer constructor supposedly should take in an array and a size(int) and output a list with that size.
ex: input: data = {1,3,5,6};int = 5;output = {1,3,5,6,0}
edit: change n to i
#include <iostream>
using namespace std;
class list_element{
public:
list_element(int n = 0,list_element* ptr = nullptr):
d(n),next(ptr){}
int d;
list_element* next;
};
class List{
public :
List():head(nullptr),cursor(nullptr){}
List(const int* arr, int n); // copy array transfer data
List(const List& lst); //copy constructor
void prepend(int n);
int get_element()
{
return cursor->d;
}
void advance() { cursor = cursor->next; }
void print();
~List(); //delete
private:
list_element* head;
list_element* cursor;
};
//transfer array
List::List(const int* arr, int n) {
List temp;
int i = 0;
while (i < n)
{
head = new list_element(arr[i], head);
++i;
}
}
//delete
List::~List(){
for (cursor = head; cursor != 0;)
{
cursor = head->next;
delete head;
head = cursor;
}
}
//deep copy constructor
List::List(const List& lst) {
if (lst.head == nullptr)
{
head = nullptr; cursor = nullptr;
}
else
{
cursor = lst.head;
list_element* h = new list_element();
list_element* previous;
head = h;
h->d = lst.head->d;
previous = h;
for (cursor = lst.head; cursor != 0;)
{
h = new list_element();
h->d = cursor->d;
previous->next = h;
cursor = cursor->next;
previous = h;
}
cursor = head;
}
}
void List::prepend(int n)
{
if (head == nullptr)
cursor = head = new list_element(n, head);
else
head = new list_element(n, head);
}
void List::print()
{
list_element* h = head;
while (h != 0)
{
cout << h->d << ',';
h = h->next;
}
cout << "###" << endl;
}
int main()
{
List a, b;
//change size
int data[10] = { 1,3,5,7};
List d(data, 10);
d.print();
return 0;
}
Main Question
With regards to your 'from_array' constructor, you have a temporary List variable that you are not using and is also unnecessary.
Second you are assigning the head pointer each time meaning that by the end of the constructor call, head now points to the last element you constructed.
Third your list_element constructor is taking the old head pointer which points to the previous element meaning the list is tries to advance from the bottom element upwards through the list, causing the reversed read you mentioned in a comment.
You can fix this two ways. First, you could reverse the order you read the input array so it constructs your linked-list back to front.
List::List(const int* arr, int n)
{
int i = n - 1;
list_element* it = new list_element(arr[i], nullptr); ///< Last element
--i;
while (i > -1)
{
it = new list_element(arr[i], it);
--i;
}
head = it; ///< assign head to the last instance of it.
it = nullptr;
}
However, there is a better why that expresses the linking of the elements more intuitively. First you need to pre-construct the next element and give it some default values (I didn't bother implementing a default constructor for list_element but you might want to.) and assign it to next, then assign head to a new list_element passing in the pointer to next. increment i so that you can assign next's value to the second value in the array. Finally incremenet i again so we can loop through the rest of the array. Finally, in the while loop, copy next into a variable called prev. Assign next to a new list_element with the value from the array and a nullptr. Finally assign prev->next to the new next pointer and increment i.
list_element::list_element()
List::List(const int* arr, int n)
{
int i = 0;
list_element* next = new list_element(0, nullptr);
head = new list_element(arr[i], next);
++i;
next->d = arr[i];
++i;
while (i < n)
{
list_element* prev = next;
next = new list_element(arr[i], nullptr);
prev->next = next;
++i;
}
}
Side Notes
Because you stated you are tying to learn about C++ constructors (and I'm assuming data structures) I would suggest starting with a static array type similar to std::array as it's constructors a bit more trivial to implement or even just start with simple classes/struct that just hold simple data like a few ints or whatnot as you can get an idea for the semantics around the various constructors in C++.
Also, the C++ standard library has two linked list types (std::list and std::foward_list)
Finally, you might be better off using a std::initializer_list instead of a raw array as this give you iterators to the data you want to copy which is a bit nicer to use.
Best of luck in your learning journey.
Try to avoid using variable names like 'n', which could be very confusing.
In your copy constructor for transferring the array, you should not access the array using 'n', which is the desired size of the transferred array, nor increment it.
Additionally, sizeof(arr) doesn't work as you would expect. You are querying the size of the pointer.
I have been learning C++ for the last couple of months, and after going through an online course I have started doing some challenges with using raw pointers. I have successfully created class that can initiate and iterate through linked list, with lots of struggle I managed to create method that deletes its element(s), but I am struggling to write a method that would remove all the duplicates.
I have a method such as
void linked_list::remove_dups(){
Node *p = first;
Node *g = first;
while(p!=NULL){
int x = 0;
g = new Node;
g = first;
Node *remove = first;
while (g!=NULL){
if(p->data == g->data){
x++;
}
if(x>1){
remove = new Node;
remove = p;
p = p->next;
remove->next = p->next;
delete remove;
x--;
}
g = g->next;
}
p = p->next;
}
}
where Node is standard
struct Node{
int data;
struct Node *next;
};
first is private member
Node *first;
Can anyone point out what is wrong in this function and explain please? I initiated list with pointers that is
int a[] = {3,5,5,7,65,5,65,65,4,4,15};
and it removed most of the duplicates, but not all. If I add more though, I have EXC_BAD_ACCESS error.
Really appreciate any help!
The main issue is:
p = p->next;
remove->next = p->next;
delete remove;
You try to set remove->next to point to the new next element. But remove is going to be deleted. So the element before remove will still point to something deleted. So, after the delete, you'll have a broken list that contains pointer to deleted elements. Next loop iteration, it will break.
Below is the code I edited as I went. Please note that
remove = new Node;
remove = p;
is not only useless, it leaks memory. You can directly do remove = p;
Just draft code as I went. Not fixed. Proper would be keeping a prev pointer or something.
void linked_list::remove_dups()
{
Node *p = first;
Node *g = first;
while(p!=NULL)
{
// int x = 0; remove this
// g = new Node; remove this
g = first;
Node *remove = first;
while (g != NULL)
{
if(p->data == g->data) // test directly, no x
{
// remove = new Node; remove this
remove = p;
p = p->next;
// remove->next = p->next; You are going to delete this, no point modifying it
delete remove;
}
g = g->next;
}
p = p->next;
}
}
Something I wrote for my custom doubly-linked list that will remove duplicates. Not necessarily the most efficient but it gets the job done.
void unique()
{
std::map<int, int> hash;
Node<T>* temp = this->head;
while(temp)
{
hash[temp->data]++;
if(hash[temp->data] > 1)
{
Node<T>* next = temp->next;
Node<T>* prev = temp->prev;
delete temp;
temp = nullptr;
if(!next)
{
temp = prev;
temp->next = nullptr;
hash[temp->data]--;
break;
}
next->prev = prev;
prev->next = next;
temp = next;
hash[temp->data]--;
}
else
{
temp = temp->next;
}
}
}
I have been working on a linked list problem, using two pointers to the head and the tail of the list respectively. The issue I have is the following:
When I want to remove from the front or the back of the list, I get memory leaks when the corresponding class methods are implemented using temporary dynamically allocated node pointer and I can't find out what exactly is the problem that is causing the leak.
#include <iostream>
class Node {
public:
Node():data(0),ptrNode(nullptr){};
~Node() {};
// declaring the getters and setters
float getData() const {return data;};
void setData(float d) {data = d;};
Node* getNodePtr() const {return ptrNode;};
void setNodePtr(Node* n){ptrNode = n;};
private:
float data;
Node* ptrNode;
};
class List {
private:
Node * ptrHead;
Node * ptrTail;
public:
List():ptrHead(nullptr),ptrTail(nullptr) {};
~List() {};
void insertAtFront(float x) {
Node * temp = new Node();
temp->setData(x);
if (ptrHead == nullptr) {
ptrHead = temp;
ptrTail = temp;
} else {
temp->setNodePtr(ptrHead);
ptrHead = temp;
}
};
void insertAtBack(float x) {
Node * temp = new Node();
temp->setData(x);
if (ptrHead == nullptr) {
ptrHead = temp;
ptrTail = temp;
} else {
ptrTail->setNodePtr(temp);
ptrTail = temp;
}
};
void removeFromFront() {
if (ptrHead==nullptr) { // if list is empty
std::cout << "List is already empty" << std::endl;
return;
}
if (ptrHead==ptrTail) { // if list has one element
delete ptrHead;
ptrHead = nullptr;
ptrTail = nullptr;
return;
}
// assign current Head to temp, assign next node to head
// delete temp
Node * temp = new Node();
temp = ptrHead;
ptrHead = ptrHead->getNodePtr();
delete temp;
temp = nullptr;
};
void removeFromBack() {
if (ptrHead==nullptr) { // if list is empty
std::cout << "List is already empty" << std::endl;
return;
}
if (ptrHead==ptrTail) { // if list has one element
delete ptrHead;
ptrHead = nullptr;
ptrTail = nullptr;
return;
}
// create two temp Node pointers for this one
Node * sec2last = new Node();
Node * last = new Node();
sec2last = ptrHead;
last = ptrTail;
// locate second to last element and assign it to sec2last
while (sec2last->getNodePtr() != ptrTail) {
sec2last = sec2last->getNodePtr();
}
ptrTail = sec2last;
ptrTail->setNodePtr(nullptr);
delete last;
delete sec2last;
last = nullptr;
sec2last = nullptr;
};
};
I run the following in main():
// global function that dynamically allocates memory in its scope
// for a linked list
void generateList(int x) {
if (x<=0) {
cout << "Give a positive integer!" << endl;
return;
}
List * aList = new List();
for (int i = 0;i<x;i++) {
aList->insertAtFront(i+1);
}
aList->removeFromBack(); // this causes leaks
delete aList;
}
// MAIN
int main() {
for (int i = 0;i<80000000;i++)
generateList(1); // just repeatedly generate dynamic list
// with one element
return 0;
}
I should point out that if we don't use dynamically allocate memory for the temporary nodes within the removeFromFront() and removeFromBack() methods, then the program works fine. But, like I said, It kills me that I can't see why we have leaks with the code above.
Thanks!
Node * temp = new Node();
temp = ptrHead;
This is a memory leak right here. You allocate a new Node and store a pointer to it in temp. Then you overwrite that pointer and leak the node you just allocated.
// create two temp Node pointers for this one
Node * sec2last = new Node();
Node * last = new Node();
sec2last = ptrHead;
last = ptrTail;
And here you do it again. Why are you allocating new Nodes here?
I tried to implement C++ singly linked. I have created a method which creates a node and add a value and points to another node but i have to remember index.
How to improve the code and create nodes without remembering index? (I want to maintain order = first created node points to another etc.)
Class method:
void LinkedList::addValue ( int val )
{
if ( ! index )
{
n = new Node();
head = n;
n->value = val;
n->next = NULL;
}
else
{
n->next = new Node( );
n = n->next;
n->value = val;
}
++index;
}
I guess, you already have two member variables: head which is the root node and n which is the last node. You should initialize both of them with NULL (nullptr for c++11) in constructor. Then you can just check if n==NULL when you add a new value to the list.
LinkedList::LinkedList():head(NULL),n(NULL)
{}
void LinkedList::addValue ( int val )
{
if (n==NULL)
{
n = new Node();
head = n;
n->value = val;
n->next = NULL;
}
else
{
n->next = new Node( );
n = n->next;
n->value = val;
}
}
The index variable, however, can be useful if you want to find the list size in one fast read operation without iterating over all its nodes.
You can create another pointer called Tail to point to the last element. That way you can add value to the list without the index.
I call this method append
void append(int val){
void append(int val) {
Node* tmp = new Node(); // creating a temporary pointer to a new node
tmp -> value = val
last -> next = tmp; // connect the new node to the linked list
last = tmp; // set the last to the newly created node
listSize++; // increase the size of the list
}
You can also improve the code by making a Constructor for the node:
Class Node{
public:
int value;
Node * next;
Node(Node * nextEle = NULL) {
next = nextEle;
}
Node(int val,Node * nextEle = NULL) {
value = val;
next = nextEle;
}
}
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.