So, I have a problem with this sorting function, that I wrote to sort struct.
My initial thought was to have a while cycle until there is no changes happening, and inside have for cycle that goes through an array[10], comparing two elements that are next to each other. If next element is larger than previous one, they are exchanged, and iterator thrown back to zero.
Everything kind of works, except for the first element, which is not highest. From second to last, everything is fine.
Any pointers, to where I have made a mistake? Code seems fine to me...
I know I could have used <algorithm>, but I am supposed to write my own function for this.
void izvadaPecRez(Pari masivs[])
{
Pari temp;
bool change;
int i;
while(change!=false)
{
for(i=0;i<10;i++)
{
if(masivs[i+1].kopejais>masivs[i].kopejais)
{
temp=masivs[i];
masivs[i]=masivs[i+1];
masivs[i+1]=temp;
change=true;
i=0;
}
else
{
change=false;
}
}
}
for(i=0;i<10;i++)
printone(masivs, i);
}
i=0 is going to happen right before the increment in the for loop runs, so the effect of the statement will be that i==1 in the next loop. The easiest way to get your intended behavior is to just break from the for-loop after you swap the elements (don't worry, it'll only break the for-loop, not the while-loop). You should also just set change=false at the top of the while-loop, rather than setting it whenever you don't swap elements.
Use this code instead for sorting (still untested):
#include<array>
#include<functional>
#include<algorithm>
std::array<Pari,100> masivs;
auto compare=[](const Pari& a, const Pari& b) {return a.kopejais<b.kopejais;};
std::sort(masivs.begin(), masivs.end(), compare);
Here, instead of a normal array, std::array is used. Next, a custom comparison lambda-function is defined and passed to std::sort.
I don't know whether it does exactly the same as your code, but at least will sort your classes according to the entry kopejais.
EDIT: Here is a version of your code which should work faster than the one in the accepted answer:
void izvadaPecRez(Pari masivs[])
{
bool change=true;
while(change==true)
{
change=false;
for(int i=0;i<10;i++)
{
if(masivs[i+1].kopejais > masivs[i].kopejais)
{
std::swap(masivs[i], masivs[i+1]);
change=true;
}
}
};
for(i=0;i<10;i++)
printone(masivs, i);
}
The reason is that you are not repeatedly looping over already ordered parts of the array, which is done by using break after the swap.
Related
I have to write a function that erases an element out of the list if it's bigger than the previous element.(The previous element is the one which points to the next element before deletion)
I think I've basically finished it but I don't know why it doesn't erase 5 out of my list.
void deleteBigger(list<int> s){
list<int>::iterator test;
for(test = s.begin(); test != s.end(); test++){
int sk1=*test;
cout<<sk1<<endl;
test--;
int sk2=*test;
cout<<sk2<<endl;
if(sk1>sk2){
cout<<"Im here!\n";
s.erase(test);
}
test++;
}
}
It doesn't give an error or anything it just doesn't erase. I tried to test the erase method in the main function of the program, and there it worked fine.
There are three problems with your code:
Your list is passed by value, not reference. So you are changing a copy of your list and it doesn't alter the original container
You try to remove an element from a list while iterating it. Edit: As #Remy Lebeau mentioned in the comments, to be more precise it's a problem because you don't update the iterator properly, but not a problem on its own. Be advised, that when you remove an element from a list, the iterator which pointed to the erased element is considered invalidated.
Upon the first iteration, you decremented the iterator out of the container's bounds
Summing it up, what you might want to have here looks something like this:
void deleteBigger(std::list<int> &s) {
using namespace std;
if (s.empty()) {
return;
}
for(auto test = next(s.cbegin()); test != s.cend(); ++test){
while ((*test > *prev(test)) && (test != s.cend())) {
test = s.erase(test);
}
}
}
I've copied your code and it doesn't work. The problem is your iterator pointer "test". You can't degree a pointer at the begin. The only thing you can do is use a control.
Note: it's wrong decrement a pointer because you are decrementing of (32 bits) the index of memory. In this case there is overriding -- operator that saves your program but be careful next times
Control your program. It's important use a debugger editor where you can stop the program at certain point and control the value of the variables
I have a C++11 list of complex elements that are defined by a structure node_info. A node_info element, in particular, contains a field time and is inserted into the list in an ordered fashion according to its time field value. That is, the list contains various node_info elements that are time ordered. I want to remove from this list all the nodes that verify some specific condition specified by coincidence_detect, which I am currently implementing as a predicate for a remove_if operation.
Since my list can be very large (order of 100k -- 10M elements), and for the way I am building my list this coincidence_detect condition is only verified by few (thousands) elements closer to the "lower" end of the list -- that is the one that contains elements whose time value is less than some t_xv, I thought that to improve speed of my code I don't need to run remove_if through the whole list, but just restrict it to all those elements in the list whose time < t_xv.
remove_if() though does not seem however to allow the user to control up to which point I can iterate through the list.
My current code.
The list elements:
struct node_info {
char *type = "x";
int ID = -1;
double time = 0.0;
bool spk = true;
};
The predicate/condition for remove_if:
// Remove all events occurring at t_event
class coincident_events {
double t_event; // Event time
bool spk; // Spike condition
public:
coincident_events(double time,bool spk_) : t_event(time), spk(spk_){}
bool operator()(node_info node_event){
return ((node_event.time==t_event)&&(node_event.spk==spk)&&(strcmp(node_event.type,"x")!=0));
}
};
The actual removing from the list:
void remove_from_list(double t_event, bool spk_){
// Remove all events occurring at t_event
coincident_events coincidence(t_event,spk_);
event_heap.remove_if(coincidence);
}
Pseudo main:
int main(){
// My list
std::list<node_info> event_heap;
...
// Populate list with elements with random time values, yet ordered in ascending order
...
remove_from_list(0.5, true);
return 1;
}
It seems that remove_if may not be ideal in this context. Should I consider instead instantiating an iterator and run an explicit for cycle as suggested for example in this post?
It seems that remove_if may not be ideal in this context. Should I consider instead instantiating an iterator and run an explicit for loop?
Yes and yes. Don't fight to use code that is preventing you from reaching your goals. Keep it simple. Loops are nothing to be ashamed of in C++.
First thing, comparing double exactly is not a good idea as you are subject to floating point errors.
You could always search the point up to where you want to do a search using lower_bound (I assume you list is properly sorted).
The you could use free function algorithm std::remove_if followed by std::erase to remove items between the iterator returned by remove_if and the one returned by lower_bound.
However, doing that you would do multiple passes in the data and you would move nodes so it would affect performance.
See also: https://en.cppreference.com/w/cpp/algorithm/remove
So in the end, it is probably preferable to do you own loop on the whole container and for each each check if it need to be removed. If not, then check if you should break out of the loop.
for (auto it = event_heap.begin(); it != event_heap.end(); )
{
if (coincidence(*it))
{
auto itErase = it;
++it;
event_heap.erase(itErase)
}
else if (it->time < t_xv)
{
++it;
}
else
{
break;
}
}
As you can see, code can easily become quite long for something that should be simple. Thus, if you need to do that kind of algorithm often, consider writing you own generic algorithm.
Also, in practice you might not need to do a complete search for the end using the first solution if you process you data in increasing time order.
Finally, you might consider using an std::set instead. It could lead to simpler and more optimized code.
Thanks. I used your comments and came up with this solution, which seemingly increases speed by a factor of 5-to-10.
void remove_from_list(double t_event,bool spk_){
coincident_events coincidence(t_event,spk_);
for(auto it=event_heap.begin();it!=event_heap.end();){
if(t_event>=it->time){
if(coincidence(*it)) {
it = event_heap.erase(it);
}
else
++it;
}
else
break;
}
}
The idea to make erase return it (as already ++it) was suggested by this other post. Note that in this implementation I am actually erasing all list elements up to t_event value (meaning, I pass whatever I want for t_xv).
I am using vector::erase() function to delete the the first element in the vector until it is empty but my compiler is giving me an "Invalid Pointer Operation" error.
I have a vector of class-defined objects, foo, called bar.
I am deleting elements one by one like so:
for(int i = 0; i < bar.size(); i++){
if(!bar.empty()){
bar.erase(bar.begin());
}
}
When I run my program, it only completes one iteration (no matter the size) and breaks on the second.
Specifically, it breaks on the STL function _Destroy
template<class _TyDtor> inline
void _Destroy(_TyDtor _FARQ *_Ptr)
{ // destroy object at _Ptr
_DESTRUCTOR(_TyDtor, _Ptr);
}
*note I know there is a clear function that would do this more neatly but this is just a simplified example of something else that I am trying to do
As always when modifying a range while traversing the range, you cannot unconditionally increment the loop counter.
Consider the simple example of a two-element set. When i is 0, you remove the first element and increment i. Now i is 1 and bar.size() is 1, and the loop exits, failing to delete the second element.
The standard solution is to make the increment conditional on not modifying the container:
for (int i = 0; i < bar.size(); /* no increment here */) {
if (!bar.empty()) {
bar.erase(bar.begin());
} else {
++i;
}
}
When you do modify the container by erasing an element, i stays constant but the range moves one element closer to it. Also incrementing i would be double-counting.
(This is an extremely common mistake, though usually it's expressed in terms of iterators, e.g. here or here or here.)
The problem is the condition part of the loop bar.size() which keeps changing in each iteration i.e., decreasing in this example by 1.
It is difficult to say what exactly you are trying to achieve within the loop but if you want to execute the loop bar-size () times then use the follow logic:
const size_t vecSize = bar.size ()
for(int i = 0; i < vecSize ; i++)
{ ..... }
Otherwise if you want to execute the loop till bar-size () is empty, then use the while loop as follows:
while (bar.size > 0)
{ ..... }
I seen weird for loop syntax in C++. Please see following program.
#include <iostream>
using namespace std;
int main()
{
int num, count = 0;
int array[]= {1,1,2,3,4,1,3,2,9,8,7};
cout<<"Which number would you like to know about how many times it appeared?"<<endl;
cin>>num;
for (int i:array)
{
if (i == num)
++count;
}
cout<<"Number "<<num<<" appeared "<<count<<" times in the array"<<endl;
return 0;
}
It is successfully run on GCC Linux platform.
Reference link Here.
So, My question is, Is it the correct way to use for loop in C++?
Also, Is for (int i:array) equivalent to for ( int i:array ; ; )?
Sorry for my poor english. Thanks in advance.
There are now (since C++11) two distinct syntaxes for for-loops, the old C-style:
for (initialization; test; increment)
and the new
for (declaration: container)
In the new syntax, the declaration declares a variable which is successively given the value of each element of the container. Common values of "declaration" are auto val, const auto val, auto& val, and const auto& val, depending on whether you want a reference to the value in the container or a copy, and whether you want the value constant or not.
Both syntaxes are correct. It rather depends what you want to do in the loop. My preference is to use the range based for unless I am going to need the loop counter or iterator ... in which case I fall back on the old style for.
See http://en.cppreference.com/w/cpp/language/range-for for the gory details of the specification (and what is meant by "container").
The syntax for (int i:array) iterates through each element in the array, compared to for (int i = 0; i<sizeof(array); i++) which creates a counter that automatically increments on each iteration of the loop. The counter can then be used to access elements of the array with array[i]
As for which one you'd use, it depends on what you want to do. In your example there isn't a need to keep track of which iteration of the loop you are on, so the former will work fine. If you wanted to, say, print the iteration number each time then you would use the latter.
P.S. your English is perfect :)
While trying to make this merge-sort algorithm with recursive calls, i ended up getting an exception of std::out_of_range.
I don't know much about debugging and finding causes of errors. Below i will post the code(not full, only some parts) and a sourcefile containing the same code(full version, of course).
I will gladly listen to suggestions, even if they don't provide any help against this error, so feel free to comment this code and make jokes of me :)
https://docs.google.com/file/d/0ByVN9ccAyFY2dkVLN0ZlTWVHZG8/edit
Main func
int main()
{
vector<int> original; //input vector
input (&original); //write input to vector<int> original
divide(&original); //pass the vector
for(unsigned int i=0;i<original.size();i++)//output the results
cout<<original.at(i);
}
Input func
int input(vector<int> *inVec) //read all input until non-integer
{
int tmp;
while (cin>>tmp)
inVec->push_back(tmp);
for(unsigned int i=0;i<inVec->size();i++)
cout<<inVec->at(i)<<endl;
}
Divide
int divide(vector<int> *original)
{
int origL=original->size();
if(origL>1)
{
vector<int> first; //vectors for holding 2 halfs of "original"
vector<int> second; //
first.assign(original->begin(),original->begin()+origL/2);//1st half of "original"
second.assign(original->begin()+origL/2+1,original->end());//2nd half
divide(&first); //recursive call until "first" and
divide(&second); //"second" include only one number
merge(&first,&second,original);//merge first and second back into one vector
}
}
Merge func
int merge(vector<int> *A,vector<int> *B,vector<int> *original)
{
//clear the original vector. we will use it to store sorted results.
original->erase(original->begin(),original->end());
unsigned int i=0,j=0;
//out the smallest number from A and B into
//original[0] and so on. This makes it a
//sorting algorithm.
for(i=0;i<A->size();i++)
{
if(j<B->size())
if(A->at(i)<=B->at(j))
original->push_back(A->at(i));
else{
original->push_back(B->at(j));
i--;j++;}
}
//the ABOVE loop scans whole vector A or B.
//if there are still uncopied elements in
//the other vector, then we check for them and
//push them into original.
if(j<B->size())
for(i=j;i<B->size();i++)
original->push_back(B->at(i));
if(i<A->size())
for(j=i;j<A->size();j++)
original->push_back(A->at(j));
return EXIT_SUCCESS;
}
EDIT1:
Made the changes to MERGE, so now there are no runtime errors. However, output is not right. If someone notices what could cause the problem, please kindly tell me. Meanwhile I am going to try to find it myself.
What will happen when you run out of elements in B in the function merge? OOR. Try a test case when all the elements in B are smaller than the ones in A and only call merge to see what I mean.
Also this is c++ please use reference in preference to pointers.
There exists a bug in your merge function, you should test if either vector B or vector A is empty, or the access to the vectors will cause the exception.
Next part is incorrect:
first.assign(original->begin(),original->begin()+origL/2);
second.assign(original->begin()+origL/2+1,original->end());
F.e. when you have origL==2, first vector will be { original[0] }, and second vector will be empty. You must reimplement filler for second vector:
second.assign(original->begin()+origL/2,original->end())