how do i append a struct Person in c++ - c++

I have a struct Person
struct Person {
string name;
int age;
Person* next;
};
and i need to make a function that appends structs after each other like a linked list. I started off by creating a pointer head which is a nullpointer in the beginning.
using namespace std;
Person* head = nullptr;
void append(Person*& list, const string& name, int age) {
auto p = new Person{name, age, nullptr}; // typ List_Node*
if (list == nullptr) {
list = p;
return;
}
auto last = list;
while (last != nullptr) {
last = last->next;
}
last = p;
}
int main() {
append(head, "First_Person", 21);
append(head, "Second_Person", 22);
// testing the result
head = head->next;
cout << "head->name outside: " << head->name << endl;
return 0;
}
Now the problem i have now is that after the first append the append() function doesn't seem to link the first person to the second person because the program crashes
head = head->next;
Question is: how do i get append() to append more than 1 Person to this linked list?

auto last = list;
while (last != nullptr){
last = last->next;
}
last = p;
When you get out of this loop, last is pointing at nullptr again instead of the last element of the list. You need to check last->next != nullptr instead.
Also, last = p does not do anything useful; it should be last->next = p in order to actually append p to the list.

Your append() is not managing the pointers correctly. Try this instead:
void append(Person*& list, const string& name, int age)
{
auto p = new Person{name, age, nullptr}; // typ List_Node*
if (list == nullptr)
{
list = p;
return;
}
auto last = list;
while (last->next != nullptr)
last = last->next;
last->next = p;
}
If you get rid of the list parameter, you can avoid having to iterate through the entire list on every append:
Person* head = nullptr;
Person* last = nullptr;
void append(const string& name, int age)
{
auto p = new Person{name, age, nullptr};
if (head == nullptr)
head = p;
if (last != nullptr)
last->next = p;
last = p;
}
int main()
{
append("First_Person", 21);
append("Second_Person", 22);
//testing the result
for(Person *p = head; p != nullptr; p = p->next)
std::cout << "name: " << p->name << std::endl;
// free the list
Person *p = head;
while (p != nullptr)
{
Person *next = p->next;
delete p;
p = next;
}
return 0;
}
That being said, you should use std::list or std::forward_list to hold your Person items and let it handle all of these details for you, eg:
#include <list> // or: #include <forward_list>
struct Person
{
string name;
int age;
};
std::list<Person> mylist; // or: std::forward_list<Person> mylist;
void append(const string& name, int age)
{
mylist.push_back(Person{name, age});
}
int main()
{
append("First_Person", 21);
append("Second_Person", 22);
//testing the result
for (auto &person: mylist)
std::cout << "name: " << person.name << std::endl;
return 0;
}

If you are writing this in a non-academic context, then don't reinvent the wheel, and use a standard container like vector or list.
However, assuming the point of the exercise is to learn data structures, then using a builtin container doesn't teach you much.
The problem I see is you are doing nothing but changing the "last" pointer, which doesn't actually append anything to the list. You need to first find the last node (by using the 'last' pointer to move to each node->next until you reach a node which has a null ->next pointer. Then assign that node's ->next to your new node.
Your existing code...
// Already handled empty list case before we get here...
auto last = list; // last pointer set to address list (head)
while (last != nullptr){ // while not at end of list, last points to next node
last = last->next;
}
// after loop, last will always be null, not very useful
last = p; // last points to p (the new node) which doesn't _do_ anything
Consider doing something like:
// Already handled empty list case before we get here...
auto last = list; // last pointer set to address list (head)
while (last && last->next != nullptr){ // while node->next not null, move to next node
last = last->next;
}
// at end of list, last will always point to last node
last->next = p; // link last->next to the new 'p' Person
Keep in mind if you do a lot of list append operations, it is cheaper for your list data structure to keep a head and a tail pointer, and append simply becomes
if(tail)
tail->next = p;
tail = p;
Besides that, it is cheaper to insert items at the head of a list.
p->next = head;
head = p;

Related

Doubly linked list not taking input after calling a delete function

Greetings stack overflow. My program is supposed to take a user inputted line of characters and append them to a list. The program is also supposed to delete the most recent character appended if a hashtag is read in the input.
My program mostly works, but I run into errors when I try to break it by adding too many hashtags. Upon doing this the list stops accepting appends will display nothing if one too many hashtags are used.
I hope I only included what I thought was useful code, sorry if the main function was not necessary.
#include <iostream>
using namespace std;
class doubleList
{
public:
doubleList() { first = NULL; } // constructor
void append(char); // adds entry to the end of the list
void remove_last(); // removes the last item from a list
friend ostream& operator<<(ostream& out, const doubleList& l); // outputs in forward order
private:
struct Node
{
char data;
Node *next;
Node *prev;
};
Node *first;
Node *last;
};
void doubleList::append(char entry)
{
Node* temp = new Node();
temp -> data = entry;
temp -> next = NULL;
if (first == NULL)
{
first = temp;
last = temp;
}
else
{
last -> next = temp;
temp -> prev = last;
last = temp;
}
}
void doubleList::remove_last()
{
if (first -> next == NULL)
{
delete first;
}
else if (first != NULL)
{
last = last -> prev;
delete last -> next;
last -> next = NULL;
}
}
ostream& operator<<(ostream& out, const doubleList& l)
{
doubleList::Node* q;
q = l.first;
while (q != NULL)
{
out << q -> data;
q = q -> next;
}
return out;
}
int main()
{
doubleList list;
char ch[100];
cout << "Enter a line of characters; # will delete the most recent character." << endl;
for (int i = 0; i < 100; i++)
{
cin.get(ch[i]);
list.append(ch[i]);
if (ch[i] == '#')
{
list.remove_last();
list.remove_last(); // called twice becaue it removes the hashtag from the list
} // and i was too lazy to make it so it doesnt do that so this
// is simply an easier fix
if (ch[i] == '\n') // exits the loop when enter is clicked
break;
}
cout << list;
return 0;
}
A successful run of my program would look like:
Enter a line of characters; # will delete the most recent character.
abcd##fg
abfg
My program when too many hashtags are added:
Enter a line of characters; # will delete the most recent character.
ab#####efgh
Nothing is shown after user input is taken. Thanks in advance.
You should also set the pointer last to nullptr in the constructor
doubleList() { first = nullptr; last = nullptr; }
The function append is incorrect because it does not set the data member prev of the first node appended to the list. It should be written like
void doubleList::append(char entry)
{
Node* temp = new Node();
temp -> data = entry;
temp -> next = nullptr;
temp -> prev = last;
if (first == NULL)
{
first = temp;
}
else
{
last -> next = temp;
}
last = temp;
}
The function removeList can invoke undefined behavior because in the very beginning of the function it does not check whether the pointer first is equal to nullptr. And after deleting the node pointed to by the pointer first it does not set the pointers first and last to nullptr. The function can be defined the following way.
void doubleList::remove_last()
{
if ( last )
{
Node *tmp = last;
last = last->prev;
if ( last != nullptr )
{
last->next = nullptr;
}
else
{
first = nullptr;
}
delete temp;
}
}
You rely on first being NULL when deallocated in remove_last() but do not set it after the free. As your code is incomplete, I cannot tell what last is, and why you your logic doesn't rely on it in remove_last() instead of first.
Something along these lines (untested);
void doubleList::remove_last() {
if(!last) return;
if(last == first) {
free last;
first = nullptr;
last = nullptr;
return;
}
last = last->prev;
free last->next;
last->next = nullptr;
}

DoubleLinkList Append Value

I have a question: How the first Node in my double link list get the value? Could someone explain it? Because what i see in append_value function is to add the end Node value not the first Node value. But when i try to run the code, the first Node somehow has some value. thanks.
Please see the code:
struct Node
{
int value;
Node *next;
Node *prev;
};
class DoubleLinkList
{
private:
Node *first;
Node *end;
public:
DoubleLinkList();
void show_list();
void append_value(int);
};
DoubleLinkList::DoubleLinkList()
{
first = NULL;
end = NULL;
}
void DoubleLinkList::show_list()
{
Node *node;
node = first;
while(node)
{
cout << node->value << " ";
node = node->next;
}
cout << endl;
}
void DoubleLinkList::append_value(int value)
{
Node *ptr = end;
end = new Node;
if (first == NULL)
first = end;
else
ptr->next = end;
if(end)
{
end->next = NULL;
end->prev = ptr;
end->value = value;
}
}
Normally, in a doubly linked list, when the head pointer is nul, this indicates that the list is empty:
void DoubleLinkList::append_value(int value)
{
//...
if (first == nullptr)
{
// List is empty
}
//...
}
At this time, the head pointer and the end pointer are set to the new node, thus placing a node into the list:
//...
if (first == nullptr)
{
first = end;
}
//...
You could simplify it by filling in the Node values on construction:
void DoubleLinkList::append_value(int value) {
end = new Node{value, nullptr, end};
// Check if there was a node there since before and, if so, make it point
// at the new `end`.
if(end->prev) {
end->prev->next = end;
} else {
first = end; // this is the first node added, make it point at `end`
}
}
Note: You also need a destructor to delete the objects and implement/delete the copy constructor, copy assignment operator, move constructor and move assignment operator.
See The rule of five

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

getting user input to form linked list

I'm trying to create a linked list from user input but it's not printing anything when i try to print it. Not even the head. Also note, it is intentionally backwards.
Here is my function for getting user input, it returns the list. I know it is wrong but i've spent hours on it and can't get it to work...
#include <iostream>
#include <limits>
#include <ios>
struct Node {
int value;
Node *next;
}
Node* getInput() {
Node* head = nullptr;
Node* tmp;
while (true) {
int x;
if (!(cin >> x)) {
break;
} else if ( head == nullptr) {
head = new Node{x, nullptr);
} else {
tmp = new Node{x , nullptr};
tmp->next = head;
head = head->next;
}
}
return tmp;
}
int main() {
cout << getInput()->value;
}
A couple of good solutions up, but because the request was for a backward list, this can be really, really simple.
Node* getInput()
{
Node* head = nullptr;
int x;
while (std::cin >> x) // keep going until we can't get a good x.
{
head = new Node{x, head}; // point node at current head, assign new head
// head always points at the beginning of list because items are
// always inserted at the start of the list.
}
return head;
}
So to prove this list prints backward, here's a simple tester
int main()
{
Node* cur = getInput();
while (cur)
{
std::cout << cur->value << '\n';
cur = cur->next;
}
}
The return value of get input() is not the actual head/start of the list. Head will always point to null the moment you insert any node. Head value can be stored in a temporary pointer during first insert and return temporary pointer instead of head.
If you are trying to print the linked list in reverse order, here's a working version:
#include <iostream>
#include <limits>
#include <ios>
using namespace std;
struct Node {
int value;
Node *next;
Node(int val, Node *nextPtr) {
value = val;
next = nextPtr;
}
};
Node *getInput() {
Node *head = nullptr;
Node *tmp;
while (true) {
int x;
if (!(cin >> x)) {
break;
} else if (head == nullptr) {
head = new Node(x, nullptr);
} else {
tmp = new Node(x, nullptr);
tmp->next = head;
head = tmp;
}
}
return head;
}
int main() {
Node *head = getInput();
Node *tmp;
while (head != nullptr) {
cout << head->value << ", ";
tmp = head;
head = head->next;
delete tmp;
}
cout << endl;
return 0;
}
head = head->next; is the problem. You're allocating a Node correctly, but you immediately leak that Node and head is pointing to nullptr.
The simplest solution is to keep head pointed at the most recent Node. You'll need a special case for the first insertion since head will be uninitialized (fix that by the way), but that way you're always pointing at the most recent Node.
If you have trouble, draw your Nodes out on paper with arrows. Watch how the arrows change at each insertion, and you'll see what's happening.

Linked list insert with recursion trouble

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.