I'm new to C++ and having some difficultly with the use of shared ptrs. I've been able to construct a linked list. I want to traverse the linked list, and when the nodes (elements) have a valid to_add field, to add the element into the list, and continue.
class Element{
public:
char el = 'Z';
std::shared_ptr<Element> next;
char to_add = 'Z';
Element(char input){
el = input;
next = nullptr;
}
};
^ structure of node. I'm looking at the to_add field to see if there's an element to add in.
class Polymer{
public:
std::vector<Instruction> instruction;
std::shared_ptr<Element> extract_and_store_input(std::ifstream &file);
void prepare_insertion(std::shared_ptr<Element> head);
void insert_elements(std::shared_ptr<Element> head);
Polymer(std::ifstream &file){
std::shared_ptr<Element> head = extract_and_store_input(file);
prepare_insertion(head);
insert_elements(head);
}
};
^ calling code format
v problem is somewhere here
std::shared_ptr<Element> add_element(std::shared_ptr<Element> head_cpy){
auto tmp = head_cpy->next;
head_cpy->next = std::make_shared<Element>(head_cpy->to_add);
head_cpy->next = tmp;
return tmp;
}
void Polymer::insert_elements(std::shared_ptr<Element> head) {
auto trav = head;
auto tmp= head;
// for (int i = 0; i < num_steps; i ++)
while (trav->next != nullptr) {
std::cout<<trav->el<<std::endl;
if (trav->to_add != 'Z'){
std::cout<<"adding"<<trav->to_add<<std::endl;
trav=add_element(trav);
trav=trav->next; // progress forward twice
}
trav=trav->next;
}
// while (tmp->el != 'Z'){
// std::cout<<tmp->el<<std::endl;
// tmp=tmp->next;
// }
}
I think that the issue may be that the pointer doing the traversal (trav) is updating the list itself, rather than progressing through the list? I would really appreciate any insights!
If anyone recognises the q, it's from AoC day 14 :) I'm sure there are better ways to do it, but I'd like to understand the problem here first!
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 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;
}
So I'm trying to create a linked list in C++ out of a list container class. The list class contains the member head (a node), and insert function, and the node class contains the data I need to add (first name, last name, age). However, I don't think I'm actually creating a list, rather just writing over p during the input loop
Here's the while-loop in the main program that reads data from a file
while(!infile.eof())
{ infile >> first >> last >> age;
// Process if okay
if(infile.good())
a.insert(first,last,age);
};
Here's the actual insert function definition
void list::insert(string first, string last, int age)
{
node *p;
p = new node;
p->first = first;
p->last = last;
p->age = age;
if (head == NULL)
{
head = p;
head->put(cout);
} else
{
if (head->next != NULL)
{
head->put(cout);
insert((p->next)->first, (p->next)->last, (p->next)->age);
} else
{
p->next = p;
p->put(cout);
}
}
}
I can't change the Node header or main program, so I need to use these parameters in the function calls. Any ideas?
Inserting a node in a linked list is simple when the list is empty.
head = p;
and you are done.
Inserting a node at the end of linked list is a little bit involved when the list is not empty. Pictorially, let's say you have:
and you want to add a new node at the end. You will need to make the link between the last node of the existing linked list and the new node so that you will end up with:
In order to be able to do that you have to walk the linked list to get to the last node. Then, you can use:
lastNode->next = p;
and you are done.
Ignoring the calls to create the output, here's what your function would look like.
void list::insert(string first, string last, int age)
{
node *p = new node;
p->first = first;
p->last = last;
p->age = age;
if (head == nullptr)
{
// The simple case
head = p;
}
else
{
// Gather the last node in the linked list.
node* lastNode = head;
while ( lastNode->next != nullptr )
{
lastNode = lastNode->next;
}
lastNode->next = p;
}
}
Using recursion for inserting a node looks very different. Here's an untested suggestion:
void list::insert(node*& ptr, node* p)
{
if ( ptr == nullptr )
{
ptr = p;
}
else
{
insert(ptr->next, p);
}
}
void list::insert(string first, string last, int age)
{
node *p = new node;
p->first = first;
p->last = last;
p->age = age;
insert(head, p);
}
I don't recommend using the recursive method. It not only takes away the readability of the algorithm but is also more expensive at run time. If you have a linked list with a large number of items in it, it might even lead to stack overflow.
How can I delete all objects from a linked list,implemented in struct? After my delete-funktion (loeschen) are shown comic numbers and I donĀ“t know any more where my pointers are.
struct domino {
int data1;
int data2;
domino *next;
};
int readSteine (){ //Reading from numbers from file
FILE *file;
if((file=fopen("datei.dat", "r") )==NULL) { /
std::cout<<"File cant be open"<<std::endl;
return 0;
}else {
int beginning;
int temp;
fscanf(file, "%d", &beginning);
for(int i=0; i<beginning; i++) {
domino *domino1= new domino;
//if(i>0) temp2->next=domino1;
fscanf(file, "%i", &temp);
domino1->data1=temp;
fscanf(file, "%i", &temp);
domino1->data2=temp;
printf("[%d:%d]", domino1->data1, domino1->data2);
domino1->next=0;
}
}return 0;
}
Function for deleting of the list:
void deletelist (domino *head) {
domino* tmp;
while(head != 0) {
tmp=head->next;
delete head;
head=tmp;
}
}
int main() {
domino *pHead=NULL;
domino s;
readSteine();
deletelist(pHead);
std::cout<<s.data1<<"...."<<s.data2<<std::endl;
return 0;
}
Below is an example of a clear method. The idea is to traverse over the list and delete the nodes one by one.
Node *pDel = _pHead;
/* Traverse the list and delete the node one by one from the head */
while (pDel != NULL) {
/* take out the head node */
_pHead = _pHead->_pNext;
delete pDel;
/* update the head node */
pDel = _pHead;
}
/* Reset the head and tail node */
_pTail = _pHead = NULL;
Define the function either like
void loeschen (dominostein **kopf) {
dominostein* tmp;
while(*kopf != 0) { //bzw. NULL
tmp = ( *kopf )->next;
delete *kopf;
*kopf = tmp;
}
}
and call it like
loeschen(&pKopf);
or the following way
void loeschen (dominostein * &kopf) {
dominostein* tmp;
while( kopf != 0) { //bzw. NULL
tmp = ( kopf )->next;
delete kopf;
kopf = tmp;
}
}
and call it like
loeschen(pKopf);
Your original function also is correct but after deleteing all nodes
loeschen(pKopf);
you should set pKopf to NULL
pKopf = NULL;
Or you simply should not use this pointer to access the deleted nodes.:)
Take into account that this statement
std::cout<<s.data1<<"...."<<s.data2<<std::endl;
does not make sense because data members data1 and data2 of object s were not inistilaized when the object was created
domino s;
And this object has nothing common with the list.
And function readSteine does not use node pHead defined in main
domino *pHead=NULL;
//...
readSteine();
You shpuld rewrite the function. It is wrong.
Your loeschen will never works becouse you pass to it just a NULL.
You first need to correctly construct the list. First, you need to pass your Kopf (a kind of reference to it) to the readSteine function, and assign it to each new next, and after that assign to it the new domino, and so on to form the linked list..
I strongly recommend you to read Programming: Principles and Practice Using C++ .
You mix C functions and style of programming with C++. You dont even need to know these C functions which are hard to learn right in order to begin to learn C++.
This is my linked list constructor and insertion function:
template <class T>
List<T>::List() {
_first = nullptr;
}
template <class T>
bool List<T>::insert(const T& item, const int& position) {
Node* ptr = new Node();
// sets node data members
ptr->data = item;
ptr->next = nullptr;
if (position == 1) {
ptr->next = _first;
_first = ptr;
return true;
}
// iterates through list until reached position passed into function
Node* predptr = _first;
for (int i = 0; i < position - 2; i++) {
predptr = predptr->next;
}
// repoints pointer of previous node to it and its pointer to what previous pointer was pointing too
ptr->next = predptr->next;
predptr->next = ptr;
return true;
}
I tried this before but screwed my whole program up and got lost so I just reverted back to what I had working as a starting point.
In my notes it says to traverse the list do this:
(_first != 0) {
ptr = first;
do
{ // process ptr->data
ptr = ptr->next; }
while (ptr != _first);
}
but when I tried changing the code to add this, it just screwed everything up. If I could get pointed in the right direction, i'd appreciate it, thanks!
I do not have your code so it is not tested. But I think this would work. First to make clear, I use zero-based-indices.
For the first part:
Tipp You can pass position as copy because a reference to int has the same size as the int itself. For larger object though it would make sense.
I added a member size function which counts the number of elements. The two asserts are assuring that you do not pass a position which is greater than the number of elements in the list. The second one guarantees that the special case is correctly as it tests if _first == nullptr then position must be zero because no other element has been added before.
// create the node
Node* ptr = new Node();
// just to make sure position is not out of bounds
assert(position <= size);
size++;
// sets node data members
ptr->data = item;
// special case, list is empty
if (_first == nullptr)
{
// position must be 0 if you insert for the first time
assert(position == 0);
ptr->next = ptr;
_first = ptr;
return true;
}
Ok for the second part:
When you have already inserted one element you can loop over all elements. I used a special node which is pointing to _first and started the looping at position -1 this should ensure you get the previous element to reassign the pointers accordingly.
// create special-node pointing to first node
Node* predptr = new Node();
predptr->next = _first;
for (int i = -1; i < position; ++i)
{
if (i == position - 1)
{
ptr->next = predptr->next;
predptr->next = ptr;
}
predptr->next = predptr;
}
return true;
I hope that works for you.