Lets say I have a vector of numbers and I want to create two vectors to separate the numbers on odd and even ones. Easily with a simple for:
std::vector<int> odds;
std::vector<int> evens;
std::vector<int> numbers;
for (int number : numbers) {
if isOdd(number)
odds.push_back(number);
else
evens.push_back(number);
}
I would like to know if there any kind of inserter_iterator that can manage to do this so i can write something like
std::vector<int> odds;
std::vector<int> evens;
std::vector<int> numbers;
auto pred = [](int i) { return isOdd(i) ? True : False;};
auto identity = [](int i) {return i;};
std::transform(std::begin(numbers), std::end(numbers), some_inserter(odd, evens, pred), identity);
This is just out of curiosity, I'm trying to learn how algorithms and iterators work. A solution based on ranges is also valid.
You can use std::partition_copy algorithm, as follows https://en.cppreference.com/w/cpp/algorithm/partition_copy
int main() {
std::vector vec = {1, 2, 3, 4, 5, 6};//the vector to be partitioned
std::vector<int> odds(vec.size());//will hold odds (to have the sam size as the original (the maximum case) )
std::vector<int> evens(vec.size());//will hold evens
auto [oddsEnd, evensEnd] =
std::partition_copy(vec.begin(), vec.end(), odds.begin(), evens.begin(), [](int i){return i%2!=0;});//will copy at the front (won't insert at the back)
odds.erase(oddsEnd, odds.end());//to erase the undesired excess
evens.erase(evensEnd, evens.end());//to erase the undesired excess
}
OR use std::back_inserter https://en.cppreference.com/w/cpp/iterator/back_inserter (See the comments)
int main() {
std::vector vec = {1, 2, 3, 4, 5, 6};//the vector to be partitioned
std::vector<int> odds;//will hold odds
std::vector<int> evens;//will hold evens
std::partition_copy(vec.begin(), vec.end(), std::back_inserter(odds), std::back_inserter(evens), [](int i){return i%2!=0;});//will insert
}
You can simply use for_each which can be used almost like you described:
std::vector<int> odds;
std::vector<int> evens;
std::vector<int> numbers;
auto pred = [](int i) { return i % 2;};
auto identity = [](int i) {return i;};
std::for_each(std::begin(numbers), std::end(numbers), [&](int number) mutable {
int iden = identity(number);
if (pred(iden)) odds.push_back(iden);
else evens.push_back(iden);
});
// Or with range-based loop:
for (auto &elem : numbers) {
if (pred(iden)) odds.push_back(iden);
else evens.push_back(iden);
}
Related
Say I have a std::vector<int> with a simple operation to copy those elements which are even:
#include <vector>
int main()
{
std::vector<int> v = {1, 2, 3, 4, 5, 6};
std::vector<int> even;
std::copy_if(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()), std::back_inserter(even), [](int i){return i%2 == 0;});
return 0;
}
My question how can I combine the above with any other method to remove the elements from vector v which was copied to vector even
I wouldn't recommend trying to use std::copy_if here. Instead use std::stable_partition to move even elements to the end of v, copy this part to even using the vector constructor and then erase the copied elements from v:
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5, 6 };
// actual logic
auto pivot = std::stable_partition(v.begin(), v.end(), [](int x) { return (x % 2) != 0; });
std::vector<int> even(pivot, v.end());
v.erase(pivot, v.end());
// display results
std::cout << "v:\n";
for (auto x : v)
{
std::cout << x << '\n';
}
std::cout << "even:\n";
for (auto x : even)
{
std::cout << x << '\n';
}
return 0;
}
For objects that are expensive to copy, you may want to use std::move_iterator when creating even as suggested in #MooningDucks answer:
std::vector<int> even(std::move_iterator(pivot), std::move_iterator(v.end()));
I would switch to using std::remove_if instead and have the function you pass to remove_if do the adding to the other vector. That gives you
std::vector<int> v = {1, 2, 3, 4, 5, 6};
std::vector<int> even;
v.erase(std::remove_if(v.begin(), v.end(),
[&](auto val){ bool cond = (val % 2 == 0);
if (cond) even.push_back(val); return cond; }), v.end());
I'm trying to get into 'modern' C++, so I'm trying to learn how to properly use functors, and, subsequently, lambdas.
I think I've understood basic principle behind it, but I'm having trouble to understand how to acquire any element from a vector that is passed to my algorithm.
So, let's say I wish to create a Fibonacci sequence of length N...
struct Adder {
int operator()(int a, int b) {return a+b;}
};
const int N = 10;
int main() {
std::vector<int> vec = {0, 1};
vec.resize(N);
//Old Way
for(int i = 2; i < vec.size(); i++) {
vec[i] = vec[i-1] + vec[i-2];
}
std::transform(vec.begin(), vec.end(), vec.begin(), [](int i){return i*3;}); //single operator given to function, works
// std::transform(vec.begin()+2, vec.end(), vec.begin(), /*here two calls are needed , for a and b operators*/);
return 0;
}
Basically my question is how to activate functor defined in struct Adder? What is the proper way to pass two operators to him?
Adder::operator() should be const. And your Adder functor is unnecessary. Just use std::plus<>.
Since C++17, we have the transform overload that accepts two sequences. So we can do: (you can use Adder{} in place of std::plus<>{} if you want)
std::transform(vec.begin(), vec.end() - 2, vec.begin() + 1, vec.begin() + 2, std::plus<>{});
Minimal example: (live demo)
#include <algorithm>
#include <iostream>
#include <vector>
constexpr int N = 10;
int main()
{
std::vector<int> vec{0, 1};
vec.resize(N);
std::transform(vec.begin(), vec.end() - 2, vec.begin() + 1, vec.begin() + 2, std::plus<>{});
for (int x : vec)
std::cout << x << " ";
std::cout << "\n";
}
Suppose I have a vector as follows
std::vector<int> v = {3, 9, 7, 7, 2};
I would like to sort this vector of elements so that the vector will be stored as 77932. So first, we store the common elements (7), then we sort the remaining elements from the highest to the lowest.
If I have a vector as follows
std::vector<int> v = {3, 7, 7, 7, 2};
Here, it would lead to 77732.
Same for
std::vector<int> v = {7, 9, 2, 7, 9};
it should lead to 99772, because the 9s are higher than 7s.
One last example
std::vector<int> v = {7, 9, 7, 7, 9};
it should lead to 77799, because there are more 7s than 9s.
What could be the fastest algorithm to implement this?
Use std::multiset to do counting for you. Then sort using a simple custom comparer with tie breaking logic implemented with std::tie:
std::vector<int> data = {7, 9, 2, 7, 9};
std::multiset<int> count(data.begin(), data.end());
std::sort(
data.begin()
, data.end()
, [&](int a, int b) {
int ca = count.count(a);
int cb = count.count(b);
return std::tie(ca, a) > std::tie(cb, b);
}
);
std::copy(data.begin(), data.end(), std::ostream_iterator<int>(std::cout, " "));
Demo 1
Edit: count(n) function of of std::multiset is linear in the number of duplicates, which may degrade the performance of your sorting algorithm. You can address this by using std::unordered_map in its place:
std::vector<int> data = {7, 9, 2, 7, 9};
std::unordered_map<int,int> count;
for (auto v : data)
count[v]++;
std::sort(
data.begin()
, data.end()
, [&](int a, int b) {
return std::tie(count[a], a) > std::tie(count[b], b);
}
);
std::copy(data.begin(), data.end(), std::ostream_iterator<int>(std::cout, " "));
Demo 2.
You will need an auxiliary frequency count structure, then you can just define a comparator lambda and use whatever sort you like, std::sort is a sensible default
std::unordered_map<int, size_t> frequency;
std::for_each(v.begin(), v.end()
, [&](int i) { ++frequency[i]; });
std::sort(v.begin(), v.end()
, [&](int lhs, int rhs)
{
return std::tie(frequency[lhs], lhs) < std::tie(frequency[rhs], rhs);
});
I wouldn't be satisfied if a candidate proposed an auxiliary map for this task - clearly a sort does most of the work, and the auxiliary structure should be a vector (or, after I've actually tried to implement it, 2 vectors):
void custom_sort(vector<int> &v)
{
if (v.size() < 2)
return;
sort(v.begin(), v.end(), std::greater<int>());
vector<int> dupl;
vector<int> singl;
int d;
bool dv = false;
for (int i = 1; i < v.size(); ++i)
{
if (!dv)
{
if (v[i - 1] == v[i])
{
d = v[i];
dv = true;
dupl.push_back(d);
}
else
{
singl.push_back(v[i - 1]);
}
}
else
{
dupl.push_back(d);
if (v[i] != d)
dv = false;
}
}
if (!dv)
singl.push_back(v.back());
else
dupl.push_back(d);
auto mid = copy(dupl.begin(), dupl.end(), v.begin());
copy(singl.begin(), singl.end(), mid);
}
But yes, the branching is tricky - if you want to use it for more than an inverview, please test it... :-)
EDIT this answers an early version of the question.
If the elements are small integers, i.e. have limited range, we can extend the counting sort algorithm (since the keys here are the elements, we don't need to establish the starting position separately).
void custom_sort(std::vector<int>&v, const int N)
// assume that all elements are in [0,N[ and N elements fit into cash
{
vector<int> count(N);
for(auto x:v)
count.at(x) ++; // replace by count[x]++ if you're sure that 0 <= x < N
int i=0;
// first pass: insert multiple elements
for(auto n=N-1; n>=0; --n)
if(count[n] > 1)
for(auto k=0; k!=count[n]; ++k)
v[i++] = n;
// second pass: insert single elements
for(auto n=N-1; n>=0; --n)
if(count[n] == 1)
v[i++] = n;
}
There is O(N Log(N)) algorithm with extra O(N) memory.
#include <cstdio>
#include <vector>
#include <algorithm>
#include <utility>
int main(){
typedef std::pair<int, int> pii;
typedef std::vector< int > vi ;
typedef std::vector< pii > vii;
vi v = {7, 9, 7, 7, 9};
//O( N log(N) )
std::sort(v.begin(), v.end());
vii vc;
vc.reserve(v.size());
// O (N) make (cnt, value) pair of vector
for(size_t i = 0; i != v.size(); ++i)
{
if (vc.empty() || v[i] != vc.back().second ){
vc.push_back( pii(0, v[i]) ) ;
}
vc.back().first ++ ;
}
// O (N Log(N) ) sort by (cnt, value)
std::sort( vc.begin(), vc.end() ) ;
// O(N) restore they, reverse order.
v.clear();
for(int i = 0; i < (int)vc.size(); ++i){
int rev_i = vc.size() - i - 1;
int cnt = vc[rev_i].first;
for(int k = 0; k < cnt; ++k)
v.push_back( vc[rev_i].second ) ;
}
/////////////////////////
for(size_t i = 0; i != v.size(); ++i){
printf("%4d, ", v[i]);
}
printf("\n");
}
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';
}
This mean that while I am sorting the v2 in the nonincreasing order, the v1 should looks like this:
Vectors look as following.
v1 = {0, 5, 5, 2, 10};
v2 = {0 ,2, 6, 20, 5};
The output:
v1 = {2, 5, 10, 5, 0};
v2 = {20, 6, 5, 2, 0};
I was trying to solve that problem mixing std::sort and lambdas.
That is why I have read several questions about std::sort, there were no answers which solve the problem similar to mine so that is why I am asking.
I am especially interested in the answers which contains it's usage or other features of C++11 and C++14.
It is not a question like:
"I completely do not know what to do."
I know how to achieve the output using C++98, but I am wondering if there is an more efficient and prettier way to do it.
Big thanks for your help :)
You could zip, sort, and unzip.
#include <iostream>
#include <vector>
#include <algorithm>
//converts two vectors into vector of pairs
template <typename T, typename U>
auto zip(T t, U u) {
std::vector<std::pair<typename T::value_type,typename U::value_type>> pairs;
for (size_t i = 0; i < t.size(); ++i){
pairs.emplace_back(u[i],t[i]);
}
return pairs;
}
//converts a vector of pairs, back into two two vectors
template <typename T, typename U, typename V>
void unzip(V pairs, T & t, U & u) {
for (auto const& it: pairs){
u.emplace_back(it.first);
t.emplace_back(it.second);
}
}
int main(){
//vectors
std::vector<int> v1 = {0, 5, 5, 2, 10};
std::vector<int> v2 = {0 ,2, 6, 20, 5};
//zip vectors
auto pairs = zip(v1,v2);
//sort them
std::sort(pairs.begin(),pairs.end(),std::greater<>());
//unzip them
v1.clear();
v2.clear();
unzip(pairs,v1,v2);
//print
std::cout << '\n';
for (auto i: v1) std::cout << i << ' ';
std::cout << '\n';
for (auto i: v2) std::cout << i << ' ';
std::cout << '\n';
}
Well, I don't if this is would be efficient, or not, but this demonstrates how to do this with std::generate, std::sort, and std::transform, with some extra seasoning from mutable lambdas, and iterators.
#include <algorithm>
#include <iostream>
int main()
{
std::vector<int> v1={0, 5, 5, 2, 10},
v2 = {0, 2, 6, 20, 5};
std::vector<int> index;
index.resize(5);
std::generate(index.begin(), index.end(),
[n=0]
()
mutable
{
return n++;
});
std::sort(index.begin(), index.end(),
[&]
(auto &a, auto &b)
{
return v2[b] < v2[a];
});
std::vector<int> v1_out, v2_out;
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v1_out),
[&]
(auto a)
{
return v1[a];
});
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v2_out),
[&]
(auto a)
{
return v2[a];
});
for (auto n: v1_out)
std::cout << n << ' ';
std::cout << std::endl;
for (auto n: v2_out)
std::cout << n << ' ';
std::cout << std::endl;
}
Big thanks for all your answers. I found an easy way to achieve the same effect and the idea comes from this answer.
1.
Firstly I used all code from answer which I linked, so it is:
template <typename T>
vector<size_t> sort_indexes(const vector<T> &v)
{
// initialize original index locations
vector<size_t> idx(v.size());
for (size_t i = 0; i != idx.size(); ++i) idx[i] = i;
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2)
{
return v[i1] >= v[i2];
});
return idx;
}
2.Then I did a function which will sort first and a second vector using the third one.To achieve it I had to create temporary vectors one for a first vector, one for a second, and two for the last one.
Why two?
Because I have to remember the sorted indexes of the third vector and I need a one temporary to which I will be pushing elements of the original third vector according to sorted indexes.
void SortByIndexes(vector<int>& Pi,vector<int> &Wi,vector<int>& PidivWi)
{
vector<int> Pitemp, Witemp, PidivWitemp,SortedIndexes;
for (auto i : sort_indexes(PidivWi))
{
SortedIndexes.push_back(i);
}
for (auto i : SortedIndexes)
{
Pitemp.push_back(Pi[i]);
Witemp.push_back(Wi[i]);
PidivWitemp.push_back(PidivWi[i]);
}
swap(Pi, Pitemp);
swap(Wi, Witemp);
swap(PidivWi,PidivWitemp);
}
3. After sorting just swap sorted vectors with original ones.
Done.
Thank you all guys.