STL algorithm function with reverse iterators doesn't work - c++

I need to find minimum element in my array, but if amount of minimum elements more than 1, I need to use the most right one.
Consider this code:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a(n);
for (int& x : a)
cin >> x;
vector<int>::iterator it_min = min_element(a.rbegin(), a.rend());
}
It doesn't work. And it doesn't make sense to me why. Reverse_iterator basically provides all operators needed for correct execution of function. But apparently min_element() expecting only "normal" iterator is being given. Can I somehow bypass that? Ok, I can convert my reverse_iterator to iterator with .base() function member (min_element(a.rbegin().base(), a.rend().base())), but that doesn't resolve my problem since operator+ is now going forward, not backwards. I could not think of anything sensible. Is there an elegant solution to this problem?
P.S. There's a solution to my problem with custom comparator and it works with normal iterators, but still I want to find out if there is a solution with reverse_iterators:
vector<int>::iterator it_min = min_element(a.begin(), a.end(), [](int min, int b) { return min >= b; });
UPD: After the answer, I understood that everything I said about min_element() is wrong. It can accept reverse_iterators and work with them correctly, but I was confused about why it requires conversion reverse_iterators to iterators, but it didn't required the a.rbegin() and a.rend() to convert to "normal" iterators. It required to convert the returning iterator itself.

You're passing reverse_iterator to min_element, then it returns reverse_iterator too.
Change the code to
vector<int>::reverse_iterator it_min = min_element(a.rbegin(), a.rend());
Or
auto it_min = min_element(a.rbegin(), a.rend());
You can get the vector<int>::iterator from the returned reverse_iterator later by it_min.base() - 1.

Related

How do i iterate over alternate elements(or taking leaps of specific size) in set?

In other words, what I mean to say is : Is itr+=2 a valid argument in c++ ?, where (itr is an iterator to first element of the set). If so, then the following piece of code should work:
In this piece if code, the code written in /comment section/ functions well, while the code not in comment section do not. Help me out to iterate alternate elements.
#include <bits/stdc++.h>
using namespace std;
int main()
{
set<int> s;
s.insert(5);
s.insert(7);
s.insert(8);
auto it=s.begin();
cout<<*it<<'\n';
it+=2;
cout<<*it<<'\n';
/*for(auto it=s.begin();it!=s.end();it++)
cout<<*it<<" ";*/
return 0;
}
Is itr+=2 a valid argument in c++?
It depends on the container type. For example, it would be perfectly valid for std::vector or std::array, but not for std::set. Each container, due to its nature, provides different types of iterators. std::set only provides BidirectionalIterator, which do not support jumping over arbitrary number of elements, only incrementation and decrementation.
However, you can use std::advance() from <iterator> library (or just increment the iterator twice). Beware that you must never increment end() iterator, so you need to take it into account in loop condition.
for(auto it=s.begin(); it != s.end() && it != std::prev(s.end()); std::advance(it, 2))

Why do I get a runtime error: Vector erase iterator outside range

I have faced with a strange runtime error for the following code:
#include <algorithm>
#include <vector>
using std::vector;
struct Data
{
int id;
};
int main()
{
vector<Data> mylist;
Data m;
m.id = 10;
mylist.push_back(m);
mylist.erase(std::remove_if(
mylist.begin(),
mylist.end(),
[](const Data &m) {
return m.id>100;
}));
return 0;
}
The error says:
Vector erase iterator outside range
I am not after solving the problem like Ref1, Ref2 but realizing the cause of the problem and whether I have done something wrong.
The correct form is
mylist.erase(
std::remove_if(mylist.begin(),mylist.end(),lambda),
mylist.end());
You need to pass the end to the erase too.
In the above example, the remove_if function template will return an invalid iterator. Now as per the documentation of std::vector::erase the iterator pos must be valid and dereferenceable which is in your case is not valid and because of which the assert message is thrown.
Alternatively, you can just provide the end() iterator as second argument to the std::vector::erase function or use std::vector::pop_back to pop the element from the vector.

std::transform on a multiset giving me error C3892

I am trying to understand how the std::transform function works, but I'm having a bit of trouble with the following code. I want to take a multiset ms, add 1 to the contents of each element and store them in a new multiset msc. Here is what I have:
int op_increase(int i) { return ++i; }
int main()
{
std::multiset<int> ms = {1,1,2,2,3};
std::multiset<int> msc;
std::transform(ms.begin(), ms.end(), msc.begin(), op_increase);
return 0;
}
However I get the following error:
C3892: _Dest: you cannot assign to a variable that is const
Your code was not utilizing the correct argument to std::transform that allows insertion into an empty container. This requires using an iterator that is intelligent enough to call the appropriate function that calls the container's insert() function.
The solution is to provide std::transform the std::inserter iterator that automatically inserts into the empty multiset. Here is an example:
#include <set>
#include <algorithm>
#include <iterator>
int op_increase(int i) { return ++i; }
int main()
{
std::multiset<int> ms = {1,1,2,2,3};
std::multiset<int> msc;
std::transform(ms.begin(), ms.end(), std::inserter(msc, msc.begin()), op_increase);
// msc now contains 2,2,3,3,4
}
Note the std::inserter is used, and not merely msc.begin(). The inserter will automatically insert the items into the map.
Live Example
The problem here is that std::multiset<T>::begin() returns a std::_Tree_const_iterator type. That's why you cannot change its value. This behavior is sensible: the std::multiset, like std::set, is a sorted container typicaly implemented as a red-black tree, and thus change of a value of one element may require update of the whole data structure. If user really wants to do this, he may erase a node and add it back.
To better understand the std::transform behavior, you may use std::vector container instead of std::multiset. Cplusplus.com contains a good example of code using std::transform.
Also, as far as I understand from your code, you try to add the resulting data into the initially empty std::multiset. To achieve this functionality, you may use std::insert_iterator (Cplusplus.com), like this:
int op_increase(int i) { return ++i; }
int main()
{
std::multiset<int> ms = {1,1,2,2,3};
std::multiset<int> msc;
std::transform(ms.begin(), ms.end(), inserter(msc, msc.begin()), op_increase);
return 0;
}

Producing permutations of a set of integers in C++. Getting Segmentation Fault

I was trying to solve a problem which involved producing all the permutations of a set of numbers. The idea seemed simple enough (code below), but I keep getting segmentation faults. Can anyone tell me what I'm doing wrong?
void permute(set<int>& s, vector<int>& v) {
if(s.empty()) {
// read the permutation in v.
return;
}
set<int>::iterator i;
for(i = s.begin(); i != s.end(); i++) {
int x = *i;
s.erase(i);
v.push_back(x);
permute(s, v);
v.pop_back();
s.insert(x);
}
}
To produce all permutations in C++ use std::next_permutation. The problem here is that you cannot permutate the set, because the ordering is preset by the key comparator operator (in your case the less than operator). What you can do is store your values in a non associative container and then have a go.
#include <set>
#include <algorithm>
#include <iterator>
#include <vector>
using namespace std;
void permut(std::set<int> const &inp, std::vector<int> &all_permutations)
{
vector<int> tmp(inp.size());
copy(inp.begin(), inp.end(), tmp.begin());
vector<int> all_permutations;
do
{
copy(tmp.begin(), tmp.end(), back_inserter(all_permutations));
} while (std::next_permutation(tmp.begin(), tmp.end()));
}
The permute() function calls itself recursively while iterating over the mutable containers and making changes. This violates iterator validity promises.
You might try something based on std::next_permutation()
I have not gone into the precise behaviour you can expect from a set, but it seems pretty clear that the things you are doing inside the for loop are enough to make the iterator invalid. I would suggest redesigning things so that you have two sets, and work through one (which starts off full) while generating the permutation in the other (which starts off empty).

Vectors in C++ : Why i'm getting so much errors for this simple copy & print program?

i'm trying to use algorithm lib & vector lib to first copy a set of numbers from an array into a vector then printing it using iteration, where is the problem of my code?
and one thing is that i chose 2 way to do this iteration first using vec.begin() ; vec.end() method & the other one is for (i = 0 ; i < vec.capacity() ; i++)
both facing errors.
what should i do?
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int intArray[] = {5,6,8,3,40,36,98,29,75};
vector<int> vecList(9);
//vector<int>::iterator it;
copy (intArray, intArray+9,vecList);
//for(it = vecList.begin() ; it != vecList.end() ; it++)
for (int it = 0 ; it < vecList.capacity() ; it++)
{
cout<<*it<<endl;
}
system("pause");
return 0;
}
There are several improvements possible.
You confuse iterators with indices. An iterator it is a glorified pointer into the vector, that you need to derefence by typing *it. An index i is an offset from the beginning of the vector and saying vecList[i] will give you that element.
The initialization of the vector is best done using initializer lists (C++11), rather than reading from an array.
You need to loop to vecList.size(). The capacity of the vector is the size of the allocated storage space for the elements of the vector container. Looping is best done with a ranged-for loop as shown by Kerrek SB, or a std::for_each + a lambda expression, or a regular for loop as you did. In that case however, it's best to get into the habit of doing it != vecList.end() (instead of using <) and doing ++it instead of it++.
Note that I also used auto to avoid writing the explicit iterator type. It's also a good habit to get into using auto wherever you can.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
// initialize vector with a list of elements
vector<int> vecList {5,6,8,3,40,36,98,29,75};
// loop from begin() to end() of vector, doing ++it instead of it++
for (auto it = vecList.begin(); it != vecList.end(); ++it)
{
cout<<*it<<endl;
}
// the pause command is better done by setting a breakpoint in a debugger
return 0;
}
Output on Ideone (this uses the g++ 4.5.1 compiler, it's best to upgrade to at least that version to take advantage of C++11 features).
The problem is that you're confusing indexes and iterators.
w/ index:
for (int i = 0 ; i < vecList.size() ; it++)
{
cout<<vecList[i]<<endl;
}
w/ iterators
for (std::vector<int>::const_iterator it = vecList.begin() ; i != vecList.end() ; it++)
{
cout<<*it<<endl;
}
A. you need to iterate on vecList.size() not vecList.capacity() which mean how much memory the vector is reserving for himself (not how much of it is in use).
B. you tried to use the integer index it as an iterator with the call to *it, you should check Luchian Grigore answer for the right way to do it.
This isn't an answer, but I wanted to show how modern C++ allows you to do away with lots of the brittle dependencies on details:
int intArray[] = {5,6,8,3,40,36,98,29,75};
std::vector<int> vecList(std::begin(intArray), std::end(intArray));
for (int i : vecList) { std::cout << i << std::endl; }
Using iterators and algorithms idiomatically, you can often remove any explicit mention of details such as lengths of arrays, thus making your code more robust.
Typo mistake use : copy (intArray, intArray+9,vecList.begin());
so,
#include<iostream>
#include<vector>
#include <algorithm>
using namespace std;
int main()
{
int intArray[] = {5,6,8,3,40,36,98,29,75};
vector<int> vecList(9);
vector<int>:: iterator it;
copy (intArray, intArray+9,vecList.begin());
for (it=vecList.begin();it!=vecList.end(); it++)
{
cout<<*it<<endl;
}
system("pause");
return 0;
}