I have two containers std::set and std::vector and my task is to return elements from std::vector which are present in std::set. What is the most effective way to achieve it?
Simple solution:
Iterate through elements of vector and call set.find on each and then vector.erase if not found.
How about just looking for every element? If your vector is not sorted, then there's no way around n log(n)
#include <algorithm>
std::vector<int> result;
for(auto&& el: myvector) {
auto it_found = myset.find(el);
if(it != myset.end())
result.push_back(*it_found);
}
Now result has all the elements that are in both.
PS: Haven't compiled the code, there might be slight errors.
You can use more STL :)
#include <algorithm>
#include <set>
#include <vector>
#include <iostream>
#include <iterator>
int main() {
std::vector<int> v {5, 4, 3, 2, 1};
std::set<int> s {1, 3, 5};
v.erase(std::remove_if(v.begin(), v.end(),
[&s](int a) { return s.find(a) == s.end(); }),
v.end());
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
}
Shortest way is probably to use std::set_intersection. But you should sort a vector to make it work:
int main()
{
std::set<int> s{1,2,3,4,5,6,7,8};
std::vector<int> v{7,5,10,9};
std::sort(v.begin(), v.end()); // should not bother you if vector is small
std::vector<int> intersection;
std::set_intersection(s.begin(), s.end(), v.begin(), v.end(), std::back_inserter(intersection));
for(int n : intersection)
std::cout << n << ' ';
}
Prints: 5 7
Depending on relative size of set and vector, remove_if may be the right thing...
#include <set>
#include <vector>
#include <iostream>
#include <algorithm>
int main()
{
std::set<int> s{1,2,3,4,5,6,7,8};
std::vector<int> v{7,5,10,9};
v.erase(std::remove_if(v.begin(), v.end(), [&](int e){return s.count(e) == 0;}), v.end());
for(int n : v)
std::cout << n << ' ';
}
If you look for the most cpu-effective way of doing this in terms of complexity, have additional memory and a good hash function, you can do it in O(n + m):
std::vector<int> v;
std::set<int> s;
std::unordered_set<int> us{s.cbegin(), s.cend(), s.size()};
v.erase(
std::remove_if(v.begin(), v.end(),
[&us] (const int entry) { return us.find(entry) == us.cend(); }),
v.end());
Explanation: you iterate over your set once (O(m)) to prepare unordered_set. Then you iterate through your vector once (O(n)), performing unordered_set::find (0(1)) each step. It gives you the resulting complexity of O(n+m).
Also, the size of unordered_set being equal to the size of set and a good hash function helps to reduce constant part in complexity of std::unordered_set::find.
See live example.
Bear in mind, however, that lower complexity doesn't necessarily mean faster execution in particular circumstances (for example, because of extra allocations).
Related
Let's say I have a set of vector<int>:
std::vector<int> a = {2,3,8,4,9,0,6,10,5,7,1};
std::vector<int> b = {6,10,8,2,4,0};
std::vector<int> c = {0,1,2,4,5,8};
I want to create a new vector, in such a way that only elements which are common to all input vectors are input into the new vector as follows:
std::vector<int> abc = {8,2,0,8}; // possible output, order doesn't matter
I have seen many questions asking for how to remove duplicates, but I wish to retain only duplicates.
Is there an existing efficient STL algorithm or construct that will do this for me, or do I need to write my own?
As mentioned, you can use an algorithm set_intersection to do this:
But you will also have to sort the vectors first
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> a = {0,1,2,3,4,5,6,7,8,9,10};
std::vector<int> b = {0,2,4,6,8,10};
std::vector<int> c = {0,1,2,4,5,8};
std::vector<int> temp;
std::vector<int> abc;
std::sort(a.begin(), a.end());
std::sort(b.begin(), b.end());
std::sort(c.begin(), c.end());
std::set_intersection(a.begin(), a.end(),
b.begin(), b.end(),
std::back_inserter(temp));
std::set_intersection(temp.begin(), temp.end(),
c.begin(), c.end(),
std::back_inserter(abc));
for(int n : abc)
std::cout << n << ' ';
}
LIVE DEMO
I'm new to C++, I came from Swift background, and I'm thankful for your help in advance.
I have a vector that contains int values. Some of them are repeated.
My task here is to get the largest repeated value from the vector.
Example:
std::vector<int> myVector;
myVector.push_back(1);
myVector.push_back(8);
myVector.push_back(4);
myVector.push_back(4);
I need a function that returns 4 because it's the largest duplicate int in the vector.
Thanks again, and please, if you have any question, please ask it instead of downvoting.
Solution based only on std algorithms:
Sort the list using std::sort.
Iterate backwards over its elements and detect the first one that is equal to its predecessor using std::adjacent_find and reverse iterators.
I doubt it gets simpler than this. For your enjoyment:
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{1,2,3,3,4,4,4,5,6,7,7,8};
std::sort(v.begin(), v.end());
auto result = std::adjacent_find(v.rbegin(), v.rend());
if(result == v.rend())
std::cout << "No duplicate elements found.";
else
std::cout << "Largest non-unique element: " << *result;
}
Live example on Coliru.
Properties:
Zero space overhead if the list can be sorted in place.
Complexity: O(N log(N)) less than comparisons and K equality comparisons where K is equal to the number of unique elements larger than the one you're after.
Lines of code making up the algorithm: 2
You could use a map, as someone who commented above, and then place the number of appearances of each element of the vector. Afterwards, you take the maximum element via a custom comparator.
Ideone
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
int largestRepeatingNumber(const std::vector<int> & vec)
{
std::map<int, int> counter;
std::for_each(std::begin(vec), std::end(vec), [&counter] (int elem) {
counter.find(elem) == counter.end() ? counter[elem] = 1 : ++counter[elem]; });
return std::max_element(std::begin(counter), std::end(counter), [] (auto lhs, auto rhs) {
if (lhs.second == rhs.second)
return lhs.first < rhs.first;
return lhs.second < rhs.second;
})->first;
}
int main()
{
std::vector<int> myVector;
myVector.push_back(1);
myVector.push_back(8);
myVector.push_back(4);
myVector.push_back(4);
myVector.push_back(3);
myVector.push_back(3);
std::cout << largestRepeatingNumber(myVector);
return 0;
}
I have used the lower bound and upper bound
so strategy is
1)Sort the original vector (so that can use unique function on it)
2)find unique (copy the unique values to a vector so that we can use it to find the values in original vector , not extra search)
3)Find the lower and upper bound value with max distance
for example 1 4 4 8
4 will have max distance
5)Store in map using the count as index (map is ordered so max duplicate value will be at the end )
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
int main()
{
std::vector<int> myVector,myvec,counter;
map<int,int> maxdupe;
myVector.push_back(1);
myVector.push_back(8);
myVector.push_back(4);
myVector.push_back(4);
sort(myVector.begin(),myVector.end());
std::unique_copy(myVector.begin(), myVector.end(), std::back_inserter(myvec));
std::copy(myvec.begin(), myvec.end(), std::ostream_iterator<int>(std::cout, " "));
cout<<endl;
for(auto i = myvec.begin(); i!=myvec.end();i++)
{
auto lower = std::lower_bound(myVector.begin(), myVector.end(), *i);
auto upper = std::upper_bound(myVector.begin(), myVector.end(), *i);
maxdupe[upper - lower] = *i;
}
for(auto i= maxdupe.begin();i!= maxdupe.end();i++)
{
cout<<i->first<<i->second<<endl;
}
return 0;
}
Output
1 4 8
18
24
Program ended with exit code: 0
I have a vector of pairs of integers that looks somehow like that:
(0, 1)
(1, 9)
(2, 3)
(6, 1)
(4, 0)
I want to extract unique elements from there, so that the result looks as follows:
0, 1, 9, 2, 3, 6, 4
(basically just all numbers without duplicates)
At the moment I'm doing it like that:
std::vector<int> getElements(std::vector<std::pair<int, int>> S) {
std::vector<int> V;
for (std::vector<std::pair<int, int>>::iterator i = S.begin(); i != S.end(); i++) {
if (std::find(V.begin(), V.end(), i->first) == V.end()) {
V.push_back(i->first);
}
if (std::find(V.begin(), V.end(), i->second) == V.end()) {
V.push_back(i->second);
}
}
return V;
}
Is there any more efficient way to do it?
Your current solution is O(n^2). You can reduce the linear-scan for already seen elements to an amortized O(1) by using std::unordered_set to store the already seen numbers; This will improve your runtime to O(n).
Here is an improved algorithm:
std::vector<int> getElements(std::vector<std::pair<int, int>> S) {
std::unordered_set<int> ss;
std::for_each(S.begin(), S.end(), [&ss](const auto& p) {
ss.insert(p.first);
ss.insert(p.second);
});
return std::vector<int>(ss.begin(), ss.end());
}
See an example Live On Coliru
Is there any more efficient way to do it?
Yes, there is. std::find has O(n) complexity for vector, so repeating it for each element gives you O(n*n) complexity.
A simple alternative is to add every element into std::set. The complexity of building the set is O(n log n).
Not measured, but I think it is faster...
#include <iostream>
#include <algorithm>
#include <vector>
std::vector<int> getElements(std::vector<std::pair<int, int>>& S) {
std::vector<int> V;
V.reserve(2*S.size());
for (const auto& i : S) {
V.push_back(i.first);
V.push_back(i.second);
}
std::sort(V.begin(), V.end());
V.erase(std::unique(V.begin(), V.end()), V.end());
return V;
}
int main()
{
std::vector<std::pair<int, int>> v{{0, 1},{1, 9},{2, 3},{6, 1},{4, 0}};
for(const auto& i : getElements(v))
std::cout << i << ' ';
std::cout << '\n';
}
I am trying to find a way to copy elements of a vector to another vector.
int main()
{
std::vector<int> aVec{0,1,2,3,4};
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin()+3;
std::vector<int> aVecNew;
// How to insert elements ranging from itBegin till itEnd(including itEnd) to
// the vector aVecNew
return 0;
}
Most of the insert methods appear not to include itEnd. Is there a clean way to do this?
EDIT: If I am not sure ++itEnd is the end iterator or not. In such case it would fail. Is there any safer way without the mess ?
You can use std::copy from <algorithms> and std::back_inserter from <iterator>:
int main(int a, char**){
std::vector<int> aVec{ 0, 1, 2, 3, 4 };
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin() + 3;
std::vector<int> aVecNew;
std::copy(itBegin, itEnd, std::back_inserter(aVecNew));
return 0;
}
PS: Also, as it was mentioned in the comment, this code copies excluding itEnd. If you want to copy elements including itEnd, just increment its value by 1:
int main(int a, char**){
std::vector<int> aVec{ 0, 1, 2, 3, 4 };
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin() + 3;
std::vector<int> aVecNew;
std::copy(itBegin, ++itEnd, std::back_inserter(aVecNew));
return 0;
}
Some documentation on back_inserter and copy.
Beyond the ways already mentioned, std::vector has a constructor that will do exactly what you want (take the elements from range given begin and end iterators).
std::vector<int> aVecNew(itBegin, ++itEnd);
In the general case - the target vector does already exist - the copy onto a back_insert_iterator until just before ++itEnd is the right way, but in your case,
std::vector<int> aVecNew(itBegin, ++itEnd);
is the appropriate measure. std::vector has ctor #(4) for that; no reason to first create and then populate the vector here.
#include <vector>
#include <iostream>
#include <iterator> // ostream_iterator
#include <algorithm> // copy
int main()
{
std::vector<int> aVec{0,1,2,3,4}; // this is c++ of 2011 or later...
// ... thus this
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin() + 3;
// (if used) would at least be a case for "auto"
std::vector<int> aVecNew(itBegin, ++itEnd);
std::copy(aVecNew.begin(), aVecNew.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
return EXIT_SUCCESS;
}
output:
0 1 2 3
live at Coliru's
#include <iostream>
#include <list>
#include <algorithm>
int main()
{
std::list<int> numbers = {1, 3, 0, -8, 5, 3, 1};
auto positionInMiddle = std::find(numbers.begin(), numbers.end(), -8);
std::sort(positionInMiddle, numbers.end()); // This doesn't work,
// Needs random access iterator.
numbers.sort(); // This sorts the entire list.
for (int i : numbers)
std::cout << i << std::endl;
return 0;
}
Is there some trick I can use? For example if there was a method that swaps two nodes in the list, then I could use mergesort.
Lists have constant time insertions and removal, so making a temporary list with splice to sort is quite fast insertion wise (still linear in copying elements, unfortunately):
#include <iostream>
#include <list>
#include <algorithm>
int main()
{
std::list<int> numbers = {1, 3, 0, -8, 5, 3, 1};
auto positionInMiddle = std::find(numbers.begin(), numbers.end(), -8);
std::list<int> temp;
temp.splice(temp.end(), numbers, positionInMiddle, numbers.end());
temp.sort();
numbers.splice(numbers.end(), temp, temp.begin(), temp.end());
for (int i : numbers)
std::cout << i << std::endl;
return 0;
}
You can't use std::sort to sort std::list, because std::sort requires iterators to be random access, and std::list iterators are only bidirectional.
However, std::list has a member function sort that will sort it: