Linked Lists Program Crashes After Receiving Values from User - c++

My program is meant to run several functions, insertnode takes values from the user and creates a list of them using nodes and sorts them in order from least to greatest, printlist prints the values separated by spaces, mergelist merges the two lists in order, and reverselist reverses the list. The command prompt accepts values but once the stopping condition (0) is entered for the second list it crashes. Visual Studio shows no errors. I figure something is either wrong with the functions or the pointers. Someone spoke to me of a memory leak but Im unsure as to how to fix that.
#include <iostream>
#include <stack>
using namespace std;
class node {
private:
double num;
node *link;
public:
node() { }
node(double m, node *n) { num = m; link = n; }
node* getlink() { return link; }
double getdata() { return num; }
void setdata(double m) { num = m; }
void setlink(node* n) { link = n; }
};
typedef node* nodeptr;
void insertnode(nodeptr& head, double m);
void printlist(nodeptr head);
nodeptr mergelists(nodeptr& head1, nodeptr& head2);
void reverselist(nodeptr& head);
int main()
{
double input;a
nodeptr head1 = NULL; // Pointer to the head of List #1
nodeptr head2 = NULL; // Pointer to the head of List #2
nodeptr temp;
// Part 1 - Create two sorted lists
cout << "-------------------------------------" << endl;
cout << "CREATE LIST #1: " << endl;
cout << "-------------------------------------" << endl;
do {
cout << "Enter value (0 to quit): ";
cin >> input;
insertnode(head1, input);
} while (input != 0);
cout << "-------------------------------------" << endl;
cout << "CREATE LIST #2: " << endl;
cout << "-------------------------------------" << endl;
do {
cout << "Enter value (0 to quit): ";
cin >> input;
insertnode(head2, input);
} while (input != 0);
// Part 1 - Print the lists to make sure that they are correct.
printlist(head1);
printlist(head2);
// Part 2 - Merge the two lists and display the new merged list
temp = mergelists(head1, head2);
printlist(temp);
// Part 3 - Reverse the merged list and then display it
reverselist(temp);
printlist(temp);
return 0;
}
void insertnode(nodeptr& head, double m){
nodeptr p = head;
nodeptr k = p;
if (!p){
nodeptr n = new node(m, NULL);
}
else {
while (m >= p->getdata()){
k = p;
p = p->getlink();
}
nodeptr n = new node;
n->setdata(m);
k->setlink(n);
if (p){
n->setlink(p);
}
}
}
void printlist(nodeptr head){
nodeptr p = head;
while (p){
double m = p->getdata();
cout << m << " ";
p = p->getlink();
}
cout << endl;
}
nodeptr mergelists(nodeptr &head1, nodeptr &head2){
nodeptr result = 0, last = 0;;
if (head1->getdata() <= head2->getdata()){
result = head1;
head1 = head1->getlink();
}
else {
result = head2;
head2 = head2->getlink();
}
last = result;
while (head1 && head2){
if (head1->getdata() <= head2->getdata()){
last->setlink(head1);
last = head1;
head1 = head1->getlink();
}
else{
last->setlink(head2);
last = head2;
head2 = head2->getlink();
}
}
if (head1)
last->setlink(head1);
else if (head2)
last->setlink(head2);
last = 0;
head1 = 0;
head2 = 0;
return result;
}
void reverselist(nodeptr& head){
stack<double> holder;
nodeptr p = head;
while (p){
holder.push(p->getdata());
p = p->getlink();
}
p = head;
while (p){
p->setdata(holder.top());
holder.pop();
p = p->getlink();
}
}

There are a few issues with this method:
void insertnode(nodeptr& head, double m){
nodeptr p = head;
nodeptr k = p;
if (!p)
{
head = new node(m, NULL); // Update head
}
else
{
while (p && m >= p->getdata()) // Check for p!=NULL
{
k = p;
p = p->getlink();
}
nodeptr n = new node;
n->setdata(m);
k->setlink(n);
if (p)
{
n->setlink(p);
}
}
}
The simplified version:
void insertnode(nodeptr& head, double m)
{
nodeptr p = head;
nodeptr k = nullptr;
while (p && m >= p->getdata())
{
k = p;
p = p->getlink();
}
if (!k)
{
head = new node(m, p);
}
else
{
k->setlink(new node(m, p));
}
}

There are two issue is that you are not updating the head node when you insert into the linked list. The previous answer by #uncletall outlined this.
The second issue is very simple -- you failed to initialize the link to NULL when you default construct a node. Make the following change in your node class:
class node {
private:
double num;
node *link;
public:
node() : link(0) { } // here is the change here
//...
//... the rest of your code
};
When you default construct a node the link is now initialized. Without this change, your link node was a garbage value, thus you were not traversing the links properly when you were inserting more items in the list.
The change is similar to (but not exactly) the same as this:
class node {
private:
double num;
node *link;
public:
node() { link = 0; } // here is the change here
//...
//... the rest of your code
};
The difference is that link is assigned in the body of the constructor here, while the first example initializes the link to 0 before the constructor has started. Both wind up doing the same thing.

Related

Only Printing the First Value of Linked List

I have no idea why display function is not displaying anything other than the first node's data. I've tried switching the While(p!=NULL) to while(p->next!= NULL but when I do that instead of only the first node's data displaying no data is being displayed.
#include <iostream>
using namespace std;
class Node {
public:
int no;
Node* next;
};
Node* createNode(int no1) {
Node* n = new Node();
n->no = no1;
n->next = NULL;
return n;
}
void addValue(int x, Node** head) {
//insert first node into linked list
Node* n = createNode(x),*p = *head;
if (*head == NULL) {
*head = n;
}
//insert second node onwards into linked list
else {
while (p->next!= NULL) {
p->next = n;
p = p->next;
}
}
}
void display(Node *head) {
Node* temp = head;
// temp is equal to head
while (temp->next!=NULL) {
cout << temp->no;
temp = temp->next;
}
}
int main() {
int num; char choice;
Node* head = NULL;
do {
cout << "Enter a number : ";
cin >> num;
addValue(num,&head);
cout << "Enter [Y] to add another number : ";
cin >> choice;
} while (choice == 'Y');
cout << "List of existing record : ";
display(head);
return 0;
}
I've tried changing the contents fo the else while loop in the addRecord function to p = p->next; p->next = n; in that order to no avail.
In the while loop, it should be
while (p->next!= NULL) {
p = p->next;
}
p->next = n;
Traverse until the end of linked list is reached and then, add the new entry.

SIGSEV in Singly Linked List

I am writing a code to rotate a singly linked list counter-clockwise by K number of nodes. I wrote the following code. For example for the input linked list, 1,2,3,4,5,6,7,8 the rotate function Node *rotate(Node *head, int k){} returns the head pointer to the linked list 5,6,7,8,1,2,3,4. I wrote the following code. In this code, If I call print(head) inside the rotate function then it gives correct output but once it returns the head pointer to main then it either throws SIGSEV or is producing 1,2,3,4.
#include <iostream>
using namespace std;
class List {
public:
int data;
List *next;
explicit List(int element) : data(element), next(nullptr){}
};
List *insert() {
int n, i, value;
List *temp = nullptr, *head = nullptr;
cin >> n;
for(i = 0; i < n; ++i) {
cin >> value;
if(i == 0) {
head = new List(value);
temp = head;
continue;
} else {
temp ->next = new List(value);
temp = temp->next;
}
}
return head;
}
void print(List *start) {
while(start != nullptr) {
cout << start ->data << " ";
start = start->next;
}
}
List* rotate(List* head, int k) {
List *traverse = head, *temp = head;
List *kth, *end;
int i = 0;
while(i < k - 1) {
traverse = traverse ->next;
++i;
}
kth = traverse;
while(traverse->next != nullptr) {
traverse = traverse->next;
}
end = traverse;
head = kth->next;
kth->next = nullptr;
end->next = temp;
print(head);
return head;
}
int main() {
int k;
List *head = insert();
cin >> k;
print(head);
cout << endl;
rotate(head, k);
print(head);
return 0;
}
PS: I am only allowed to change the rotate function.

How to remove a certain node from a linked list by the data its holding?

We are suppose to enter a string, and then find where the string is in the linked list and remove that node
when i insert to the front of the list, so i enter data values a, b, c , d, when i print it it comes up as d,c,b,a. Now i insert to the rear of it, entering f and g, and the list now looks, d,c,b,a,f,g. I want to remove f but it just use the remove function it does not and still output the same list
using namespace std;
struct node {
string data;
node* next;
};
node* addFront(node* s);
node* addRear(node* s);
void remove(node* head, string abc);
void print(node* head);
int main() {
node* head = NULL;
cout << "Enter 5 data strings\n";
cout << "This will be inserted from the back\n";
for (int i = 0; i < 5; i++) {
head = addFront(head);
}
print(head);
cout << "Enter 3 strings and this will be inserted from the back of the orignal string\n";
for (int i = 0; i < 3; i++) {
head = addRear(head);
}
print(head);
cout << "Removing the head node\n";
string n;
cout << "Enter a string to remove\n";
cin >> n;
remove(head, n);
print(head);
}
node* addFront(node* s)
{
node* person = new node;
cin >> person->data;
person->next = s;
s = person;
return s;
}
node *addRear(node*s ) {
node* person = new node;
cin >> person->data;
person->next = NULL;
if (s == NULL) {
return person;
}
else {
node* last = s;
while (last->next != NULL) {
last = last->next;
}
last->next = person;
}
return s;
}
void remove(node* head, string a) {
node* previous = NULL;
node* current = head;
if (current == NULL) {
cout << "Value cannot be found\n";
return;
}
else {
while (previous != NULL) {
if (current->data == a) {
previous->next = current->next;
delete current;
break;
}
current = current->next;
}
}
}
void print(node * head)
{
node* temp = head;
while (temp != NULL) // don't access ->next
{
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
In remove function, previous is most certainly NULL when you hit that while loop.
Perhaps consider a do-while loop instead (with better handling of previous).
You may be better off handling the first node in a different manner since the holder of its previous is essentially the root pointer.

Bidirectional list

The following code calculates the sum of the elements of the unidirectional list items greater than 3 and smaller than 8 and the result of the sum is changed the beginning of the list.
#include <iostream>
using namespace std;
struct List
{
int num;
List* nItem;
};
int Input()
{
int number;
cout << "Enter the number: "; cin >> number;
return number;
}
void MakeList(List **head, int n)
{
if (n > 0) {
*head = new List;
(*head)->num = Input();
(*head)->nItem = NULL;
MakeList(&(*head)->nItem, n - 1);
}
}
void Print(List* head)
{
if (head != NULL) {
cout << head->num << " ";
Print(head->nItem);
}
}
List* Add_start(List* head, int index, int elm)
{
List* p = new List;
p->num = elm;
p->nItem = NULL;
if (head == NULL) {
head = p;
}
else {
List* current = head;
for (int i = 0; (i < index - 1) && (current->nItem != NULL); i++)
{
current = current->nItem;
}
if (index == 0)
{
p->nItem = head;
head = p;
}
else {
if (current->nItem != NULL) {
p->nItem = current->nItem;
}
current->nItem = p;
}
}
return head;
}
int Sum(List* head)
{
int sum = 0;
List* p = head;
while(p) {
if ((p->num > 3) && (p->num < 8))
sum += p->num;
p = p->nItem;
}
return sum;
}
void DeleteList(List* head)
{
if (head != NULL) {
DeleteList(head->nItem);
delete head;
}
}
int main()
{
int n = 10;
List* head = NULL;
cout << "Enter 10 number to the list\n" << endl;
MakeList(&head, n);
int sum = Sum(head);
head = Add_start(head, 0, sum);
cout << "\nList: ";
Print(head);
cout << endl;
DeleteList(head);
system("pause");
return 0;
}
How can I do the same operation with a bidirectional list?
Notes:
A bidirectional (or double linked) list, also has a member pointing to the previous node: this is the whole difference between the 2 list types (as a consequence the first element - or the one at the left of the list, will have this member pointing to NULL). So, when such a node is created/inserted into a list, this new member should be set as well (I commented in the code places where this happens), for the new node and for the one following it (if any).
I modified the way of how a list is created - MakeList replaced by _MakeList2 + MakeList2; the underscore(_) in _MakeList2 specifies that it's somehow private (convention borrowed from Python) - it's not very nice, but I thought it's easier this way
I don't have Visual Studio on this computer, so I used gcc. It complained about system function so I had to add #include <stdlib.h>
I renamed some of the identifiers (List -> Node, Add_start -> AddNode, nItem -> nNode) either because the new names make more sense, or their names are consistent
I tried to keep the changes to a minimum (so the solution is as close as possible to your original post)
I enhanced (by adding an additional argument: toRight (default value: 1)) the Print func, so it can iterate the list both ways - I am iterating right to left (for testing purposes) before deleting the list
I corrected some (minor) coding style issues
Here's the modified code:
#include <iostream>
#include <stdlib.h>
using namespace std;
struct Node {
int num;
Node *pNode, *nNode; // Add a new pointer to the previous node.
};
int Input() {
int number;
cout << "Enter the number: "; cin >> number;
return number;
}
Node *_MakeList2(int n, Node *last=NULL) {
if (n > 0) {
Node *node = new Node;
node->num = Input();
node->pNode = last;
node->nNode = _MakeList2(n - 1, node);
return node;
}
return NULL;
}
Node *MakeList2(int n) {
return _MakeList2(n);
}
void Print(Node *head, int toRight=1) {
if (head != NULL) {
cout << head->num << " ";
if (toRight)
Print(head->nNode, 1);
else
Print(head->pNode, 0);
}
}
Node* AddNode(Node *head, int index, int elm) {
Node *p = new Node;
p->num = elm;
p->pNode = NULL; // Make the link between this node and the previous one.
p->nNode = NULL;
if (head == NULL) {
head = p;
} else {
Node *current = head;
for (int i = 0; (i < index - 1) && (current->nNode != NULL); i++) {
current = current->nNode;
}
if (index == 0) {
p->nNode = head;
head->pNode = p; // Make link between next node's previous node and the current one.
head = p;
} else {
if (current->nNode != NULL) {
p->nNode = current->nNode;
}
p->pNode = current; // Make the link between this node and the previous one.
current->nNode = p;
}
}
return head;
}
int Sum(Node* head) {
int sum = 0;
Node *p = head;
while(p) {
if ((p->num > 3) && (p->num < 8))
sum += p->num;
p = p->nNode;
}
return sum;
}
void DeleteList(Node *head) {
if (head != NULL) {
DeleteList(head->nNode);
delete head;
}
}
int main() {
int n = 10;
Node *head = NULL, *tail = NULL;
cout << "Enter " << n << " number(s) to the list" << endl << endl;
head = MakeList2(n);
int sum = Sum(head);
head = AddNode(head, 0, sum);
cout << endl << "List: ";
Print(head);
cout << endl;
tail = head;
if (tail) {
while (tail->nNode != NULL)
tail = tail->nNode;
cout << endl << "List reversed: ";
Print(tail, 0);
cout << endl;
}
DeleteList(head);
system("pause");
return 0;
}

Error while trying link previous node to the node after the node to be deleted

For this assignment I'm supposed to create a linked list with a insert function, a remove function, and a reverse function. It uses an input file to get the values for the list and uses another input file for values to be removed. My insert and reverse functions work, but my remove function gives me an error in the else statement when I try to link the previous node to the node after nodePtr. It says potentially uninitialized local pointer variable 'previousNodePtr' used. I'm not sure what I'm doing wrong to relink my list so that I can remove the desired node. You can find my problem in the remove function about 90 lines down. Here's my code:
//use a linked list to maintain a sorted list of numbers in descending
//order
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
const int ARRAY_SIZE = 100;
//input the data from a file to an array A, n is used to calculate the total number of
//data in the file
void input(int A[], int & n, const string & fileName)
{
ifstream inputFile;
int value;
inputFile.open(fileName.c_str());
if (!inputFile)
{
cout << "Error opening the file " << fileName << endl;
exit(0);
}
//read data until the end of the file and calculate n
while(inputFile >> value)
{
A[n] = value;
n++;
}
inputFile.close();
}
struct ListNode
{
int value;
ListNode *next;
//struct constructor
ListNode (int input_value, ListNode * input_next = NULL)
{
value = input_value;
next = input_next;
}
};
//define a class for the linked list
class LinkedList
{
private:
ListNode *head;// the head of the linked list
public:
LinkedList() {head = NULL;}
~LinkedList();
void insert(int x);// add a number x the end of the list
void remove(int x);//remove x from the list
void reverse();//reverse the list
void printListToFile(const string & fileName) const; // print the list to a file
void printList() const; // print the list to the console (computer's screen), this function may be helpful when you test your program
};
//insert
void LinkedList::insert(int x)
{
ListNode *nodePtr, *previousNodePtr;
if (head == NULL || head->value <= x)
{
head = new ListNode(x, head);
}
else
{
previousNodePtr = head;
nodePtr = head->next;
while (nodePtr != NULL && nodePtr->value > x)
{
previousNodePtr = nodePtr;
nodePtr = nodePtr->next;
}
previousNodePtr->next = new ListNode(x, nodePtr);
}
}
//remove
void LinkedList::remove(int x)
{
ListNode *nodePtr, *previousNodePtr;
if (!head) return;
if (head->value == x)
{
nodePtr = head;
head = head->next;
delete nodePtr;
}
else
{
nodePtr = head;
while (nodePtr != NULL && nodePtr->value != x)
{
previousNodePtr = nodePtr;
nodePtr = nodePtr->next;
}
if (nodePtr)
{
previousNodePtr->next = nodePtr->next;
delete nodePtr;
}
}
}
//reverse
void LinkedList::reverse()
{
ListNode *nodePtr, *previousNodePtr, *nodeNext;
nodePtr = head;
nodeNext = NULL;
previousNodePtr = NULL;
while (nodePtr != NULL)
{
nodeNext = nodePtr->next;
nodePtr->next = previousNodePtr;
previousNodePtr = nodePtr;
nodePtr = nodeNext;
}
head = previousNodePtr;
}
void LinkedList::printList() const
{
ListNode *p = head;
while (p != NULL)
{
if (p->next == NULL)
cout << p->value << endl;
else
cout << p->value << " -> ";
p = p->next;
}
}
void LinkedList::printListToFile(const string & fileName) const
{
ofstream outputFile;
outputFile.open(fileName.c_str());
if (!outputFile)
{
cout << "Error opening the file " << fileName << endl;
exit(0);
}
ListNode *p = head;
while (p != NULL)
{
if (p->next == NULL)
outputFile << p->value << endl;
else
outputFile << p->value << " -> ";
p = p->next;
}
outputFile.close();
}
//destructor, delete all nodes
LinkedList::~LinkedList()
{
ListNode *p = head;
while (p != NULL)
{
ListNode * garbageNode = p;
p = p->next;
delete garbageNode;
}
}
int main()
{
int A[ARRAY_SIZE];// store numbers for insert
int B[ARRAY_SIZE]; // store numbers for remove
int n = 0;// the number of elements that will be stored in A
int m = 0;// the number of elements that will be stored in B
string inputFile1 = "hw2_Q1_insertData.txt"; // file with data for insert
string inputFile2 = "hw2_Q1_removeData.txt"; // file with data for remove
//input data from the two files
input(A, n, inputFile1);
input(B, m, inputFile2);
LinkedList list;
//do insert operations
for (int i = 0; i < n; i++)
list.insert(A[i]);
//do remove operations
for (int i = 0; i < m; i++)
list.remove(B[i]);
//list.printList();
//reverse the list
list.reverse();
list.printList();
//print the list to the output file
string outputFile = "hw2_Q1_output.txt"; // the output file
list.printListToFile(outputFile);
return 0;
}
In this case, the compiler is being overly careful - it is warning you that there might be a path through your program where previousNodePtr is not set to a value.
As you are checking for head matching the value and returning earlier, you will always go through the loop at least once, so previousNodePtr will always be set.
One way to fix this would be to set previousNodePtr to NULL when you declare it - this will get rid of the warning.
A better way would be to realise you don't need to test against the head node again in your loop. You can then do something like this:
...
else
{
ListNode *previousNodePtr = head;
nodePtr = head->next;
while (nodePtr != NULL && nodePtr->value != x)
{
previousNodePtr = nodePtr;
nodePtr = nodePtr->next;
}
if (nodePtr)
{
previousNodePtr->next = nodePtr->next;
delete nodePtr;
}
Also note that I moved the declaration of previousNodePtr closer to where it was used.