Deleting nodes that are outside of a specific numeric range in a singly linked list - c++

I am working on this program that reads a text file and grabs the data out of the text file and inserts it into nodes of a linked list.
I have the whole program running and working fine besides node deletion. I am filtering the data from the text file so I only need to print out the data that have values within a certain range. I can do this with an if() statement and it works fine but that's not the result I want.
I want to delete the nodes that are outside of the specified range and free up the memory that they are using. I have a few lines of code that I wrote that try to do this but it just ends up deleting the entire list. So if anyone could point me in the right direction and tell me what I'm doing wrong that would be great!
#include <fstream>
#include <iostream>
using namespace std;
struct Employee
{
string firstN;
string lastN;
float salary;
float bonus;
float deduction;
Employee *link;
};
typedef Employee* EmployPtr;
void insertAtHead( EmployPtr&, string, string, float, float,float );
void insert( EmployPtr&, string, string, float, float,float );
float netSalary( EmployPtr& );
int main()
{
//Open file
fstream in( "payroll.txt", ios::in );
//Read lines
string first, last;
float salary, bonus, deduction;
EmployPtr head = new Employee;
//Inserts all the data into a new node in the linked list, creating a new node each time the loop executes.
while( in >> first >> last >> salary >> bonus >> deduction)
insertAtHead (head, first, last, salary, bonus, deduction);
//Close file
in.close();
cout << "-Salary in the range of ($45,000 - $60,000)-\n" << "Printed in format: First Name, Last Name, Salary, Bonus, Deduction, Net Salary.\n";
EmployPtr iter, temp;
for(iter = head; iter!= NULL; iter = iter->link)
{
temp = head;
//Deletes nodes outside of range.
while(netSalary(iter)<45000 || netSalary(iter)>60000)
{
EmployPtr nodeToDelete = temp;
temp = temp->link;
delete nodeToDelete;
}
cout << iter->firstN << ", " << iter->lastN << ", " << iter->salary << ", " << iter->bonus << ", " << iter->deduction << ", " << netSalary(iter) <<endl;
}
return 0;
}
//Based off of the input values, this function will create a new node and insert it at the beginning of the linked list. This function ONLY allows insertion at the beginning of the list and no where else.
void insertAtHead(EmployPtr& head, string firstValue, string lastValue,
float salaryValue, float bonusValue,float deductionValue)
{
EmployPtr tempPtr= new Employee;
tempPtr->firstN = firstValue;
tempPtr->lastN = lastValue;
tempPtr->salary = salaryValue;
tempPtr->bonus = bonusValue;
tempPtr->deduction = deductionValue;
tempPtr->link = head;
head = tempPtr;
}
//Based off of the input values, this function creates a new node and inserts it AFTER the node provided in the argument.
void insert(EmployPtr& afterNode, string firstValue, string lastValue,
float salaryValue, float bonusValue,float deductionValue)
{
EmployPtr tempPtr= new Employee;
tempPtr->firstN = firstValue;
tempPtr->lastN = lastValue;
tempPtr->salary = salaryValue;
tempPtr->bonus = bonusValue;
tempPtr->deduction = deductionValue;
tempPtr->link = afterNode->link;
afterNode->link = tempPtr;
}
//This function calculates a net salary based off of the salary, bonus, and deduction variables of the input node.
float netSalary(EmployPtr& node)
{
float netSalary, newDeduction;
newDeduction = ((node->salary) + (node->bonus)) * (node->deduction);
netSalary = (node->salary + node->bonus) - newDeduction;
return netSalary;
}
EDIT: Changed && back to || still having issue.
EDIT #2: Solution
while(netSalary(iter)<45000 || netSalary(iter)>60000)
{
EmployPtr nodeToDelete = new Employee;
nodeToDelete = iter;
iter = iter->link;
delete nodeToDelete;
}

This line right here:
while(netSalary(iter)<45000 && netSalary(iter)>60000)
I believe your conditional should be OR (||). It would not make sense for a value to both be less than 45000 and more than 60000 at the same time.
Given a value 25000, it will be less than 45000, but not more than 60000, therefore nothing will ever get deleted.
Edit:
Perhaps try something along these lines:
for (iter = head; iter != NULL; iter = iter->link)
{
cout << iter->salary; // so you can see what node it's looking at
if (netSalary(iter) < 45000 || netSalary(iter) > 60000)
{
EmployPtr nodeToDelete = iter;
iter = iter->link; // difference here is that you're explicitly moving the iter forward
delete nodeToDelete;
}
}

I think you want the condition to be
while(netSalary(iter) >= 45000 && netSalary(iter) <= 60000)
I say think because I don't see a statement of what you actually want to filter out.

You should change the while condition, as a start. The '&&' should be '||' because it doesn't make sense to have '< 45000' while at the same time being greater than 60000. Also, as an alternative, why not skip adding nodes to the list entirely if they don't meet these conditions? In other words, while creating the list, check these conditions and don't add to the list if they aren't met. That way you aren't creating a list and then immediately coming back and modifying it.
EDIT:
Okay, the problem, I believe, is with the while loop using 'iter'. As soon as the iterator matches your condition in the while loop, you do nothing to move the iterator forward after that (because you don't get back out to the for loop) and therefore delete the rest of the list inside the while loop. Try changing the while to an 'if' and see what you get.

The most recent solution should take care of the main issue (deleting the entire list because the loop started with head instead of iter), but you may still run into another issue. If the last element is deleted, the next time the loop conditional is checked, netSalary will be called on a null pointer (as iter will be null once it's advanced to iter->link). Furthermore, attempting to modify that loop to account for the null pointer could instead lead the outside for loop attempting to access the link member of a null pointer.
The simplest solution that I could suggest would be to modify the code to use just one while loop and conditional, as shown in the following code:
EmployPtr iter = head, temp;
while(iter!= NULL)
{
if(netSalary(iter)<45000 || netSalary(iter)>60000)
{
// bad node, delete and advance
EmployPtr nodeToDelete = iter;
iter = iter->link;
delete nodeToDelete;
}
else
{
// good node, write and advance
cout << iter->firstN << ", " << iter->lastN << ", " << iter->salary << ", " << iter->bonus << ", " << iter->deduction << ", " << netSalary(iter) <<endl;
iter = iter->link;
}
}

Related

Inserting a basic singly linked list node seems to break my c++ code?

Singly Linked List and Node classes and the start of the main function, where I wrote a brief outline of the code functionality. The issue is toward the end of the main function. I wrote '...' in place of what I believe to be irrelevant code because it simply parses strings and assigns them to the string temp_hold[3] array.
#include <bits/stdc++.h>
using namespace std;
class Node {
public:
string value;
string attr;
string tagname;
Node *next;
Node(string c_tagname, string c_attr, string c_value) {
this->attr = c_attr;
this->value = c_value;
this->tagname = c_tagname;
this->next = nullptr;
}
};
class SinglyLinkedList {
public:
Node *head;
Node *tail;
SinglyLinkedList() {
this->head = nullptr;
this->tail = nullptr;
}
void insert_node(string c_tagname, string c_attr,string c_value) {
Node *node = new Node(c_tagname,c_attr, c_value);
if (!this->head) {
this->head = node;
} else {
this->tail->next = node;
}
this->tail = node;
}
};
int main(int argc, char **argv) {
/* storage is a vector holding pointers to the linked lists
linked lists are created and the linked list iterator sll_itr is incremented when
previous line begins with '</' and the currentline begins with '<'
linked lists have nodes, which have strings corresponding to tagname, value, and attribute
*/
SinglyLinkedList *llist = new SinglyLinkedList();
vector<SinglyLinkedList*> sllVect;
sllVect.push_back(llist);
auto sll_itr = sllVect.begin();
string temp_hold[3];
// to determine new sll creation
bool prev = false;
bool now = false;
//input
int num1, num2;
cin >> num1; cin >> num2;
//read input in
for (int i = 0; i <= num1; ++i) {
string line1, test1;
getline(cin, line1);
test1 = line1.substr(line1.find("<") + 1);
//determine to create a new linked list or wait
if (test1[0] == '/') {
prev = now;
now = true;
} else {
//make a node for the data and add to current linked list
if (i > 0) {
prev = now;
now = false;
//if last statement starts with '</' and current statment starts with '<'
// then start a new sll and increment pointer to vector<SinglyLinkedList*>
if (prev && !now) {
SinglyLinkedList *llisttemp = new SinglyLinkedList();
sllVect.push_back(llisttemp);
sll_itr++;
}
}
//parse strings from line
int j = 0;
vector<string> datastr;
vector<char> data;
char test = test1[j];
while (test) {
if (isspace(test) || test == '>') {
string temp_for_vect(data.begin(),data.end());
if (!temp_for_vect.empty()) {
datastr.push_back(temp_for_vect);
}
data.clear();
} else
if (!isalnum(test)) {
} else {
data.push_back(test);
}
j++;
test = test1[j];
}
//each node has 3 strings to fill
int count = 0;
for (auto itrs = datastr.begin(); itrs!=datastr.end(); ++itrs) {
switch (count) {
case 0:
temp_hold[count]=(*itrs);
break;
case 1:
temp_hold[count]=(*itrs);
break;
case 2:
temp_hold[count]=(*itrs);
break;
default:
break;
}
count++;
}
}
cout << "before storing node" << endl;
(*sll_itr)->insert_node(temp_hold[0], temp_hold[1], temp_hold[2]);
cout << "after" << endl;
}
cout << "AFTER ELSE" << endl;
return 0;
}
And here is the line that breaks the code. The auto sll_itr is dereferenced which means *sll_itr is now a SinglyLinkedList* and we can call the insert_node(string, string, string) to add a node to the current linked list. However when I keep the line, anything after the else statement brace does not run, which means the cout<<"AFTER ELSE"<< endl; does not fire. If I remove the insert_node line, then the program runs the cout<<"AFTER ELSE"<< endl; I am unsure what the issue is.
(*sll_itr)->insert_node(temp_hold[0],temp_hold[1],temp_hold[2]);
cout << "after" << endl;
} //NOT HANGING. This closes an else statement.
cout << "AFTER ELSE" << endl;
return 0;
}
Compiled as g++ -o myll mylinkedlist.cpp and then myll.exe < input.txt And input.txt contains
8 3
<tag1 value = "HelloWorld">
<tag2 name = "Name2">
</tag2>
</tag1>
<tag5 name = "Name5">
</tag5>
<tag6 name = "Name6">
</tag6>
Your linked list isn't the problem, at least not the problem here.
A recipe for disaster in the making: retaining, referencing, and potentially manipulating, an iterator on a dynamic collection that potentially invalidates iterators on container-modification. Your code does just that. tossing out all the cruft between:
vector<SinglyLinkedList*> sllVect;
sllVect.push_back(llist);
auto sll_itr = sllVect.begin();
....
SinglyLinkedList *llisttemp = new SinglyLinkedList();
sllVect.push_back(llisttemp); // HERE: INVALIDATES sll_iter on internal resize
sll_itr++; // HERE: NO LONGER GUARANTEED VALID; operator++ CAN INVOKE UB
To address this, you have two choices:
Use a container that doesn't invalidate iterators on push_back. There are really only two sequence containers that fit that description: std::forward_list and std::list.
Alter your algorithm to reference by index`, not by iterator. I.e. man your loop to iterate until the indexed element reaches end-of-container, then break.
An excellent discussion about containers that do/do-not invalidate pointers and iterators can be found here. It's worth a read.

Skip List C++ segmentation fault

I'm trying to implement the Skip List using this article Skip List.
Code:
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<limits>
using namespace std;
template<class T>
class SkipList{
private:
class SkipNode{
public:
T* key; //Pointer to the key
SkipNode** forward; //Forward nodes array
int level; //Node level
//SkipNode constructor
SkipNode(T* key, int maxlvl, int lvl){
forward = new SkipNode*[maxlvl];
this->key=key;
level=lvl;
}
//Method that print key and level node
print(){
cout << "(" << *key << "," << level << ") ";
}
};
SkipNode *header,*NIL; //Root and End pointers
float probability; //Level rate
int level; //Current list level
int MaxLevel; //Maximum list levels number
//Function that returns a random level between 0 and MaxLevel-1
int randomLevel(){
int lvl = 0;
while( (float(rand())/RAND_MAX < probability) && (lvl < MaxLevel-1) )
lvl++;
return lvl;
}
public:
//SkipList constructor
SkipList(float probability, int maxlvl){
this->probability = probability;
MaxLevel = maxlvl;
srand(time(0));
header=new SkipNode(NULL,MaxLevel,0); //Header initialization
T* maxValue = new T;
*maxValue = numeric_limits<T>::max(); //Assign max value that T can reach
NIL = new SkipNode(maxValue,0,0); //NIL initialization
level=0; //First level
for(int i=0; i<MaxLevel; i++){ //Every header forward node points to NIL
header->forward[i]=NIL;
}
}
//SkipList destructor
~SkipList(){
delete header;
delete NIL;
}
//Method that search for a key in the list
SkipNode* search(T* key){
SkipNode* cursor = header;
//Scan the list
for(int i=level; i>=0; i--)
while(*(cursor->forward[i]->key) < (*key))
cursor=cursor->forward[i];
cursor=cursor->forward[0];
if(*(cursor->key) == *key)
return cursor;
return NULL;
}
//Method that insert a key in the list
SkipList* insert(T* key){
SkipNode* cursor = header;
SkipNode* update[MaxLevel]; //Support array used for fixing pointers
//Scan the list
for(int i=level; i>=0; i--){
while(*(cursor->forward[i]->key) < *(key))
cursor=cursor->forward[i];
update[i]=cursor;
}
cursor=cursor->forward[0];
if(*(cursor->key) == *(key)){ //Node already inserted
return this;
}
int lvl = randomLevel(); //New node random level
if(lvl > level){ //Adding missing levels
for(int i=level+1; i<=lvl; i++)
update[i]=header;
level=lvl;
}
SkipNode* x = new SkipNode(key,MaxLevel,lvl); //New node creation
for(int i=0; i<=lvl; i++){ //Fixing pointers
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}
return this;
}
//Method that delete a key in the list
SkipList* erase(T* key){
SkipNode* cursor = header;
SkipNode* update[MaxLevel]; //Support array used for fixing pointers
//Scan the list
for(int i=level; i>=0; i--){
while(*(cursor->forward[i]->key) < *(key))
cursor=cursor->forward[i];
update[i]=cursor;
}
cursor=cursor->forward[0];
if(*(cursor->key) == *(key)){ //Deletetion of the founded key
for(int i=0; i<=level && update[i]->forward[i] == cursor; i++){
update[i]->forward[i] = cursor->forward[i];
}
delete cursor;
while(level>0 && header->forward[level]==NIL){
level=level-1;
}
}
return this;
}
//Method that print every key with his level
SkipList* print(){
SkipNode* cursor = header->forward[0];
int i=1;
while (cursor != NIL) {
cursor->print();
cursor = cursor->forward[0];
if(i%15==0) cout << endl; i++;
}
cout << endl;
return this;
}
};
main(){
SkipList<int>* list = new SkipList<int>(0.80, 8);
int v[100];
for(int i=0; i<100; i++){
v[i]=rand()%100;
list->insert(&v[i]);
}
list->print();
cout << endl << "Deleting ";
for(int i=0; i<10; i++){
int h = rand()%100;
cout << v[h] << " ";
list->erase(&v[h]);
}
cout << endl;
list->print();
cout << endl;
for(int i=0; i<10; i++){
int h = rand()%100;
cout << v[h] << " ";
if(list->search(&v[h]))
cout << " is in the list" << endl;
else
cout << " isn't in the list" << endl;
}
delete list;
}
It gives me Segmentation Fault on line 59 (the for-cycle on the insert), but I can't understand why. May you help me please? I will accept any other improvement that you suggest. My deadline is on two days, that's why I'm asking for help.
EDIT:
I've corrected the code with bebidek suggestions (Thanks). Now first level is 0. It seems to be working, but sometimes some nodes is not inserted correctly and the search give a bad result.
LAST EDIT:
It works, thanks to all
ONE MORE EDIT:
Added comments to code, if you have any suggestion you're welcome
The biggest problem in your code is probably NIL=new SkipNode(numeric_limits<T*>::max());
First of all i suspect you want the key pointer to point to a memory address that contains the biggest possible int value.
But that's not what's actually happening here. Instead the key pointer points to the biggest possible memory-address which is most likely not available for your process.
Also the forward property probably contains an array of junk pointers.
Then when the first loop in the insert method is executed this leads to 2 problems:
while(*(cursor->forward[i]->key) < *(key)) will compare the key value to an invalid pointer
cursor=cursor->forward[i]; will re-assign cursor to an invalid pointer
I would first suggest you'd change the design to let SkipNode keep a value to T instead of a pointer:
class SkipNode{
public:
T key;
SkipNode* forward[100];
This will make a lot of pointer related code unnecessary and make the code simpler so less likely to run into access violation.
Also it might be cleaner to use an actual NULL (or event better nullptr) value instead of a dummy NIL value to indicate the end of the list.
So, first problem is when you create NIL node:
NIL=new SkipNode(numeric_limits<T*>::max());
As argument you should use pointer to existing variable, for example:
T* some_name = new T;
*some_name = numeric_limits<T>::max();
NIL = new SkipNode(some_name);
Notice, I used T instead of T* in numeric_limits. Of course you have to remember about deleting this variable in destructor.
Second problem is that level variable in your code sometimes is inclusive (I mean level number level exists) as in line 61, and sometimes exclusive (level number level doesn't exist) as in line 71. You have to be consistent.
Third problem is in line 52. You probably mean cursor=cursor->forward[1];, but after loop i = 0, and forward[0] doesn't have any sense in your code.
EDIT:
Fourth and fifth problem is in erase function.
cursor->~SkipNode();
It won't delete your node, but only run empty destructor. Use delete cursor; instead.
And in loop you probably wanted to write update[i]->forward[i] == cursor instead of !=.
ONE MORE EDIT:
You haven't implemented any destructor of SkipList and also you forgot about delete list; at the end of main(). These two will give you a memory leak.
ANOTHER EDIT:
srand(time(0));
This line should be executed once at the beginning of main and that's all. If you execute it before each random generation, you will get the same result every time (as time(0) counts only seconds and your program can run function randomLevel() more than once a second).
You also forgot about rewriting precision variable in constructor of SkipList.
NEXT EDIT:
In your insert function you don't have level randomization. I mean, you do not have ability of inserting node of level less than level of whole skip list. It's not error which will crash your program or give wrong results, but time complexity of queries in your structure is O(n) instead of O(log n).
You should use lvl instead of level in this loop in insert function:
for(int i=1; i<level; i++){
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}
And also minimum result of your random function randomLevel should be 1 instead of 0, as you don't want node witch level=0.

Reading data from a file into a linked list, and searching for elements in the linked list

I am trying to write code and am taking in data from a file and storing it into a struct and creating a linked list. I don't see any immediate problems with my code but I made a function to check if a zipcode exists within any of the structs in the linked list and it doesn't seem to be working. Here's what the data from the file looks like:
id,price,bedrooms,bathrooms,sqft,yr_built,zipcode
1370804430,543115,2,1,1380,1947,98199
3744000040,518380,4,2.5,2810,2014,98038
3313600266,190000,3,1,1180,1966,98002
EDIT I implemented new code for reading the file into a linked list but my function for finding the zipcode isn't working. When I enter a zipcode I know exists in the file, nothing gets printed.
typedef struct housetype house;
struct housetype{
int id;
int price;
int bedrooms;
double bathrooms;
int sqft;
int year;
int zipcode;
house *next; };
int findzipcode(house* head, int zip){
house* current = head;
while(current != NULL){
if(current->zipcode == zip){
cout << "Zipcode exists" << endl;
return 1;
break;}
current = current->next;
}
return 0;}
int main(){
house *head = NULL;
FILE *houseinfo = NULL;
houseinfo = fopen("house-info-v4.txt", "r");
if (houseinfo == NULL) {
cout << "Error reading file" << endl;
}
else {
int res;
house *last = head;
house h;
do {
res = fscanf(houseinfo, "%d,%d,%d,%lf,%d,%d,%d",
&h.id, &h.price, &h.bedrooms, &h.bathrooms,
&h.sqft, &h.year, &h.zipcode);
if (res > 0) { // <== fscanf successful (if file fits!)
house *n = (house*)malloc(sizeof(house));
memcpy(n, &h, sizeof(house));
n -> next = NULL;
if (last) last->next = n;
if ( ! head) head = n;
last = n;
}
} while (res > 0);
}
int zip;
cout << "Enter a zipcode" << endl;
cin >> zip;
findzipcode(head, zip);}
The code you present will only load the first item, so if you are searching for later items it won't work.
Notice how the code with the fscanf doesn't have a loop.
I think you read only one row from data and made one node for linked list. If you want to make linked list then you have to read all rows from file and create node of each row and link it with loop.
For example
while (1)
read_file
if feof:
break
make_node
add row_info -> node
link_node
Trying to write this code with c++
At least two problems
head remains NULL
only one house is read from the file
Try something like
if (houseinfo == NULL) {
cout << "Error reading file" << endl;
}
else {
int res; // <== fscanf result
house *last = head; // <== last house set (head is still null)
house h; // <== holds values until fscanf check
do {
res = fscanf(houseinfo, "%d,%d,%d,%lf,%d,%d,%d",
&h.id, &h.price, &h.bedrooms, &h.bathrooms,
&h.sqft, &h.year, &h.zipcode);
if (res > 0) { // <== fscanf successful (if file fits!)
house *n = (house*)malloc(sizeof(house)); // c++ cast malloc
memcpy(n, &h, sizeof(house)); // <== copy to allocated house
n -> next = NULL;
if (last) last->next = n; // last next is our n
if ( ! head) head = n; // set head if necessary
last = n; // last house set is n, now
}
} while (res > 0);
}
Explanations are in comments. Basically
loop on fscanf until no more lines in file
read values in local variable h
if fscanf successful, allocate and set n
if we have a last, set its next to our new n
if head not set yet, set it
finally, last points to n to be used at next iteration
First of all select a language. Either C or C++.
There is a problem in your code. The thing is, you have memory leak here and that in one way is the reason for the problem.
Well to say it in detail, you allocate memory and get input and store it in that memory and then you forget about it. You don't forget but yes there is no way you can access it. This is the memory leak. And then you pass a NULL valued pointer to the search function and it returns nothing.
This change will solve the problem,
house *n = malloc(sizeof(house));
head = n; //<----- We are not losing the allocated memory
fscanf(houseinfo, "%d.....
..
Also there is one redundant thing in your code (this is not causing the problem but yes you should know this).
cout << "Zipcode exists" << endl;
return 1;
break; //<--- this is redundant
}
Once a return statement is executed that break will never be executed. Just put the return statement. No use putting the break here.
Another point you didn't free the allocated memory.(Ah! to free you need to keep track of it - which you didn't do here). After you are done working with it you should free it.
findzipcode(head, zip);
free(head);
Also check the return value of malloc. In case it returns NULL take necessary action.
in the following proposed code:
error conditions are checked and acted upon
most of the comments to the question are incorporated
And now, the proposed code:
#include <cstdio>
#include <cstdlib>
#include <errno.h>
struct housetype
{
int id;
int price;
int bedrooms;
double bathrooms;
int sqft;
int year;
int zipcode;
struct housetype *next;
};
typedef struct housetype house;
void findzipcode(house* head, int zip)
{
house* current = head;
while( current )
{
if(current->zipcode == zip)
{
cout << "Zipcode exists" << endl;
return;
}
current = current->next;
}
}
int main( void )
{
house *head = NULL;
FILE *houseinfo = fopen("house-info-v4.txt", "r");
if ( !houseinfo )
{
cerr << "fopen failed due to" << strerror( errno ) << endl;
exit( EXIT_FAILURE );
}
// implied else, fopen successful
int res;
house *last = NULL;
house h;
while( 7 == (res = fscanf(houseinfo, "%d,%d,%d,%lf,%d,%d,%d",
&h.id, &h.price, &h.bedrooms, &h.bathrooms,
&h.sqft, &h.year, &h.zipcode) ) )
house *n = new house;
if( !n )
{
cerr << "malloc failed due to: " << strerror( errno ) << endl;
exit( EXIT_FAILURE );
}
// implied else, malloc successful
memcpy(n, &h, sizeof(house));
n -> next = NULL;
if (last)
last->next = n;
else if ( ! head)
head = n;
last = n;
}
int zip;
cout << "Enter a zipcode" << endl;
cin >> zip;
findzipcode(head, zip);
while( head )
{
house *nextHouse = head->next;
delete head;
head = nextHouse;
}
}
Of course, if you really want to use C++, then suggest using a vector rather than a linked list

inserting into ordered LLL

So far I've been able to correctly add items into my linear linked list but I cannot compare data members from two separate places in my list so that I can find the correct place to insert my new node. I try to create a dummy pointer so that no data maybe lost when I get the next object in the linear linked list so that I may compare the current and previous items. However the previous item just get overridden by the new item. How do I create a dummy pointer to store data without writing over the last object?
(code below)
int political_party::insert_republican_party(candidate_info & a_candidate) {
republican_party * current = new republican_party(); //intiates the new node
current->a_candidate = a_candidate; //gives the new node a perticular value:
current->next = NULL; // declares next the next as null
if (rep_head == NULL) //no nodes exist so far
{
current->next = rep_head; //the node after current contains the value rep_head
rep_head = current; //rep head equals the value of current
}
else if (rep_head->next == NULL) {
republican_party * current2 = rep_head;
current2 = current2->next;
republican_party * previous = rep_head;
// previous -> a_candidate = current -> a_candidate;
// current -> a_candidate = a_candidate;
if (current->a_candidate.get_rank() > previous->a_candidate.get_rank()) {
current->next = rep_head;
rep_head = current;
}
}
else {
republican_party * current2 = rep_head;
while (current->next != NULL) {
current = current->next;
}
current->next = current2->next;
current2->next = current;
return 2;
}
}
Welcome to C++ and welcome to StackOverflow.
You have some hurdles to clear, so I'm going to do the following:
1) make some observations about your code.
2) Then I'm going to show you some sample code that runs with eough "printing" so you can tell what is going on.
3) Then I'll end with a hint or two on how you can proceed.
The big thing I want you to get is learning how to put enough print-outs in your code so you can follow what is going on.
They don't have to be pretty, they're just there to help your troubleshooting, then you can comment them out.
1) observations about your code.
This will be kind of long, I'm including your version of the code to talk about it.
Let's sketch out how the code you posted will run:
0: (step 0, the beginning)
rep_head = NULL
pretty much an empty list.
1: insert_republican_party( c1 );
current points to c1
execute /*A*/ if() block since rep_head is null (see below for /*A*/,
I might edit the original question to have these values for discussion).
after:
rep_head = current, so...
rep_head points to c1.
rep_head next = NULL
at the end of /*A*/ if-block, the list looks something like this:
rep_head-----+
|
v
current----> R[c1] ---next--> NULL
I'm kind of smashing R (republican_party pointer) together with c1 (a_candidate reference).
2: insert_republican_party( c2 );
rep_head points to c1, rep_head.next = NULL
current points to c2, current.next = NULL
We execute the "/*B*/ else if() block" since rep_head->next == NULL.
local var current2 gets pointed to same place rep_head points to.
current2 then pointed to rep_head->next, so current2 = NULL
At this point we don't do anything else with current2.
we'll skip the commented-out lines.
And you're looking to do an in-order insertion for your linked list based on candidate rank...
so we have 3 possible conditions:
maybe c1.rank < c2.rank
maybe c1.rank == c2.rank
maybe c1.rank > c3.rank
The /*C*/ if statement is checking to see if c2.rank > c1.rank
Suppose c2.rank > c1.rank then fine, c2 becomes the new head of the list.
after that:
current->next points to c1.
rep_head points to c2.
So the list might look like this, more or less (assuming c2.rank > c1.rank).
rep_head---+
|
v
current--> R[c2]--next--> R[c1]--next--> NULL
At this stage your previous pointers seem out synch.
Suppose c2.rank <= c1.rank, then... nothing happens and c2 never makes it onto your linked list.
This seems like a hole.
However I would recommend patching that hole in your else-block, /*E* below.
3: insert_republican_party( c3 );
Lets say we somehow get down to /*D*/ else.
and this list looks like step 2, above.
current is pointing to c3 (current.next points to NULL)
rep_head is pointing to c2.
rep_head.next is pointing to c1.
current2 is pointed to same as rep_head, which means c2.
the while() loop walks current down the linked list,
which seems like a logic errror? Should it be walking current2 (instead of current)?
int political_party::insert_republican_party(candidate_info & a_candidate)
{
republican_party * current = new republican_party(); //intiates the new node
current -> a_candidate = a_candidate; //gives the new node a perticular value:
current -> next = NULL; // declares next the next as null
/*A*/ if(rep_head == NULL) //no nodes exist so far
{
current -> next = rep_head; //the node after current contains the value rep_head
rep_head = current; //rep head equals the value of current
}
/*B*/ else if (rep_head -> next == NULL)
{
republican_party * current2 = rep_head;
current2 = current2 -> next;
republican_party * previous = rep_head;
// previous -> a_candidate = current -> a_candidate;
// current -> a_candidate = a_candidate;
/*C*/ if(current -> a_candidate.get_rank() > previous -> a_candidate.get_rank())
{
current -> next = rep_head;
rep_head = current;
}
}
/*D*/ else
{
republican_party * current2 = rep_head;
/*E*/ while(current -> next != NULL)
{
current = current -> next;
}
current -> next = current2 -> next;
current2 -> next = current;
return 2;
}
}
2) sample code that runs
I've hacked up a pretty rough "linked_list.cpp" version that has just enough code to run.
I'm going to suggest you adopt the printout part (Overrides for the output stream << since we're in c++ land).
Then add liberal printouts to your code. Search for "ostream&" in the code to find those overrides.
fwiw, this would have been easier to comment on if you had posted enough C++ code to run; I encourage you to do that in the future.
=== begin sample output ===
$ c++ linked_list.cpp
$ ./a.out
Hello from main()
c1=C[c1, rank=1]
c2=C[c2, rank=1]
c3=C[c3, rank=1]
before, r=R[C[?, rank=-1]]--next--> NULL
after, r=R[C[c1, rank=1]]--next--> NULL
party=Party.rep_head=0
1. calling party.insert_republican_party( C[c1, rank=1] )
insert: adding current=0x1498c20, *current=R[C[c1, rank=1]]--next--> NULL
insert.A: now rep_head=0x1498c20, *rep_head=R[C[c1, rank=1]]--next--> NULL
1. party=Party.rep_head=0x1498c20---> R[C[c1, rank=1]]--next--> NULL
2. calling party.insert_republican_party( C[c2, rank=1] )
insert: adding current=0x1498c50, *current=R[C[c2, rank=1]]--next--> NULL
insert.B: now rep_head=0x1498c20
2. party=Party.rep_head=0x1498c20---> R[C[c1, rank=1]]--next--> NULL
$
=== end sample output ===
=== begin linked_list.cpp ===
#include<iostream>
// disclaimer: I'm not trying to solve anybody's homework problem.
// Just adding enough code (not even good style code at that) to make
// it run well enough to offer a hint or two.
using namespace std;
class candidate_info {
public:
int rank;
const char *name;
int get_rank() { return rank; }
candidate_info( );
candidate_info( int rank, const char *name );
friend ostream& operator<<(ostream& os, const candidate_info& c);
};
class republican_party {
public:
republican_party * next;
republican_party * prev;
candidate_info a_candidate;
republican_party();
friend ostream& operator<<(ostream& os, const republican_party& r);
};
class political_party {
public:
republican_party * rep_head;
political_party();
int insert_republican_party(candidate_info & a_candidate);
friend ostream& operator<<(ostream& os, const political_party & p);
};
int main( int argc, char **argv ) {
cout << "Hello from main()\n";
candidate_info c1( 1, "c1" );
candidate_info c2( 1, "c2" );
candidate_info c3( 1, "c3" );
cout << "c1=" << c1 << "\n";
cout << "c2=" << c2 << "\n";
cout << "c3=" << c3 << "\n";
republican_party r;
cout << "before, r=" << r << "\n";
r.a_candidate = c1;
cout << "after, r=" << r << "\n";
political_party party;
cout << "party=" << party << "\n";
cout << "1. calling party.insert_republican_party( " << c1 << " )\n";
party.insert_republican_party( c1 );
cout << "1. party=" << party << "\n";
cout << "2. calling party.insert_republican_party( " << c2 << " )\n";
party.insert_republican_party( c2 );
cout << "2. party=" << party << "\n";
}
// === CANDIATE_INFO things ===
candidate_info::candidate_info( ) {
this->rank = -1;
this->name = "?";
}
candidate_info::candidate_info( int rank, const char *name ) {
this->rank = rank;
this->name = name;
}
ostream& operator<<(ostream& os, const candidate_info& c)
{
os << "C[" << c.name << ", rank=" << c.rank << "]";
return os;
}
// === REPUBLICAN_PARTY things ===
republican_party::republican_party()
{
next = prev = NULL;
}
ostream& operator<<(ostream& os, const republican_party& r)
{
// note about flush: sometimes when I feed a bad pointer to this
// it can blow up and hit "segmentation fault" so I'm adding
// flushes here and there to give us an idea of how far along we
// actually got before then.
// I strongly encourage you to do something like << for all of your
// classes to make it easier to see what is going on.
// Maybe you did this already, dont know (didn't see the full
// definition for republican_party and other classes).
os << "R[" << flush << r.a_candidate << "]" << flush;
republican_party *p = r.next;
do {
os << "--next--> ";
if( p == NULL ) {
os << "NULL";
} else {
os << " R[" << p->a_candidate << "]";
p = p->next;
}
} while( p != NULL );
return os;
}
// === POLITICAL_PARTY things ===
political_party::political_party() {
rep_head = NULL;
}
ostream& operator<<(ostream& os, const political_party & p) {
os << "Party.rep_head=" << p.rep_head << flush;
if( p.rep_head != NULL ) {
os << "---> " << *p.rep_head;
}
return os;
}
int political_party::insert_republican_party(candidate_info & a_candidate)
{
republican_party * current = new republican_party(); //intiates the new node
current -> a_candidate = a_candidate; //gives the new node a perticular value:
current -> next = NULL; // declares next the next as null
cout << "insert: adding current=" << current << ", *current=" << *current << "\n";
/*A*/ if(rep_head == NULL) //no nodes exist so far
{
current -> next = rep_head; //the node after current contains the value rep_head
rep_head = current; //rep head equals the value of current
cout << "insert.A: now rep_head=" << rep_head << ", *rep_head=" << *rep_head << "\n";
}
/*B*/ else if (rep_head -> next == NULL)
{
republican_party * current2 = rep_head;
current2 = current2 -> next;
republican_party * previous = rep_head;
// previous -> a_candidate = current -> a_candidate;
// current -> a_candidate = a_candidate;
/*C*/ if(current -> a_candidate.get_rank() > previous -> a_candidate.get_rank())
{
current -> next = rep_head;
rep_head = current;
cout << "insert.C: now rep_head=" << rep_head << "\n";
}
cout << "insert.B: now rep_head=" << rep_head << "\n";
}
/*D*/ else
{
republican_party * current2 = rep_head;
/*E*/ while(current -> next != NULL)
{
current = current -> next;
cout << "insert.E: current=" << current << "\n";
if( current != NULL ) {
cout << " *current=" << current << "\n";
}
}
current -> next = current2 -> next;
current2 -> next = current;
cout << "insert.D: now rep_head=" << rep_head << "\n";
return 2;
}
}
=== end linked_list.cpp ===
3) some hints about how you could proceed
I think you are actually fairly close with the /E/ while loop.
I would encourage you to focus your efforts on the /E/ while loop, and comment out everything in the previous /A/ and /B/ if blocks. Comment it out for now because some if it you'll want to salvage and re-use after you teach your while() loop how to figure out where the new candidate_info should go.
This is a bit complicated because the "prev" link part isn't working yet, so I would suggest fixing your code so that --next--> works (singly linked list).
Then come back and modify it so that <--prev-- also works (making it a doubly linked list).
Some questions to consider about making just the "next" linking part work all the way through...
Let's say you were inserting c25 (some candidate name with a rank of 25)..
existing list: rep_head--> R[c10]--next--> R[c20]--next--> R[c30]--next--> NULL
goal list: rep_head--> R[c10]--next--> R[c20]--next--> R[c25]--next--> R[c30]--next--> NULL
Question 1) What do you need to know to say where to splice in c25 ?
Question 2) How can you modify your /E/ while loop to find that splice point?
hint: in additon to checking for hitting end-of-list, what else to you need to consider? (e.g. why do we want c25 to go after c20, but before c30 ?
Question 3) Are you looking for a single splice point? Or would it be easier to solve with both before and after splice points?
You already have effective code that creates a new R[] and puts the candidate_info in them.
The next-handling is a bit rough, so let's close by focusing on that.
Question 4) to splice in R[c25] as show, which of the R's have next values that need to change?
How can your while-loop find those values?
Lastly, some potential null values to watch out for.
If you were inserting at the beginning of the list, what value will your "before" splice point have?
If you were adding to the end of the list, what value will your "after" splice point have?
Is there any difference between inserting into an empty list vs. inserting at both the beginning and end ?
Good luck, I think with a little bit better feedback from cout << printing you'll be able to get where you want to go.

Double linked list implementation

I am having some syntax problems with a double linked list program I am writing for educational purposes. I have created a struct in my header file, and my main program seems to be alright, but implementing my functions in the .cpp file is giving me immense difficulty. I am having trouble discerning the three cases for inserting a record into the list. Specifically, allocating the memory, initializing the list head and tail, and the order of statements is confusing to me, as is passing a copy of the record to be added to my list.
My header file is as follows:
struct rec
{
char * id;
char firstname[15];
char lastname[15];
struct rec* prev;
struct rec* next;
};
int AddItem ( rec r );
int DeleteItem ( char* delid );
void PrintList ( int order );
My .cpp file, which is where the difficulty lies, is as follows:
#include <iostream>
#include "list.h"
#include <string.h>
using namespace std;
// These pointers refer to the head and tail of the list.
rec* first = NULL;
rec* last = NULL;
int AddItem( Rec r )
{
rec* newRecEntry;
rec* current = NULL;
rec* previous = NULL;
// Check for duplicate id
current = first;
while (current)
{
if( strcmp(current -> id, r.id) == 0)
{
return 0;
}
else
// Create a new node
{
newRecEntry = new Rec;
newRecEntry->id = new char[strlen(r.id)+1];
strcpy(newRecEntry->id, r.id);
strcpy(newRecEntry->firstname,r.firstname);
strcpy(newRecEntry->lastname,r.lastname);
newRecEntry->next = NULL;
newRecEntry->prev = NULL;
}
// Find the appropriate position for the node and insert accordingly
// Check to see if the list is empty
if (first == NULL)
{
first = newRecEntry;
last = newRecEntry;
}
else if ( r.lastname>last.lastname)
{
else
{
return 0;
}
/*int DeleteItem(char* ID)
I am supposed to be able to insert at the beginning, middle, and end of the list. Delete an item from the list based on the ID, and print the list in ascending or descending order based on user input, but I'd first simply like to handle the addition of items to said list.
My function definitions are as follows and also contains some errors
lists.cpp
#include <iostream>
#include "list.h"
#include <string.h>
using namespace std;
// These pointers refer to the head and tail of the list.
rec* first = NULL;
rec* last = NULL;
int AddItem( Rec r )
{
rec* newRecEntry;
rec* current = NULL;
rec* previous = NULL;
// Check for duplicate id
current = first;
while (current)
{
if( strcmp(current -> id, r.id) == 0)
{
return 0;
}
else
// Create a new node
{
newRecEntry = new Rec;
newRecEntry->id = new char[strlen(r.id)+1];
strcpy(newRecEntry->id, r.id);
strcpy(newRecEntry->firstname,r.firstname);
strcpy(newRecEntry->lastname,r.lastname);
newRecEntry->next = NULL;
newRecEntry->prev = NULL;
}
// Find the appropriate position for the node and insert accordingly
// Check to see if the list is empty
if (first == NULL)
{
first = newRecEntry;
last = newRecEntry;
}
else if ( r.lastname>last.lastname)
{
else
{
return 0;
}
/*int DeleteItem(char* ID)
{
rec
}
*/
/*void printList(int order)
{
loop
{
cout << ptr -> Id << " ";
cout << ptr -> firstname << " ";
cout << ptr -> lastname << " ";
cout << ptr -> prev << " "; // address of previous
cout << ptr << " "; // address of item
cout << ptr -> next << " "; // address of next item
}
}
Main is as follows:
#include <iostream>
#include "list.h"
#include <string.h> // <string>
using namespace std;
void main (void)
{
int choice, printorder;
char idbuffer[100];
rec r;
do
{
cout << "Enter your choice 1 Add, 2 Delete, 3 Print, 0 quit "<<endl;
cin >> choice;
switch ( choice )
{
case 1: //AddItem
cout << "\nEnter ID ";
cin >> idbuffer;
r.id = idbuffer;
cout << "\nFirst Name ";
cin >> r.firstname;
cout << "\nLast Name ";
cin >> r.lastname;
if ( AddItem ( r ) )
{
cout << "\nSuccess!\n";
}
else
{
cout << "\nItem failed to be added\n";
}
break;
case 2: //Delete
cout << "\nEnter id :";
cin >> idbuffer;
if ( DeleteItem ( idbuffer ) )
{
cout << "\nDelete OK\n";
}
else
{
cout << "\nDelete Failed for " << idbuffer;
}
break;
case 3: // Print
cout << "Enter order 0 - Ascending, 1 - Descending\n";
cin >> printorder;
PrintList (printorder);
break;
case 0: // quit
break;
default: // bad choice
break;
} // end switch
}
while ( choice != 0 );// end do while
} // end main
It may not seem like it, but even this function
int AddItem(Record entry)
{
Record* newRecordPointer;
newRecordPointer=new Record;
strcpy(newRecordPointer->firstName,entry.firstName);
strcpy(newRecordPointer->lastName,entry.lastName);
newRecordPointer->ID=new char[strlen(entry.ID)+1];
strcpy(newRecordPointer->ID, entry.ID);
return 0;
}
is trying to do too many things.
Let's write the pseudocode description of adding an item to a list:
create a new node
populate the new node with the values provided
attach the new node to the list
I've marked the verbs and nouns involved, and you can already see one of the nouns is missing from your function. You're asking AddItem to add an item to a list ... but you don't give it a list to work on.
It's also useful to write out your expectations clearly:
before AddItem is called:
it needs a list to work on
we don't have a list container class, just the records, so we have to pass a Record
let's say we want to add our new item after the Record passed in
after AddItem is called:
whatever Record we passed in, its Next should point to the new node
the new node's Previous should point to the node passed in
etc. etc. (these are the standard doubly-linked list insertion behaviours)
note for later: we haven't described how we store an empty list
if it's a circular list, an empty list will be a Record whose Next and Previous members point to itself
if it's linear, they might both be NULL instead
it could just be a NULL pointer, but then adding the first node to an empty list needs more effort
So, let's say the minimal function that could possibly work is:
void AddItem(Record *insert_after, Record value)
{
Record *new_node = CreateRecord();
CopyRecordValues(new_node, &value);
AttachAfter(insert_after, new_node);
}
Note that if we were writing real C++ the first two lines could just use the copy constructor Record *new_node = new Record(value), but it will take more changes than that to reach idiomatic C++ code from where we started.
Now, given that, can you:
implement those three functions? (CreateRecord and CopyRecordValues are already handled in your current code)
write the equivalent pseudocode for your other operations, and translate it yourself?
Try changing this:
int AddItem(Record entry);
To this:
Record* AddItem(Record entry, Record *insertion_point = NULL );
If insertion_point is NULL, you can assume that the Record is the beginning of a new list.
Now you have enough information to set Next and Previous pointers, and return the newly created node.