How to find indexes of the n greatest elements - c++

I have a container (Vector) of some arbitrary type and i want to get a vector with indices of the n greatest (or smallest) elements.
Is there a standard way to do so?

This is exactly the topic of one of the guru of the week http://www.gotw.ca/gotw/073.htm
I am reporting the preferred solution, however, I strongly recommend you to read the article (and the blog in general), it is really good.
#include <vector>
#include <map>
#include <algorithm>
namespace Solution3
{
template<class T>
struct CompareDeref
{
bool operator()( const T& a, const T& b ) const
{ return *a < *b; }
};
template<class T, class U>
struct Pair2nd
{
const U& operator()( const std::pair<T,U>& a ) const
{ return a.second; }
};
template<class IterIn, class IterOut>
void sort_idxtbl( IterIn first, IterIn last, IterOut out )
{
std::multimap<IterIn, int, CompareDeref<IterIn> > v;
for( int i=0; first != last; ++i, ++first )
v.insert( std::make_pair( first, i ) );
std::transform( v.begin(), v.end(), out,
Pair2nd<IterIn const,int>() );
}
}
#include <iostream>
int main()
{
int ai[10] = { 15,12,13,14,18,11,10,17,16,19 };
std::cout << "#################" << std::endl;
std::vector<int> aidxtbl( 10 );
// use another namespace name to test a different solution
Solution3::sort_idxtbl( ai, ai+10, aidxtbl.begin() );
for( int i=0; i<10; ++i )
std::cout << "i=" << i
<< ", aidxtbl[i]=" << aidxtbl[i]
<< ", ai[aidxtbl[i]]=" << ai[aidxtbl[i]]
<< std::endl;
std::cout << "#################" << std::endl;
}

Related

Finding the top K frequent Elements

I am trying to solve a question on leetcode which is finding the top k frequent elements. I think my code is correct but the output for a test case is failing.
Input: [ 4,1,-1,2,-1,2,3]
K=2
My answer comes out to be {1,-1} but the expected is {-1,2}. I am not sure where am i getting wrong.
struct myComp{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
if(a.second==b.second)
{
return a.first<b.first;
}
return a.second<b.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> mp;
for(int i=0;i<nums.size();i++)
{
mp[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,myComp> minheap;
for(auto x:mp)
{
minheap.push(make_pair(x.second,x.first));
if(minheap.size()>k)
{
minheap.pop();
}
}
vector<int> x;
while(minheap.size()>0)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
link: https://leetcode.com/problems/top-k-frequent-elements
In the minheap, pairs of <frequency, element> are being pushed. Since we want to sort these pairs on basis of frequency, we need to compare on the basis of frequency only.
Let's say there are two pairs a and b. Then for normal sorting, the comparison would be :
a.first < b.first;
And for reverse sorting, the comparison would be :
a.first > b.first;
In case of min-heap, we need reverse sorting. Hence, the following comparator makes your code pass all the test cases :
struct myComp
{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
return a.first > b.first;
}
};
There are several issues with your code.
Obviously there is somewhere using namespace std; in your code. This should be avoided. You will find many posts here on SO explaining, why it this should not be done.
Then we need to qualify all elements from the std library with std::, which makes the scope very clear.
Next: You do not need your own sorting function. Since you insert the elements from the pair in swapped order into the std::priority_queue, the sorting criteria is valid for the counter part, not for the key value. So, your sorting function was anyway wrong, because it was sorting accodring to "second" and not to "first". But if we have a standard sorting, we do not need a special sorting algorithm. A std::pair has a less-than operator. So, the definition can be simply:
std::priority_queue<std::pair<int, int>> minheap;
Then, your if statement
if(minheap.size()>k)
{
minheap.pop();
}
is wrong. You will allow only k values to be inserted. And this are not necessarily the biggest ones. So, you need to insert all values from the std::unordered map. And then they are sorted.
With some cosmetic changes the code will look like the below:
#include <iostream>
#include <utility>
#include <unordered_map>
#include <vector>
#include <queue>
std::vector<int> topKFrequent(std::vector<int>& nums, size_t k) {
std::unordered_map<int, int> mp;
for (size_t i = 0; i < nums.size(); i++)
{
mp[nums[i]]++;
}
std::priority_queue<std::pair<int, int>> minheap;
for (auto x : mp)
{
minheap.push(std::make_pair(x.second, x.first));
}
std::vector<int> x;
for (size_t i{}; i< k; ++i)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
}
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
std::vector result = topKFrequent(data, 2);
for (const int i : result) std::cout << i << ' '; std::cout << '\n';
return 0;
}
An additional solution
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
auto topKFrequent(std::vector<int>& nums, size_t k) {
// Count occurences
std::unordered_map<int, size_t> counter{};
for (const int& i : nums) counter[i]++;
// For storing the top k
std::vector<std::pair<int, size_t>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
// Test code
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
for (const auto& p : topKFrequent(data, 2))
std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
return 0;
}
And of course, we do have also the universal solution for any kind of iterable container. Including the definition for type traits using SFINAE and checking for the correct template parameter.
#include <iostream>
#include <utility>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>
// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()), // begin/end and operator !=
++std::declval<decltype(std::begin(std::declval<Container&>()))&>(), // operator ++
void(*std::begin(std::declval<Container&>())), // operator*
void(), // Handle potential operator ,
std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);
// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));
// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;
// Function to get the k most frequent elements used in any Container ------------------------------------------------
template <class Container>
auto topKFrequent(const Container& data, size_t k) {
if constexpr (is_iterable<Container>::value) {
// Count all occurences of data
Counter<Container> counter{};
for (const auto& d : data) counter[d]++;
// For storing the top k
std::vector<Pair<Container>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
else
return data;
}
int main() {
std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
for (const auto& p : topKFrequent(testVector, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
for (const auto& p : topKFrequent(cStyleArray, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
std::string s{"abbcccddddeeeeeffffffggggggg"};
for (const auto& p : topKFrequent(s, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double value = 12.34;
std::cout << topKFrequent(value,2) << "\n";
return 0;
}
Developed and tested with Microsoft Visual Studio Community 2019, Version 16.8.2.
Additionally compiled and tested with clang11.0 and gcc10.2
Language: C++17

Sort just one member of the classes in a vector, leaving the other members unchanged

There are tons of answers for sorting a vector of struct in regards to a member variable. That is easy with std::sort and a predicate function, comparing the structs member. Really easy.
But I have a different question. Assume that I have the following struct:
struct Test {
int a{};
int b{};
int toSort{};
};
and a vector of that struct, like for example:
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
I do not want to sort the vectors elements, but only the values in the member variable. So the expected output should be equal to:
std::vector<Test> tvSorted{ {1,1,5},{2,2,6},{3,3,7},{4,4,8},{5,5,9} };
I wanted to have the solution to be somehow a generic solution. Then I came up with a (sorry for that) preprocessor-macro-solution. Please see the following example code:
#include <iostream>
#include <vector>
#include <algorithm>
struct Test {
int a{};
int b{};
int toSort{};
};
#define SortSpecial(vec,Struct,Member) \
do { \
std::vector<decltype(Struct::Member)> vt{}; \
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [](const Struct& s) {return s.Member; }); \
std::sort(vt.begin(), vt.end()); \
std::for_each(vec.begin(), vec.end(), [&vt, i = 0U](Struct & s) mutable {s.Member = vt[i++]; }); \
} while (false)
int main()
{
// Define a vector of struct Test
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
// Call sort macro
SortSpecial(tv, Test, toSort);
std::cout << "\n\nSorted\n";
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
Since macros shouldn't be used in C++, here my questions:
1. Is a solution with the algorithm library possible?
2. Or can this be achieved via templates?
To translate your current solution to a template solution is fairly straight forward.
template <typename T, typename ValueType>
void SpecialSort(std::vector<T>& vec, ValueType T::* mPtr) {
std::vector<ValueType> vt;
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [&](const T& s) {return s.*mPtr; });
std::sort(vt.begin(), vt.end());
std::for_each(vec.begin(), vec.end(), [&, i = 0U](T& s) mutable {s.*mPtr = vt[i++]; });
}
And we can call it by passing in the vector and a pointer-to-member.
SpecialSort(tv, &Test::toSort);
Somewhow like this (You just need to duplicate, rename and edit the "switchToShort" funtion for the rest of the variables if you want):
#include <iostream>
#include <vector>
struct Test {
int a{};
int b{};
int toSort{};
};
void switchToShort(Test &a, Test &b) {
if (a.toSort > b.toSort) {
int temp = a.toSort;
a.toSort = b.toSort;
b.toSort = temp;
}
}
//void switchToA(Test& a, Test& b) { ... }
//void switchToB(Test& a, Test& b) { ... }
inline void sortMemeberValues(std::vector<Test>& data, void (*funct)(Test&, Test&)) {
for (int i = 0; i < data.size(); i++) {
for (int j = i + 1; j < data.size(); j++) {
(*funct)(data[i], data[j]);
}
}
}
int main() {
std::vector<Test> tv { { 1, 1, 9 }, { 2, 2, 8 }, { 3,3 ,7 }, { 4, 4, 6 }, { 5, 5, 5} };
sortMemeberValues(tv, switchToShort);
//sortMemeberValues(tv, switchToA);
//sortMemeberValues(tv, switchToB);
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
With range-v3 (and soon ranges in C++20), you might simply do:
auto r = tv | ranges::view::transform(&Test::toSort);
std::sort(r.begin(), r.end());
Demo

Can non type template arguments done on stl containers?

template<typename T,int nSize>
T Sum(T (&parr)[nSize])
{
T sum=0;
for(int i = 0; i < nSize ;++i)
{
sum += parr[i];
}
return sum;
}
int _tmain(int argc, _TCHAR* argv[])
{
int nArr[] = {1,2,3,4};
int nSum = Sum(nArr);
std::cout<<"Sum :"<<nSum;
}
Can std::vector be used instead of array.Or can array be replaced by any of the stl containers?
Can std::vector be used instead of array.Or can array be replaced by
any of the stl containers?
No. It is not possible as they are different in their types. But you can generalize the given function in the following way.
Make a template function taking the begin and end iterators of the container. Then using std::accumulate, sum the elements up, which will work for any sequence containers as well as the arrays:
Following is an example code: (See live online)
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <numeric> // std::accumulate
#include <iterator> // std::iterator_traits, std::cbegin, std::cend
template<typename Iterator>
constexpr auto Sum(Iterator begin, const Iterator end) -> typename std::iterator_traits<Iterator>::value_type
{
using RetType = typename std::iterator_traits<Iterator>::value_type;
return std::accumulate(begin, end, RetType{});
}
int main()
{
int nArr[] = { 1,2,3,4 };
std::vector<int> vec{ 1,2,3,4 };
std::list<int> list{ 1,2,3,4 };
// now you can
std::cout << "Sum of array: " << Sum(std::cbegin(nArr), std::cend(nArr)) << "\n";
std::cout << "Sum of vec: " << Sum(std::cbegin(vec), std::cend(vec)) << "\n";
std::cout << "Sum of list: " << Sum(std::cbegin(list), std::cend(list)) << "\n";
}
Output:
Sum of array: 10
Sum of vec: 10
Sum of list: 10
template<typename T, int nSize>
T sum(std::array<T, nSize> const&);
would be the equivalent signature for std::array. As you see, signature differs already. Trying to do the same for std::vector is bound to fail:
template<typename T, int nSize>
T sum(std::vector<T> const&);
How would you be able to know at compile time already how many elements will reside in the vector??? You simply cannot. Even if you specified nSize in code explicitly, e. g. sum<std::vector<int>, 7>, the function then would always try to iterate over exactly seven elements, resulting in undefined behaviour if there are less and not counting the surplus ones if there are more...
The typical way to go is using begin and end iterators, just as the standard library does, too, for all of its algorithms:
template <typename Iterator>
auto sum(Iterator begin, Iterator end) -> std::remove_reference_t<decltype(*begin)>
{
using type = decltype(sum(begin, end)); // just not wanting to repeat all
// that remove_reference stuff...
type s = type();
for( ; begin != end; ++begin)
{
s += *begin;
}
return s;
}
You additionally could, based on this function, provide a generic overload for arbitrary containers then:
template <typename Container>
auto sum(Container const& c)
{
using std::begin;
using std::end;
return sum(begin(c), end(c));
}
If your compiler supports C++ 17 then you can write a single function with using if constexpr statement.
For example
#include <iostream>
#include <vector>
template<typename T>
auto Sum( const T &container )
{
if constexpr( std::is_array_v<std::remove_reference_t<T>> )
{
std::remove_extent_t<T> sum = 0;
for ( const auto &item : container )
{
sum += item;
}
return sum;
}
else
{
typename T::value_type sum = 0;
for ( const auto &item : container )
{
sum += item;
}
return sum;
}
}
int main()
{
int nArr[] = { 1, 2, 3, 4 };
int nSum = Sum( nArr );
std::cout << "Sum :"<<nSum << '\n';;
std::vector<int> v = { 1, 2, 3, 4 };
nSum = Sum( v );
std::cout << "Sum :"<<nSum << '\n';;
}
The program output is
Sum :10
Sum :10
However it is better to split the function into two functions: one for arrays and other for standard containers.
#include <iostream>
#include <vector>
template<typename T, size_t N>
auto Sum( const T ( &a )[N] )
{
T sum = 0;
for ( const auto &item : a )
{
sum += item;
}
return sum;
}
template<typename T>
auto Sum( const T &container )
{
typename T::value_type sum = 0;
for ( const auto &item : container )
{
sum += item;
}
return sum;
}
int main()
{
int nArr[] = { 1, 2, 3, 4 };
int nSum = Sum( nArr );
std::cout << "Sum :"<<nSum << '\n';;
std::vector<int> v = { 1, 2, 3, 4 };
nSum = Sum( v );
std::cout << "Sum :"<<nSum << '\n';;
}

The highest element (highest real part ) in Valarray of complex numbers in C++

How can I print out the highest element of Valarray of complex numbers in C++ ?
I have tried with this code but it is returning error messages
#include <iostream> // std::cout
#include <valarray>
#include <complex>// std::valarray
typedef std::complex<double> Complex;
typedef std::valarray <Complex > CArray;
int main ()
{
CArray y[5]={{1, 2},{3, 4},{2,0},{7,0},{9,0}};
std::cout << "The max is " << y.max() << '\n';
return 0;
}
Output:
main.cpp: In function 'int main()':
main.cpp:15:35: error: request for member 'max' in 'y', which is of non-class type 'CArray [5] {aka std::valarray<std::complex<double> > [5]}'
std::cout << "The max is " << y.max() << '\n';
^
What I am doing wrong ?
Second version of code
I have modified a bit the code, Now I would like to get all index corresponding to the highest element of my Valarray in my case all index corresponding to element {9,0}
Note :by "Highest element" I mean element having the highest real part
new code:
#include <iostream>
#include <valarray>
#include <complex>
#include <algorithm>
#include <numeric>
typedef std::complex<double> Complex;
typedef std::valarray <Complex > CArray;
int main ()
{
CArray y={{1, 2},{3, 4},{2,0},{9,0},{7,0},{9,0}};
auto max_val = std::accumulate (std::begin(y), std::end(y), *std::begin(y),
[](const Complex& a ,const Complex& b)
{
auto abs_a = abs(a);
auto abs_b = abs(b);
//if(abs_a == abs_b)
// return std::max(arg(a), arg(b));
return std::max(abs_a, abs_b);
}
);
for (std::size_t i =std::begin(y) ; i != std::end(y) ; i++) {
std::cout << "The max is found on index ["<< i <<"]" << max_val<< '\n';
}
return 0;
}
I am getting following errors :
Output:
main.cpp: In function 'int main()':
main.cpp:22:35: error: invalid conversion from 'std::complex<double>*' to 'std::size_t {aka long unsigned int}' [-fpermissive]
for (std::size_t i =std::begin(y) ; i != std::end(y) ; i++) {
^
main.cpp:22:54: error: ISO C++ forbids comparison between pointer and integer [-fpermissive]
for (std::size_t i =std::begin(y) ; i != std::end(y) ; i++) {
^
y is an array of valarrays, so you need to call max on each element in that array, not on the array itself (which of course has no member functions).
std::complex is not a comparable type, so what does it mean to have a "highest" element?
Update: Regarding your edit, I think I understand what you're after...
For the highest index of the max (by real()) element:
std::size_t max_index(CArray const& y) {
struct acc_t {
double max_value;
std::size_t max_idx, current_idx;
constexpr acc_t next() const { return {max_value, max_idx, current_idx + 1}; }
constexpr acc_t next_with(Complex const c) const {
return {c.real(), current_idx, current_idx + 1};
}
};
return std::accumulate(
std::begin(y), std::end(y), acc_t{},
[](acc_t const acc, Complex const c) {
return c.real() < acc.max_value
? acc.next()
: acc.next_with(c);
}
).max_idx;
}
Online Demo
Or for all indices of the max element:
std::vector<std::size_t> max_indices(CArray const& y) {
struct acc_t {
std::vector<std::size_t> max_idcs;
double max_value;
std::size_t current_idx;
constexpr acc_t&& next() {
++current_idx;
return std::move(*this);
}
acc_t&& next_with_current() {
max_idcs.push_back(current_idx++);
return std::move(*this);
}
acc_t&& next_with(Complex const c) {
max_value = c.real();
max_idcs.clear();
return next_with_current();
}
};
return std::accumulate(
std::begin(y), std::end(y), acc_t{},
[](acc_t& acc, Complex const c) {
return c.real() < acc.max_value ? acc.next()
: c.real() > acc.max_value ? acc.next_with(c)
: acc.next_with_current();
}
).max_idcs;
}
Online Demo
N.b. your code has abs involved but I'm not sure why since you said you just wanted comparison based on std::complex<>::real(), so I've omitted that...
The bigger problem of your original code was (as pointed by Ildjarn) that Complex lack of operator<.
I suppose that your Complex should be a little more complex (if you allow me the play on words).
I propose the following solution were Complex derive from std::complex<double> and declare a friend operator< (). One of many operator< () possible.
#include <iostream>
#include <valarray>
#include <complex>
struct Complex: public std::complex<double>
{
template <typename ... Args>
Complex (const Args & ... args) : std::complex<double>{args...}
{ }
friend bool operator< (const Complex & c1, const Complex & c2)
{
return (c1.real() < c2.real())
|| ((c1.real() == c2.real()) && (c1.imag() < c2.imag()));
}
};
typedef std::valarray <Complex > CArray;
int main ()
{
CArray y { {1.0,2.0}, {3.0,4.0}, {2.0,0.0}, {7.0,0.0}, {9.0,0.0} };
std::cout << "The max is " << y.max() << '\n';
return 0;
}
If you accept that Complex can be a templated class (using Class<double> instead of Complex, you can write a more general solution in this way (that can be used also with complex based on float and long double)
#include <iostream>
#include <valarray>
#include <complex>
template <typename T>
struct Complex: public std::complex<T>
{
template <typename ... Args>
Complex (const Args & ... args) : std::complex<T>{args...}
{ }
friend bool operator< (const Complex & c1, const Complex & c2)
{
return (c1.real() < c2.real())
|| ((c1.real() == c2.real()) && (c1.imag() < c2.imag()));
}
};
typedef std::valarray <Complex<double>> CArray;
int main ()
{
CArray y { {1.0,2.0}, {3.0,4.0}, {2.0,0.0}, {7.0,0.0}, {9.0,0.0} };
std::cout << "The max is " << y.max() << '\n';
return 0;
}
p.s.: should work with C++11 too.
p.s.2: sorry for my bad English.
--- Edited to get the index of the max element ---
#include <iostream>
#include <valarray>
#include <complex>
template <typename T>
struct Complex: public std::complex<T>
{
template <typename ... Args>
Complex (const Args & ... args) : std::complex<T>{args...}
{ }
friend bool operator< (const Complex & c1, const Complex & c2)
{
return (c1.real() < c2.real())
|| ((c1.real() == c2.real()) && (c1.imag() < c2.imag()));
}
};
typedef std::valarray <Complex<double>> CArray;
int main ()
{
CArray y { {1.0,2.0}, {3.0,4.0}, {2.0,0.0}, {7.0,0.0}, {9.0,0.0} };
auto m = 0U;
for ( auto i = 1U ; i < y.size() ; ++i)
if ( y[m] < y[i] )
m = i;
std::cout << "The max is found on index ["<< m <<"] and is " << y[m]
<< std::endl;
return 0;
}
using std::accumulate can get max of complex numbers simillar to Matlab max function:
#include <iostream>
#include <valarray>
#include <complex>
#include <algorithm>
#include <numeric>
typedef std::complex<double> Complex;
typedef std::valarray <Complex > CArray;
int main ()
{
CArray y={{1, 2},{3, 4},{2,0},{7,0},{9,0}};
auto max_val = std::accumulate (std::begin(y), std::end(y), *std::begin(y),
[](const Complex& a ,const Complex& b)
{
auto abs_a = abs(a);
auto abs_b = abs(b);
if(abs_a == abs_b)
return std::max(arg(a), arg(b));
return std::max(abs_a, abs_b);
}
);
std::cout << "The max is " << max_val<< '\n';
return 0;
}
Edit: question edited and OP wants to get index of maximum of real part of complex numbers so your answer:
#include <iostream>
#include <valarray>
#include <complex>
#include <algorithm>
#include <numeric>
typedef std::complex<double> Complex;
typedef std::valarray <Complex > CArray;
int main ()
{
CArray y={{1, 2},{3, 4},{2,0},{7,0},{9,0}};
std::vector<int> index(y.size());
std::iota( index.begin(), index.end(), 0 );
auto max_index = std::accumulate (std::begin(index), std::end(index), *std::begin(index),
[&](int a ,int b)
{
return y[a].real() > y[b].real() ? a: b;
}
);
std::cout << "index of max is " << max_index<< '\n';
return 0;
}
Edit 2: as #ildjarn mentioned modified question wants to get all indices corresponding to the highest element so modified answer:
#include <iostream>
#include <valarray>
#include <complex>
#include <algorithm>
#include <numeric>
typedef std::complex<double> Complex;
typedef std::valarray <Complex > CArray;
int main ()
{
CArray y={{1, 2},{3, 4},{2,0},{7,0},{9,0}};
std::vector<int> index(y.size());
std::iota( index.begin(), index.end(), 0 );
auto max_index = std::accumulate (std::begin(index), std::end(index), *std::begin(index),
[&](int a ,int b)
{
return y[a].real() > y[b].real() ? a: b;
}
);
std::vector<int> indices;
std::copy_if(std::begin(index), std::end(index), std::back_inserter(indices),
[&](int a)
{
return y[a] == y[max_index];
}
);
for (auto i: indices)
std::cout << "index of max is " << i << '\n';
return 0;
}
Edit 3: using std::max_element the simplest solution we have:
#include <iostream>
#include <valarray>
#include <complex>
#include <algorithm>
#include <numeric>
#include <vector>
typedef std::complex<double> Complex;
typedef std::valarray <Complex > CArray;
int main ()
{
CArray y={{1, 2},{3, 4},{2,0},{9,0},{7,0},{9,0}};
auto max_index = std::max_element (std::begin(y), std::end(y),
[](const Complex& a ,const Complex& b)
{
return a.real() < b.real() ;
}
);
std::cout << "index of first max element is " << max_index-std::begin(y) << '\n';
std::cout << "indices of all matches of max element is: " << "[";
for (auto it= std::begin(y), end = std::end(y); it != end; ++it){
if(it->real() == max_index->real()) {
std::cout << it - std::begin(y) << ' ' ;
}
}
std::cout << "]";
return 0;
}

Synchronously sort two containers by elements of first of them

Given a two containers: std::list< int > a; and std::list< int > b;, — a.size() == b.size(). Need to sort containers a and b synchronously, i.e. each swap of elements in a should cause a swapping corresponding elements in b (correspondence in sense of positional indices). Assume, that elements in a and b are very heavyweight. I.e. you can't make its copies.
What is the perfect STL-way to do it? How to use std::sort to perform the operation? What to do if a is const?
What I do currently:
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <utility>
#include <iterator>
#include <algorithm>
#include <list>
#include <vector>
#include <cstdlib>
#include <cassert>
template< typename first, typename second >
void
sort_synchronously(first & f, second & s)
{
std::size_t sz = f.size();
assert(sz == s.size());
struct P
{
typename first::iterator pfirst;
typename second::iterator psecond;
bool operator < (P const & p) const { return (*pfirst < *p.pfirst); }
void swap(P & p) noexcept { std::iter_swap(pfirst, p.pfirst); std::swap(pfirst, p.pfirst); std::iter_swap(psecond, p.psecond); std::swap(psecond, p.psecond); }
};
std::vector< P > p;
p.reserve(sz); // O(N) additional memory
auto fi = std::begin(f);
auto si = std::begin(s);
for (std::size_t i = 0; i < sz; ++i) {
p.push_back({fi, si});
++fi;
++si;
}
std::sort(std::begin(p), std::end(p)); // O(N * log N) time
}
int
main()
{
std::list< int > a{5, 4, 3, 2, 1};
std::list< int > b{1, 2, 3, 4, 5};
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
sort_synchronously(a, b);
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
return EXIT_SUCCESS;
}
But I can't provide free swap (based on P::swap) function for struct P. Is it unavoidable limitation of the language (I can't define non-lambda function inside function scope, but can define non-template class)?
ADDITIONAL:
I found that presence the swap free function overloading is not the type requirement for std::sort function. Just MoveConstructible and MoveAssignable are. Therefore the code is more appropriate (but still incomplete). There is the really hard issue: swap of elements in range provided to std::sort is (evidently) splitted into series of consistuent operations: T tmp(std::move(lhs)); lhs = std::move(rhs); rhs = std::move(tmp);. Therefore I can't swap (during std::sort) referenced elements of containers itself but only the iterators to them.
One reasonably simple solution is to build a vector v of iterators into your lists, and sort that. Then, the ith element of v points to the elements in the lists that should occupy the ith position in the sorted lists, which you can rebuild. Performance might not be optimal, due to the use of the auxiliary containers, but it's easy to understand.
void ZippedSort(std::list<A>& a, std::list<B>& b) {
using PairOfIts = pair<decltype(a.begin()), decltype(b.begin())>;
vector<PairOfIts> v;
auto i = a.begin();
auto j = b.begin();
for (; i != a.end(); ++i, ++j)
v.push_back(make_pair(i, j));
std::sort(v.begin(), v.end(), [](PairOfIts const& i, PairOfIts const& j) { return *i.first < *j.first; } );
list<A> sortedA;
list<B> sortedB;
for (auto& x : v) {
sortedA.splice(sortedA.end(), a, x.first);
sortedB.splice(sortedB.end(), b, x.second);
}
swap(sortedA, a);
swap(sortedB, b);
}
The perfect STL-way to do it is to fill vector with std::pair and create custom comparator which compares only first element in pair. Then you will have sorted vector of pairs.
The proper way to do it is to create an iterator class with something like std::pair<T1 &, T2 &> as it's value_type. It probably should contain an iterator on each sequence that is to be sorted, and properly propagate operations to them.
In fact, that's exactly what boost::zip_iterator does. I recommend using this with an appropriate comparator; or at least using boost::zip_iterator as an example of how it should work.
OK, done. But it looks like (not too dirty) hack: in T tmp(std::move(lhs)); lhs = std::move(rhs); rhs = std::move(tmp); chain of std::swap implementation I make std::sort algorithm to perform only middle operation (both other are no-op):
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <utility>
#include <iterator>
#include <algorithm>
#include <vector>
#include <forward_list>
#include <cstdlib>
#include <cassert>
template< typename first, typename second >
void
sort_synchronously(first & f, second & s)
{
std::size_t sz = static_cast< std::size_t >(std::distance(std::cbegin(f), std::cend(f)));
assert(sz == static_cast< std::size_t >(std::distance(std::cbegin(s), std::cend(s))));
struct P
{
typename first::iterator pfirst;
typename second::iterator psecond;
bool signal;
bool operator < (P const & p) const { return (*pfirst < *p.pfirst); }
P(typename first::iterator pf, typename second::iterator ps)
: pfirst(pf)
, psecond(ps)
, signal(false)
{ ; }
P(P &&) : signal(true) { ; }
void operator = (P && p) { if (!p.signal) { std::iter_swap(pfirst, p.pfirst); std::iter_swap(psecond, p.psecond); } }
};
std::vector< P > p;
p.reserve(sz);
auto fi = std::begin(f);
auto si = std::begin(s);
for (std::size_t i = 0; i < sz; ++i) {
p.emplace_back(fi, si);
++fi;
++si;
}
std::sort(std::begin(p), std::end(p));
}
int
main()
{
std::forward_list< int > a{5, 4, 3, 2, 1};
std::forward_list< int > b{10, 20, 30, 40, 50};
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
sort_synchronously(a, b);
std::cout << std::endl;
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
return EXIT_SUCCESS;
}
I am sure modification for static_assert(std::is_const< first >{}); is evident (just change typename first::iterator to typename first::const_iterator and do std::swap(pfirst, p.pfirst); instead of std::iter_swap(pfirst, p.pfirst);).