Nested loops causing reduced complexity efficency? - c++

I've written a simple function that removes elements from a vector (V2), based upon the values of another vector (V1):
std::vector<int> V1={6,2,3,4};
std::vector<int> V2={9,4,8,6,7};
for(int i=0; i<V1.size(); i++)
{
if(!V2.empty())
{
V2.erase(std::remove(V2.begin(),V2.end(),V1[i]),V2.end());
}
}
My challenge is that the above needs to be O(n) complexity. Currently this is O(n*m), n being V1, m being V2.
N.B. Arrays are not and cannot be sorted as the elements original index values are required.
Questions:
Am I right in saying 'V2.erase' is stopping this function from being O(n)? (Because its a nested iteration within the for loop).
Is there a way around this, by performing the erase operation outside the loop?

Why not use std::set_difference:
std::vector<int> test(
std::vector<int> v1,
std::vector<int>& v2)
{
// The algorithm we use requires the ranges to be sorted:
std::sort (v1.begin(), v1.end());
std::sort (v2.begin(), v2.end());
// our output vector: reserve space to avoid copying:
std::vector<int> v3;
v3.reserve (v2.size());
// Use std::set_difference to copy the elements from
// v2 that are not in v1 into v3:
std::set_difference (
v2.begin(), v2.end(),
v1.begin(), v1.end(),
std::back_inserter(v3));
return v3;
}
If v1.size() == n and v2.size() == m the runtime of this works out to roughly:
OK, so then how about this:
void test2(
std::vector<int> v1,
std::vector<int> v2)
{
// We can still sort this without affecting the indices
// in v2:
std::sort (v1.begin(), v1.end());
// Replace all the elements in v1 which appear in v2
// with -1:
std::replace_if (v2.begin(), v2.end(),
[&v1] (int v)
{
return std::binary_search(v1.begin(), v1.end(), v);
}, -1);
}
Not linear; estimating the complexity is left as an exercise for the OP.
A third alternative is this:
void test3(
std::vector<int> v1,
std::vector<int>& v2)
{
// We can still sort this without affecting the indices
// in v2:
std::sort (v1.begin(), v1.end());
auto ret = std::stable_partition (
v2.begin(), v2.end(),
[&v1] (int v)
{
return !std::binary_search(v1.begin(), v1.end(), v);
});
v2.erase (ret, v2.end());
}
Again, not linear, but options...

std::vector<int> V1={6,2,3,4};
std::vector<int> V2={9,4,8,6,7};
// must be a truly pathological case to have lookups of O(N)
std::unordered_set v1_hashed(v1.begin(), v1.end());
for(
size_t v2ix=v2.size()-1;
v2ix<v2.size(); // otherwise underflow past zero
v2ix--
) {
// generally, an O(1) is assumed here
if(v1_hashed.find(v2[v2ix]) != v1_hashed.end()) {
// removal of element will cost quite significant due to
// moving elements down. This is why doing the cycle in reverse
// (less elements to move down).
v2.erase(v2ix);
}
}
// O(N) searches + O(M) hashing of v1

Related

Sorting a vector of vectors while maintaining relative order

I've been trying to implement a c++ program that sorts a vector of vectors in ascending order while maintaining the current order in case of ties (e.g. if vector a is before vector b, then a stays before b). This is the code that I have now. Any tips would be appreciated!
EDIT: By the way, c is the column that you sort by
sort(v.begin(), v.end(), [=] (vector<int> &a, vector<int> &b) {
if (a[c] == b[c]) {
int d1 = find(v.begin(), v.end(), a)-v.begin();
int d2 = find(v.begin(), v.end(), b)-v.begin();
return d1 < d2;
}
return a[c] < b[c];
});
Just use std::stable_sort and provide criteria to sort by:
std::stable_sort(v.begin(), v.end(), [=] (vector<int> &a, vector<int> &b) {
return a[c] < b[c];
});
Sorts the elements in the range [first, last) in non-descending order. The order of equivalent elements is guaranteed to be preserved.
emphasis is mine

Most efficient way to find iterators of 4 maximum values in const vector in C++

I have to find 4 the biggest numbers in a const vector and return their positions. I want this code to have the best time and space complexity. My first idea is to copy this const vector into vector and bubble sort it 4 times. That gives me 4*N but i have to create a vector. Second idea is to put everything from this const vector into priority_queue. That gives me a N*log2(N) time complexity without creating another variables. The maximum of N is around 100.
Is there any other options to do it in the fastest and the least space-consuming way?
EDIT: It doesn't matter which one is the biggest, I just need to return position of this 4 items in the input vector.
O(n) solution
std::vector<int>::iterator max1 = v.begin(), max2 = v.begin(), max3 = v.begin(), max4 = v.begin();
for(std::vector<int>::iterator it = v.begin(); it != v.end(); it++) {
if((*max1) < (*it)) {
max4 = max3;
max3 = max2;
max2 = max1;
max1 = it;
} else if((*max2) < (*it)) {
max4 = max3;
max3 = max2;
max2 = it;
} else if((*max3) < (*it)) {
max4 = max3;
max3 = it;
} else if((*max4) < (*it)) {
max4 = it;
}
}
You can implement this quite easily with an extra vector, and the nth_element algorithm, which is O(n) time:
std::vector<int> const v = ...;
// create a vector of elements with original indexes
std::vector<std::pair<int,int>> res;
// populate the result vector
int k = 0;
for (int i : v)
res.push_back({i,k++});
// find the maximum 4 elements
std::nth_element(res.begin(), res.begin() + 4, res.end(),
[](auto const &a, auto const &b) { return a.first > b.first; });
Here's a demo.
Note that this solution uses O(n) extra space. If N grows large, then this might not be the right approach for finding just 4 largest elements. It's still a good approach if you want the M largest elements, where M grows like N.
Yes, use a heap of size four. Then you iterate through the vector and update the heap accordingly.
Sample code using std heap methods and finding minimum values (from here) follows.
const std::vector<int> input;
const size_t n = 4;
std::vector<int> ret(n);
auto dfirst = ret.begin(), dlast = ret.end();
// initialize heap with infinity distances
std::fill(dfirst, dlast, 100000000000); // do better here
for (auto it = input.begin(); it != input.end(); ++it)
{
if (*it < *dfirst) {
// remove max. value in heap
std::pop_heap(dfirst, dlast); // add comparator as third arg
// max element is now on position "back" and should be popped
// instead we overwrite it directly with the new element
*(dlast-1) = *it;
std::push_heap(dfirst, dlast); // add comparator as third arg
}
}
std::sort_heap(dfirst, dlast); // remove if not needed, or add comparator as third arg
return ret;
Adapt accordingly:
Use a pair of index, value in the heap to keep track of positions which you like to return
Use comparator that compares on value in the pair and establishes a desc. ordering
This is more generic than #Jugal Rawlani's solution if your number n might change/grow in the future. Otherwise his idea wins.
read the 4 first elements into a vector of 4. sort this vector so that minimum of the 4 is in index 0.
loop on remaining items of the const vector, if current value > min, replace it and re-sort the 4 element vector

find values and erase from vector [duplicate]

This question already has an answer here:
Erasing from a std::vector while doing a for each?
(1 answer)
Closed 8 years ago.
Hello suppose that there is vector with 5 elements
vector<int> V;
for (int i=0;i<5;i++){
v.push_back(i);
}
Now how can I use find if and erase to erase values bigger than 2 from the vector ?
Would you please suggest a sample code I prepared sth but I am not sure about that.
THANKS
You can use the erase-remove idiom using std::remove_if and a suitable functor. For example
V.erase(std::remove_if(std::begin(V), std::end(V),
[](int i){ return i > 2; } ),
std::end(V) );
If you're stuck with a pre-C++11 compiler, then
bool func(int i) { return i > 2; }
V.erase(std::remove_if(V.begin(), V.end(), func), V.end());
You can remove from vector based on predicate (condition):
std::vector<int>::iterator it = std::remove_if( // it shifts, doesn't
v.begin(), v.end(), // reduces the physical size
[](int i){ return i > 2; } ));
and erase unneeded values:
v.erase( it, v.end()); // this is needed as remove_if doesn't erase
// elements that don't meet condition (are not to be
// removed), it simply moves those that don't meet
// condition to the begin.
// now we reduce the physical size
Why remove_if is followed by erase
Removing is done by shifting (by means of move assignment) the
elements in the range in such a way that the elements that are not to
be removed appear in the beginning of the range. Relative order of the
elements that remain is preserved and the physical size of the
container is unchanged. Iterators pointing to an element between the
new logical end and the physical end of the range are still
dereferenceable, but the elements themselves have unspecified values
(as per MoveAssignable post-condition). A call to remove is typically
followed by a call to a container's erase method, which erases the
unspecified values and reduces the physical size of the container to
match its new logical size.
http://en.cppreference.com/w/cpp/algorithm/remove
Example
// Let's imagine your vector is
v = { 1, 2, 3, 4, 5};
^ ^
begin end
// copy( v.begin(), v.end(), ostream_iterator<int>(cout));
will print: 12345
after remove_if vector becomes:
v = { 1, 2, 3, 4, 5};
^ ^ ^
begin it end
// copy( v.begin(), v.end(), ostream_iterator<int>(cout));
// will still print a vector of size 5: 12345
// but
// copy( v.begin(), it, ostream_iterator<int>(cout));
will print: 12
after v.erase( it, v.end()); vector becomes:
v = { 1, 2};
^ ^
begin end
// copy( v.begin(), v.end(), ostream_iterator<int>(cout));
will print: 12
compiled example
C++03:
bool f(int i) { return i > 2; }
std::vector<int>::iterator it = std::remove_if( v.begin(), v.end(), f); // shifts
v.erase( it, v.end()); // reduces the physical size
This is an alternate solution without using std::remove_if just using the erase method and iterators.
typedef std::vector<int> IntVector;
IntVector v;
// fill
for (IntVector::iterator it = v.begin(); it != v.end();) {
if (*it > 2) {
it = v.erase(it);
} else {
++it;
}
}

How to find the positions of an item in a std::vector

My question is very similar to How to find an item in a std::vector? However, I want to go further, suppose the item I am searching for appears several times in the vector, and I want to obtain its positions in the vector as well. For example, the vector I have is [ 1 3 4 3 7], and the item I want to search is 3. Then the positions of the item is 1 and 3. Using the std::find function, I can only obtain its first position in the vector. Any ideas?
Just stick it in a while loop,
auto i = std::find(v.begin(), v.end(), n);
std::vector<std::size_t> result;
while(i != v.end())
{
result.push_back(std::distance(v.begin(), i));
i = std::find(i + 1, v.end(), n);
}
Use std::find successive times, and then put all the results together. Use as first of the range in which you find, the position that the previous std::find returned to you plus one.
You can use std::find multiple times:
std::vector<int> vec;
// fill the vector with numbers
std::vector<int>::iterator it = vec.begin();
while (it != vec.end())
{
it = std::find(it, vec.end(), 3);
// do something with *it
if (it != vec.end())
it++;
}
Or you can simply use std::for_each:
std::vector<int> vec;
// fill the vector with numbers
std::for_each(vec.begin(), vec.end(), [&](int i)
{
if (i == 3)
{
// do something
}
});
If you are looking for the indexes/iterators for the items, you can simply use a custom loop:
std::vector<int> vec;
// fill the vector with numbers
std::vector<int> results;
for (std::size_t i = 0; i < vec.size(); ++i)
{
if (vec[i] == 3)
{
results.push_back(i);
}
}
results will then hold all of the indexes for elements matching your criteria (in this case, ==3).

std::vector deleting duplicates and then deleting the same index in another vector

I know of how to delete duplicates of a std vector with the STL as such
vec1.erase(std::unique(vec1.begin(), vec1.end()),vec1.end());
but what if i have a different vec2 that is the same length as vec1 and i wish to delete the same indexes that was removed in vec1? Such that if index 2 , 4 and 6 were removed in vec1 , the same would be removed in vec2
Maybe you want to think of a different datastructure, perhaps vector<pair<vec1_type, vec2_type>>
But here's one way to do it (c++11)
std::vector<int> indices(vec1.size());
std::iota(indices.begin(), indices.end(), 0);
indices.erase(std::unique(indices.begin(), indices.end(),
[&](int a, int b){ return vec1[a] == vec1[b]; }),
indices.end());
auto vec1_iterator = vec1.begin();
auto vec2_iterator = vec2.begin();
for (int i : indices) {
*vec1_iterator++ = vec1[i];
*vec2_iterator++ = vec2[i];
}
vec1.erase(vec1_iterator, vec1.end());
vec2.erase(vec2_iterator, vec2.end());
Make a copy of the original vector, then walk the two vectors in search of matching items, and erase from the parallel vector when you do not find a match:
vector<int> copy(vec1);
vec1.erase(std::unique(vec1.begin(), vec1.end()),vec1.end());
vector<string> pv = // your "parallel" vector
for (int i = 0 ; i != vec1.size() ; i++) {
while (copy[i] != vec1[i]) {
copy.erase(copy.begin()+i);
pv.erase(pv.begin()+i);
}
}