How to check whether two vectors have common elements without repeat? - c++

Here is my function code fragment that checks whether two arrays have distinctly common elements and returns a new vector with those elements. However there is a run time error, I do not see it and cannot fix it. I learned how to use the unique function from here:
Checking for duplicates in a vector
vector<int> distinctCommonElements(vector<int>&x, vector<int>&y)
{
vector<int>z;
for(vector<int>::iterator itx=x.begin(); itx<x.end(); itx++)
{
int ctr=0;
int memory=0;
for(vector<int>::iterator ity=y.begin(); ity<=y.end(); ity++)
{
if(*itx==*ity&&ctr==0)
{
ctr++;
z.push_back(*itx);
}
}
}
//inspiration from stack overflow and the c++ reference for unique algorithm
sort(z.begin(),z.end()); //sort due to the implementation of the unique algorithm
z.erase(unique(z.begin(),z.end()), z.end()); //unique checks adjacent elements, hence sort first,
//hence answer always sorted
return z;
}

Related

The code is erasing just one dublicates and not another. Why so?

enter image description here
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int>v;
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
for(int i=1;i<nums1.size();i++)
{
if(nums1[i]==nums1[i-1])
nums1.erase(nums1.begin()+i);
}
for(int i=1;i<nums2.size();i++)
{
if(nums2[i]==nums2[i-1])
nums2.erase(nums2.begin()+i);
}
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
if(nums1[i]==nums2[j])
{
v.push_back(nums1[i]);
}
}
}
return v;
}
};
While I am comparing in both the vectors i am trying to erase dublicate elements. But eventually end up in erasing just one dublicate element and not another.The first three lines denotes vector after sorting and last denotes removing of dublicates
In the first 2 loops, you are erasing an element in a std::vector, from within a loop traversing its elements. This is a bad idea because the std::vector's size and element indices will change. A similar issue arrised when you use iterators which are invalidated by using erase. See: std::vector::erase
You can see here info how to do it correctly: What's the most efficient way to erase duplicates and sort a vector?
intersection does not access any class members (because there aren't any). It could be made into a free function, or a static method of class Solution (if it makes sense to put it in there).
Better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?
Please avoid posting images of used data. Instead copy-paste it as text so that it's easy for us to test.

Constraining remove_if on only part of a C++ list

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).

custom sorting function with struct

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.

merge sort c exception OOR UPD::output problems

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())

Iterate and erase elments from std::set

I have a std::set and I need to erase similar adjacent elements:
DnaSet::const_iterator next = dna_list.begin();
DnaSet::const_iterator actual = next;
++next;
while(next != dna_list.end()) // cycle over pairs, dna_list is the set
{
if (similar(*actual, *next))
{
Dna dna_temp(*actual); // copy constructor
dna_list.erase(actual); // erase the old one
do
{
dna_temp.mutate(); // change dna_temp
} while(!dna_list.insert(dna_temp).second); // insert dna_temp
}
++actual;
++next;
}
Sometimes the program can't exit from the main loop. I think the problem happens when I erase the last element in the dna_list. What's the correct way to do this task?
Use actual = next rather than ++actual.
Once you erase actual, it is an invalid iterator, so ++actual will behave strangely. next should remain intact, so assigning actual to next should work.
Your best option is to create a comparison functor that uses the similar() predicate. Then all you need to do is construct the set with that comparison functor and you're done. The set itself will see two similar elements as identical and will only let the first one in.
struct lt_different {
bool operator()(int a, int b) {
return a < b && !similar(a, b);
}
private:
bool similar(int a, int b)
{
// TODO:when are two elements similar?
const int EPSILON = 2;
return abs(a - b) < EPSILON;
}
};
// ...
set<int> o; // fill this set with your data
// copy your data to a new set that rejects similar elements
set<int,lt_different> s(o.begin(), o.end(), lt_different());
You can work with set s: insert elements, remove elements, modify elements -- and the set itself will make sure no two similar elements exist in the set.
That said, you can also write an algorithm yourself, if only for an alternative choice. Take a look at std::adjacent_find() from <algorithm>. It will find the first occurrence of two consecutive identical elements; hold on to that position. With that found, find the first element from that point that is different from these elements. You end up with two iterators that denote a range of consecutive, similar elements. You can use the set's erase() method to remove them, as it has an overload that takes two iterators.
Lather, rinse, repeat for the entire set.