Using struct with an insertion sort - c++

I'm having some trouble with an insertion sort, passing in data from a struct.
It's returning the error: |98| cannot convert 'store' to 'int' in assignment.
struct store{
char tag[5];
int cost;
long int volume;
};
void costSort(store storedEntries[], int count);
void volumeSort(store storedEntries[], int count);
int main(){
store record[100];
ifstream fin;
char choice;
int count = 0;
fin.open("stockdata.txt");
//file read in
if(fin.good())
{
while(!fin.eof())
{
fin >> record[count].tag;
fin >> record[count].cost;
fin >> record[count].volume;
count++;
}
count--;
}
cout << "Main Menu:" << endl;
cout << "c: sort data by Cost\nv: sort data by trade Volume\nq: Quit\nEnter Choice: ";
cin >> choice;
switch(choice)
{
case 'C':
case 'c': //costSort(record, count);
break;
case 'V':
case 'v': volumeSort(record, count);
break;
case 'q':
case 'Q': return 0;
break;
}
return 0;
}
void volumeSort(store record[], int count)
{
int p = 0, item = 0;
for(int i=1; i<count; i++){
cout << "test";
item = record[i];
p = (i - 1);
while(p>=0 && item < record[p]){
record[p+1] = record[p];
p--;
}
record[p+1] = item;
cout << record[i].tag << " " << record[i].volume << endl;
}
}
The insertion sort is in the function void volumeSort().
Any advice would be appreciated, i haven't had any issues up until now :S

You're comparing non-like types with no operator provided to support the comparison (and none needed if this is done correctly). Currently you're comparing int to store. What you should be comparing is two volume members of two store objects.
A simple loop that is probably closer to what you want would be something like this:
// note: count is size_t, an unsigned magnitude. only used signed
// integer types where it makes sense a negative integer will be
// plausible input.
void volumeSort(store record[], size_t count)
{
for(size_t i=1; i<count; ++i)
{
// compare current element to one below us, swapping if needed
// and stopping as soon as we reach an equal or lesser record
size_t j=i;
while(j>0 && record[j].volume < record[j-1].volume)
{
std::swap(record[j-1], record[j]);
--j;
}
}
}
Or something similar. Note the comparison of:
record[j].volume < record[j-1].volume
in the while condition. Apples to apples...
For an interesting insertion_sort that utilizes two wonderful features of the standard library, std::upper_bound and std::rotate, a rather dense version of the function can be created, looking something like this:
void insertion_sort(store record[], size_t len)
{
for (auto it = record; it != record+len; ++it)
{
std::rotate(std::upper_bound(record, it, *it,
[](const store& lhs, const store& rhs) { return lhs.volume < rhs.volume; }),
it, std::next(it));
}
}
This is considerably more efficient than it first may seem, as the search for the proper placement of the prospect element is done in O(logN) using std::upper_bound. Then std::rotate opens the hole where the element goes and it is swapped into place.
Just some food for thought. Coupled with the comparator that is going to inlined by even remedial optimization and it has a lot more punch than you may first think. Still not as kick-ass as std::sort, usually highly optimized utilizing multiple algorithms, but still good brain food.
Best of luck.

You are trying to compare a int with a store.
This will not work unless you overload < operator to compare a int and a store.
store record[];
int p = 0, item = 0;
//[...]
while (p >= 0 && item < record[p])
//Neither can you assign that
record[p + 1] = item;
Operator example:
bool operator<(const int &left, const store &s)
{
//You could also do some calculation in here,
//if you want to compare a value inside the struct
//like this:
return left < s.cost;
//Please... do it in place.
//item < record[p].cost;
}

if you want to sort by volume you should take
record[i].volume even when comparing...the types should be same when comparing values..
Similarly for other cases..

Related

How can I iterate through the last element of the vector without going out of bounds?

The expected output is 1a1b1c but I only get 1a1b If I try putting '-1' next to input.size() in the for loop but that will just ignore the bug. What I'm looking for is that I want to be able to iterate through the last member of the string without going out of bounds.
std::string input = "abc";
for (unsigned int i = 0; i < input.size(); i++){
int counter = 1;
while(input.at(i) == input.at(i+1) && i < input.size()-1){
counter++;
i++;
}
number.push_back(counter);
character.push_back(input.at(i));
}
Few points for you to consdier:
1: for (unsigned int i = 0; i < input.size(); i++) specifically i++. This is a postfix operation meaning it returns i then increments the value of i. Not as big a deal here with integers but with iterators this can get very expensive as you create a copy of the iterator each time. Prefer to say what you mean / what you actually want, which is to increment i, not get a copy of i and increment i afterwards. So prefer ++i which only increments i and does not make a copy.
2: unsigned int i = 0 Firstly its better than using an int which has a signed -> unsigned conversaion every comparison with input.size() which returns a size_t. Secondly unsigned int is not guaranteed to be big enough to hold the size of the string and requires a promotion from (probably) 32 bit -> 64 bit unsigned to compare with size_t
3: cognitive complexity, nested loops which both mutate the same invariant (in this case i) makes the code more difficult to reason about and will ultimately lead to more bugs as code evolves over time. where possible only have one place where a loop invariant is mutated.
4: As pointed out by others the while loop while(input.at(i) == input.at(i+1) && i < input.size()-1) can exceed the size of the string and using the .at member function of string will throw for an out of bounds access. This can be simply resolved with point 3 by refactoring ther nested loop into a single loop.
5: Avoid so many calls to .at, we are in complete control of the index we use to index the string so you can use operator[] safely as long as we can guarantee i will always be a valid index which in this case i think you can.
6: i < input.size() using < when its not the check you want and its much more expensive than the check you actually want which is i != input.size(). Check out this trivial comparison in compiler explorer
Thankfully the fix from shadowranger Fixes your problem completely ie: while(i < s.size()-1 && s.at(i) == s.at(i+1)) However i would like to offer an alternitive with no nested loops to show you how to avoid my points 3,4, 5 and 6 :
void do_the_thing(std::string const& s) {
std::cout << "Considering: \"" + s + "\"\n";
if(s.empty()) {
return;
}
size_t const length = s.length(); // avoiding repeated calls to length which never changes in this case
if(length == 1) {
std::cout << "1" << s[0] << "\n";
return;
}
std::vector<unsigned> number;
std::vector<char> character;
// do the stuff your example did
char last = s[0];
unsigned same_count = 1;
for(size_t ii = 1; ii != length; ++ii) {
char const cur = s[ii];
if(cur == last) {
++same_count;
} else {
number.push_back(same_count);
character.push_back(last);
last = cur;
same_count = 1;
}
}
if(*s.rbegin() == last) {
number.push_back(same_count);
character.push_back(last);
}
// print the things or use them in some way
assert(number.size() == character.size());
size_t const out_len = character.size();
for(size_t ii = 0; ii != out_len; ++ii) {
std::cout << number[ii] << character[ii];
}
std::cout << "\n";
}

My array is gettting an error because it's being defined as a singular integer

The point of this program is to output whether a series of digits (the number of digits undefined) is sorted or not (largest to smallest or smallest to largest).
I have defined my array in my function parameter, and I am trying to use a for loop to store the user's input, as long as it is above 0, in said array.
However, I am getting the error argument of type int is incompatible with parameter of type int*.
The exact error is the argument of type int is incompatible with parameter of type int*.
It is referring to line 22 and 23, these two;
isSorted(list[2000]); and
bool is = isSorted(list[2000]);.
I know this means my for loop is assigning a single value to my variable repeatedly from reading similar questions however I can not figure out how to fix this.
#include <iostream>
using namespace std;
bool isSorted(int list[]);
int main()
{
int i;
int list[2000];
int k = 0;
for (i = 0; i < 2000; i++)
{
int j;
while (j > 0)
{
cin >> j;
list[i] = j;
}
}
isSorted(list[2000]);
bool is = isSorted(list[2000]);
if (is == true)
cout << "sorted";
else
cout << "unsorted";
return 0;
}
bool isSorted(int list[])
{
int i = 0;
for (i = 0; i < 2000; i++)
{
if (list[i] > list[i + 1] || list[i] < list[i - 1])
{
return false;
}
else
return true;
}
}
I removed unused variable k.
Made 2000 parameterized (and set to 5 for testing).
In isSorted you are not allowed to return
true in the else as if your first element test would end in else you would return true immediately not testing other elements. But those later elements can be unsorted as well.
In isSorted you are not allowed to run the loop as for(i = 0; i < 2000; i++), because you add inside the for loop 1 to i and end up querying for i == 1999 list[2000], which is element number 2001 and not inside your array. This is correct instead: for (i = 0; i < 1999; i++). You also do not need to check into both directions.
You cannot call isSorted(list[2000]) as this would call is sorted with an int and not an int array as parameter.
You write int j without initializing it and then query while j > 0 before you cin << j. This is undefined behaviour, while most likely j will be zero, there is no guarantee. But most likely you never enter the while loop and never do cin
I renamed the isSorted as you just check in your example for ascending order. If you want to check for descending order you are welcome to train your programming skills and implementing this yourself.
Here is the code with the fixes:
#include <iostream>
using namespace std;
bool isSortedInAscendingOrder(int list[]);
const int size = 5; // Set this to 2000 again if you want
int main()
{
int i;
int list[size];
for (i = 0; i < size; i++)
{
int j = 0;
while(j <= 0)
{
cin >> j;
if(j <= 0)
cout << "rejected as equal or smaller zero" << endl;
}
list[i] = j;
}
if (isSortedInAscendingOrder(list))
cout << "sorted" << endl;
else
cout << "unsorted" << endl;
return 0;
}
bool isSortedInAscendingOrder(int list[])
{
for (int i = 0; i < size -1; i++)
{
if (list[i] > list[i + 1])
{
return false;
}
}
return true;
}
This is a definition of an array of 2000 integers.
int list[2000];
This is reading the 2000th entry in that array and undefined, because the highest legal index to access is 1999. Remember that the first legal index is 0.
list[2000]
So yes, from point of view of the compiler, the following only gives a single integer on top of being undefined behaviour (i.e. "evil").
isSorted(list[2000]);
You probably should change to this, in order to fix the immediate problem - and get quite close to what you probably want. It names the whole array as parameter. It will decay to a pointer to int (among other things loosing the information of size, but you hardcoded that inside the function; better change that by the way).
isSorted(list);
Delete the ignored first occurence (the one alone on a line), keep the second (the one assigning to a bool variable).
On the other hand, the logic of a your sorting check is flawed, it will often access outside the array, for indexes 0 and 1999. I.e. at the start and end of your loop. You need to loop over slightly less than the whole array and only use one of the two conditions.
I.e. do
for (i = 1; i < 2000; i++)
{
if (list[i] < list[i - 1])
/* ... */
The logic for checking ascending or descending sorting would have to be more complex. The question is not asking to fix that logic, so I stick with fixing the issues according to the original version (which did not mention two-way-sorting).
You actually did not ask about fixing the logic for that. But here is a hint:
Either use two loops, which you can break from as soon as you find a conflict, but do not return from the fuction immediatly.
Or use one loop and keep a flag of whether ascending or descending order has been broken. Then return true if either flag is still clear (or both, in case of all identical values) or return false if both are set.

Comparing vectors of different length c++

I'm developing a version of Conway's Game of Life in which a user may input their own rules for cell birth, death, and survival in the format "B##/S##". In order to implement these rules, I use basic string parsing to move the birth (B) rules and survival (S) rules into separate vectors. However, I'm running into trouble forming the survival vector.
The original cell rules are in a vector, and its contents are compared to the cell birth vector. The problem I have is that when a rule string such as "B012345678/S183" is entered, parts of the survival vector are not being added because one vector is longer than the other, causing a comparison of empty values and a segfault.
My question is: is there something I can do to fill those empty values, or is there a way to pass over those empty values?
// Extract cell birth rules from rules string
vector<char> cellBirth(vector<char> cellrules) {
int i = 0;
vector<char> birthrule;
while (cellrules[i] != 'S') {
birthrule.push_back(cellrules[i]);
++i;
}
return birthrule;
}
// Extract cell survival rules
vector<char> cellSurvive(vector<char> cellrules, vector<char> cellbirth) {
vector<char> surviverule;
long size = cellrules.size();
//cout << "Cellrule size: " << size << endl;
for (int i = 0; i < size; i++) {
if (cellrules[i] != cellbirth[i]) { //Comparison happens here
surviverule.push_back(cellrules[i]);
}
}
return surviverule;
}
cellbirth is always going to be smaller than cellrules, so your approach is wrong. There are MANY ways to skin this cat, here is one:
vector<char> cellSurvive(vector<char> cellrules) {
vector<char> surviverule;
bool pastS = false;
for (int i = 0; i < cellrules.size(); i++) {
if (pastS)
surviverule.push_back(cellrules[i]);
else if (cellrules[i] == 'S')
pastS = true;
}
return surviverule;
}

Applying heuristic to recursive backtracking

Searched for other problems -- and there are similar ones, but none that deal with this particular heuristic.
I have working code for a problem which asks to take a vector into some function, determine whether any values in that vector sum up to a given target value, and then returns whether it does or not (boolean). This is simple.
I have to use the backtracking heuristic supplied to create this function (below), which is working correctly in principle. I have to ensure that my function is not generating combinations which have been generated before (ABC is the same as BAC, for instance). How do I prevent my code from doing this? I cannot change the parameters going into the function (so the function prototype has to remain as it is below), but wrapper or helper functions are OK.
Here is the heuristic:
bool Solve(configuration conf) {
if (no more choices) // BASE CASE
return (conf is goal state);
for (all available choices) {
try one choice c;
// recursively solve after making choice
ok = Solve(conf with choice c made);
if (ok) return true;
else unmake choice c;
}
return false; // tried all choices, no soln found
}
My code:
bool CanMakeSum(Vector<int> & nums, int targetSum) {
if (nums.isEmpty()) {
cout << "you've reached the target sum" << endl;
return true;
} else {
for (int i = 0; i < nums.size(); i++) {
element = nums[i];
Vector<int> rest = nums;
cout << "list the subset: " << listSubset(rest) << endl;
rest.removeAt(i);
// try one
int new_target_sum = targetSum - element;
CanMakeSum(rest, new_target_sum);
if (new_target_sum == 0) {
return true;
} else {
new_target_sum = targetSum + element;
}
}
}
return false;
}
string listSubset(Vector<int> &partial_solution) {
string solution = " ";
for (int i = 0; i < partial_solution.size(); i++) {
solution += IntegerToString(partial_solution[i]) + " ";
}
return solution;
}
You could introduce ordering in choosing elements. For example after choosing ith element you cannot choose any element with index less than i. Change required in code is that after choosing ith element you need to remove all elements from index 0 to i.

Why this Dijkstra (graph) implementation isn't working?

I made this implementation for this problem :
http://www.spoj.pl/problems/SHOP/
#include<iostream>
#include<stdio.h>
#include<queue>
#include<conio.h>
#include<string.h>
using namespace std;
struct node
{
int x;
int y;
int time;
};
bool operator <(const node &s,const node &r)
{
if(s.time>r.time)
return true;
else return false;
}
node beg,src,dest,tempa;
int b,a,temp;
int map[25][25];
bool vis[25][25];
int X[]={1,0,-1,0};
int Y[]={0,1,0,-1};
int djs_bfs(node src,node dest)
{
int result=0;
priority_queue<node>pq;
pq.push(src);
while(!pq.empty())
{
node top = pq.top();
pq.pop();
if(top.x==dest.x && top.y==dest.y) return result;
if(top.x<0 || top.x>=a) continue;
if(top.y<0 || top.y>=b) continue;
if(vis[top.x][top.y]) continue;
vis[top.x][top.y]=true;
result+=map[top.x][top.y];
for(int i=0;i<4;i++)
{
tempa.x=top.x+X[0];
tempa.y=top.y+Y[0];
tempa.time=map[top.x+X[0]][top.y+Y[0]];
pq.push(tempa);
}
}
return -1;
}
int main()
{
memset(vis,false,sizeof(vis));
scanf("%d %d",&a,&b);
while(a != 0)
{
for(int i=0;i<a;i++)
for(int j=0;j<b;j++)
{
scanf("%c",&temp);
if(temp=='X') {map[i][j]=0;vis[i][j]=true;}
if(temp=='S') {src.x=i;src.y=j;src.time=0;}
if(temp=='D') {dest.x=i;dest.y=j;dest.time=0;}
else map[i][j]=temp-'0';
}
cout<<djs_bfs(src,dest)<<endl;
scanf("%d %d",&a,&b);
}
return 0;
getch();
}
I don't know why my code doesn't generate the right answer for the testcases.
If someone can help me improve the code, please do so :D
First of all, the graph parsing code is incorrect. The first line specifies width and height, where the width is the number of characters per line the height is the number of lines. Therefore, swap &a and &b in the first scanf, or swap the order of the nested for loops (but not both). Also, I had to add dummy scanf("%c", &dummy); calls at various places to filter out newlines. A simple dump, such as this, will help determine if your map was parsed correctly:
cout << "a=" << a << endl;
cout << "b=" << b << endl;
for (int i=0; i<a; i++) {
for(int j=0; j<b; j++) {
cout << (char)('0' + map[i][j]) << ",";
}
cout << endl;
}
Note: I also set map[i][j] to 0 for 'S' and 'D', also changing the repeated if statements into an if; else if; else chain. This makes the algorithm more robust, since you can generically add time from the source or destination.
Now, on to the algorithm itself....
Each loop of the algorithm increments result by the current map location weight. However, the algorithm is searching multiple paths simultaneously (i.e., the number of entries in the priority queue), and therefore result ends up being the sum of all processed node weights, not the current path weight. The current path weight is top.temp, and therefore you can eliminate the result variable and simply return top.temp when you reach the destination.
Also, as other answers noted, you need to use X[i] and Y[i] in your inner loop, otherwise you are only searching in one direction.
Now, because of the addition/subtraction from X[i] and Y[i], you will likely access map[][] out of range (-1 or 25). Therefore, I recommend moving the if guards to the inner for loop to guard against the out-of-range access. This also avoids filling the priority queue with illegal possibilities.
Here is my version of the algorithm, with minimal corrections, for reference:
priority_queue<node>pq;
pq.push(src);
while(!pq.empty())
{
node top = pq.top();
pq.pop();
if(top.x==dest.x && top.y==dest.y) return top.time;
if(vis[top.x][top.y]) continue;
vis[top.x][top.y]=true;
for(int i=0;i<4;i++)
{
tempa.x=top.x+X[i];
tempa.y=top.y+Y[i];
if(tempa.x<0 || tempa.x>=a) continue;
if(tempa.y<0 || tempa.y>=b) continue;
tempa.time=top.time + map[tempa.x][tempa.y];
pq.push(tempa);
}
}
return -1;
I hope this helps.
Why do you have 0 indexes?
tempa.x=top.x+X[0];
tempa.y=top.y+Y[0];
tempa.time=map[top.x+X[0]][top.y+Y[0]];
Nitpick:
bool operator <(const node &s,const node &r)
{
if(s.time>r.time)
return true;
else return false;
}
Isn't this more readable:
bool operator <(const node &s,const node &r)
{
return (s.time>r.time);
}
You're using X[0] and Y[0] instead of X[i] and Y[i] in that inner loop.
By the way, other than that your Dijkstra's is very inefficient. First, you're pushing nodes onto the queue even when they have already been visited, and secondly, you may have several of the same node in the queue, just with different times. Ultimately neither of these things effect the outcome, but you're changing the complexity.
Edit: Oh, tempa.time should equal top.time plus the edge weight, not just the edge weight.