Related
Suppose I have a map named m and an iterator i for the map. Presently I am visualising a map iterator as an array index and I want to implement a code like the one given below:
for(auto i = m.begin(); i != m.end(); i++) {
auto l = i - 1; // error type 1
auto r = i + 1; // error type 1
while(l >= m.begin() && r < m.end()) { // error type 2
// ...
r++;
l--;
}
}
Now, I have got some questions which has been confusing me a bit.
For the error type 1 stated in the code, incrementing or decrementing the iterator value gives error, but the similar operation done within the loop (I mean to say i++) gives no error.
For error type 2, why does comparing two iterators (l >= m.begin()), straight up give error, but doing the similar operation in a loop does not give error?
And finally, how could I make this code work on the lines of how an array index works using this map? I hope you can understand what I am trying to implement.
Iterator for std::map is defined by standard as Bidirectional Iterator. This type of iterators can be incremented(operator ++) and decremented (operator --), but you can't perform mathematical operations on them (mostly because it would take O(n) time rather than O(1))
And again, for error 2, bidirectional iterator does not overload < operator (nor other variations), as it doesn't make sense to have compare operator with O(n) complexity. They are overloaded in random access iterators at the lowest.
To achieve what you wanted, your code can look like this:
#include <iterator> /for std::next() and std::prev()
for(auto i = m.begin; i != m.end(); ++i)
{
auto l = i;
auto r = i;
if (i != m.begin())
l = std::prev(i);
if (i != m.end())
r = std::next(i);
while (l != m.begin() && r != m.end())
{
//make sure you don't use r if it's equal to m.end()
--l;
++r;
}
Map is an associative container. The following containers are defined in the current revision of the C++ standard with associative container: set, map, multiset, multimap.
Associative Containers support bidirectional iterators. Bidirectional iterators are iterators that can be used to access the sequence of elements in a range in both directions (towards the end and towards the beginning). They are similar to forward iterators, except that they can move in the backward direction also, unlike the forward iterators, which can move only in forward direction.
Bidirectional iterators support following operations:
Is default-constructible, copy-constructible, copy-assignable and
destructible X a; X b(a); b = a;
Can be compared for equivalence using the equality/inequality
operators (meaningful when both iterator values iterate over the same
underlying sequence). a == b a != b
Can be dereferenced as an rvalue (if in a dereferenceable state).
*a a->m
For mutable iterators (non-constant iterators): Can be dereferenced
as an lvalue (if in a dereferenceable state). *a = t
Can be incremented (if in a dereferenceable state). The result is
either also dereferenceable or a past-the-end iterator. Two iterators
that compare equal, keep comparing equal after being both increased.
++a a++ *a++
Can be decremented (if a dereferenceable iterator value precedes it).
--a a-- *a--
So, + and - is not defined for it, which leads to the error1.
And so is >=, <=, >, < not defined for it, which leads to error2.
The problem is iterator doesn't work like array indices. The elements of map will be stored at different locations in memory and there is no guarantee that the between elements i and j in map, if i comes before j, then it will be stored in memory just before j. The values are not stored at consecutive locations in memory in case of map.
The ++ operator is overloaded for iterators and it will give the proper location of the next element.
Also, if you consider the points above, then comparing two iterators makes no sense because the fact that one iterator comes after another doesn't give us any important information regarding the corresponding values that will be accessed using these two iterators.
there are different iterator categories/concepts: Iterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator and ContiguousIterator. They differ in available operations. Simple iterators only support step forward (operator ++), dereference (operator *) and inequality comparison (operator !=). This is the required minimum for range-based for loop. std::map::iterator is BidirectionalIterator - it doesn't support arithmetic or comparison operators.
Solving the x-y problem: I want to do "this", I wrote code that does "that"
So I presume you really do want to have access to the previous element in the map, and the next element. I presume you only want this when those 2 elements are "good"
You could recast your loop to cascade the knowledge of the 3 neighbouring iterators:
for(auto r = m.begin(), e= m.end(), i= e, l= e; r != e; l =i, i =r, r++)
{
if (l != e)
{
// All 3 iterators are good here
}
}
I am bit confused about the difference between the usage of std::remove algorithm. Specifically I am not able to understand what is being removed when I use this algorithm. I wrote a small test code like this:
std::vector<int> a;
a.push_back(1);
a.push_back(2);
std::remove(a.begin(), a.end(), 1);
int s = a.size();
std::vector<int>::iterator iter = a.begin();
std::vector<int>::iterator endIter = a.end();
std::cout<<"Using iter...\n";
for(; iter != endIter; ++iter)
{
std::cout<<*iter<<"\n";
}
std::cout<<"Using size...\n";
for(int i = 0; i < a.size(); ++i)
{
std::cout<<a[i]<<"\n";
}
The output was 2,2 in both the cases.
However, if I use erase with the remove something like this:
a.erase(std::remove(a.begin(), a.end(), 1), a.end());
I get the output as 2.
So my questions are:
(1). Is there any use of std::remove other than using it with erase function.
(2). Even after doing std::remove, why a.size() returns 2 and not 1?
I read the item in Scott Meyer's Effective STL book about the erase-remove idiom. But am still having this confusion.
remove() doesn't actually delete elements from the container -- it only shunts non-deleted elements forwards on top of deleted elements. The key is to realise that remove() is designed to work on not just a container but on any arbitrary forward iterator pair: that means it can't actually delete the elements, because an arbitrary iterator pair doesn't necessarily have the ability to delete elements.
For example, pointers to the beginning and end of a regular C array are forward iterators and as such can be used with remove():
int foo[100];
...
remove(foo, foo + 100, 42); // Remove all elements equal to 42
Here it's obvious that remove() cannot resize the array!
What does std::remove do?
Here's pseudo code of std::remove. Take few seconds to see what it's doing and then read the explanation.
Iter remove(Iter start, Iter end, T val) {
Iter destination = start;
//loop through entire list
while(start != end) {
//skip element(s) to be removed
if (*start == val) {
start++;
}
else //retain rest of the elements
*destination++ = *start++;
}
//return the new end of the list
return destination;
}
Notice that remove simply moved up the elements in the sequence, overwriting the values that you wanted to remove. So the values you wanted to remove are indeed gone, but then what's the problem? Let say you had vector with values {1, 2, 3, 4, 5}. After you call remove for val = 3, the vector now has {1, 2, 4, 5, 5}. That is, 4 and 5 got moved up so that 3 is gone from the vector but the size of vector hasn't changed. Also, the end of the vector now contains additional left over copy of 5.
What does vector::erase do?
std::erase takes start and end of the range you want to get rid off. It does not take the value you want to remove, only start and end of the range. Here's pseudo code for how it works:
erase(Iter first, Iter last)
{
//copy remaining elements from last
while (last != end())
*first++ = *last++;
//truncate vector
resize(first - begin());
}
So the erase operation actually changes the size of container and frees up the memory.
The remove-erase idiom
The combination of std::remove and std::erase allows you to remove matching elements from the container so that container would actually get truncated if elements were removed. Here's how to do it:
//first do the remove
auto removed = std::remove(vec.begin(), vec.end(), val);
//now truncate the vector
vec.erase(removed, vec.end());
This is known as the remove-erase idiom. Why is it designed like this? The insight is that the operation of finding elements is more generic and independent of underlying container (only dependent on iterators). However operation of erase depends on how container is storing memory (for example, you might have linked list instead of dynamic array). So STL expects containers to do its own erase while providing generic "remove" operation so all containers don't have to implement that code. In my view, the name is very misleading and std::remove should have been called std::find_move.
Note: Above code is strictly pseudocode. The actual STL implementation is more smarter, for example, using std::move instead of copy.
std::remove does not remove the actual objects, rather, pushes them to the end of the container. Actual deletion and deallocation of memory is done via erase. So:
(1). Is there any use of std::remove other than using it with erase function.
Yes, it helps to get a pair of iterators to a new sequence without having worry about proper de-allocation etc.
(2). Even after doing std::remove, why a.size() returns 2 and not 1?
The container still holds to those objects, you only have a new set of iterators to work with. Hence the size is still what it used to be.
i faced the same issue, trying to understand the difference.
the explanations that have been give so far are right on the money, but i only understood them after seeing an example;
#include <algorithm>
#include <string>
#include <iostream>
#include <cctype>
int main()
{
std::string str1 = "Text with some spaces";
std::string::iterator it = remove(str1.begin(), str1.end(), 't');
std::cout << str1 << std::endl;// prints "Tex wih some spaceses"
for (str1.begin();it != str1.end(); ++it)
{
std::cout << *it; //prints "es"
}
}
as you can see, the remove, only moves the lower case 't' to the end of the string, while returning a new iterator to the end of the new string (new string is the old string up to where the removed element are inserted)
this is why when you print the iterator that you got from "remove"
"Text with some spaces"
^ ^removes both 't', then shift all elements forward -1 //what we want to remove
"Text with some spaces"
^ end of string -2 //original state of string
"Tex with some spacess"
^end of string -3 //first 't' removed
"Tex wih some spaceses"
^end of string -4 //second 't' removed
"Tex wih some spaceses"
^new iterator that remove() returned -5 // the state of string after "remove" and without "erase"
if you pass the iterator you obtained from step 5 to "erase()" it will know to erase from there to the end of string re-sizing the string in process
To remove element with some condition(equal some value or other condition like less than) in container like vector, it always combine function member function erase and std::remove or std::remove_if.
In vector, the function erase can just delete element by position, like:
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
But if you want to erase elements with some condition, you can combine it with std::remove or std::remove_if.
For example, you want to erase all the elements 6 in the below vector:
std::vector<int> vec{6, 8, 10, 3, 4, 5, 6, 6, 6, 7, 8};
// std::remove move elements and return iterator for vector erase funtion
auto last = std::remove(vec.begin(), vec.end(), 6);
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 6 6 7 8
vec.erase(last, vec.end());
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8
std::remove works as below, it does't erase any elements, it just move elements and returns the iterator.
Possible implementation:
template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
first = std::find(first, last, value);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!(*i == value))
*first++ = std::move(*i);
return first;
}
Conclusion:
If you want to remove elements with some condition, you use vector::iterator erase (iterator first, iterator last); essentially.
First get range start:
auto last = std::remove(vec.begin(), vec.end(), equal_condition_value);
erase by range(always with end())
vec.erase(last, vec.end());
cited:
https://en.cppreference.com/w/cpp/algorithm/remove
Simplest I can come up with:
erase() is something you can do to an element in a container. Given an iterator/index into a container, erase( it ) removes the thing the iterator refers to from the container.
remove() is something you can do to a range, it re-arranges that range but doesn't
erase anything from the range.
remove doesn't "really" remove
anything, because it can't.
In order to "actually" remove the elements from container you need to access container APIs. Where as remove works only with iterators irrespective of what containers those iterators points to. Hence, even if remove wants an "actual remove", it can't.
Remove overwrite "removed" elements by the following elements that were not removed and then it is up to the caller to decide to use the returned new logical end instead of the original end.
In your case remove logically removed 1 from vector a but size remained to 2 itself. Erase actually deleted the elements from vector. [ from vector new end to old end ]
The main idea of remove is it cannot change the number of elements and it just remove elements from a range as per criteria.
Say that I have the following example using a set in c++:
set <int> a;
for (int i = 0; i <10; i++){
//Assume i is a random number
a.insert(i);
}
How can you find the maximum and minimum values for the set example shown above? Ideally I thought that the following would work but it gives the following error:
error: cannot convert 'std::_Rb_tree_const_iterator<int>' to 'int' in assignment
I'm using the following functions to try getting max/min:
min = a.begin();
max = a.end();
First of all, begin and end return iterators, which you need to perform indirection on (*) to get the element they point at.
Secondly, end returns the past-the-end iterator, so doesn't actually refer to the last element. You can instead use the reverse begin iterator.
min = *a.begin();
max = *a.rbegin();
a.begin() and a.end() are iterators, not elements. Use
min = *a.begin();
to receive min element and
max = *a.rbegin();
to receive max.
max = *a.end();
will not work because it points on the next element after the last one. So it will return garbage.
As others have said, an iterator (as the one returned by begin() or rbegin()) must be dereferenced to retrieve the value it points to.
But, before dereferencing, the iterator must be checked for validity. For example, on an empty vector begin() will return an out-of-range iterator (pointing one position past the last).
So, a more cautious way would be:
// assuming a is a vector<int>
vector<int>::const_iterator p = a.cbegin(); // or use auto to hide iterator type
int min_val = p != a.cend()? (*p) : (INT_MAX); // if empty, yield a special value like INT_MAX
I have a std::vector<T> variable. I also have two variables of type T, the first of which represents the value in the vector after which I am to insert, while the second represents the value to insert.
So lets say I have this container: 1,2,1,1,2,2
And the two values are 2 and 3 with respect to their definitions above. Then I wish to write a function which will update the container to instead contain:
1,2,3,1,1,2,3,2,3
I am using c++98 and boost. What std or boost functions might I use to implement this function?
Iterating over the vector and using std::insert is one way, but it gets messy when one realizes that you need to remember to hop over the value you just inserted.
This is what I would probably do:
vector<T> copy;
for (vector<T>::iterator i=original.begin(); i!=original.end(); ++i)
{
copy.push_back(*i);
if (*i == first)
copy.push_back(second);
}
original.swap(copy);
Put a call to reserve in there if you want. You know you need room for at least original.size() elements. You could also do an initial iteraton over the vector (or use std::count) to determine the exact amount of elements to reserve, but without testing, I don't know whether that would improve performance.
I propose a solution that works in place and in O(n) in memory and O(2n) time. Instead of O(n^2) in time by the solution proposed by Laethnes and O(2n) in memory by the solution proposed by Benjamin.
// First pass, count elements equal to first.
std::size_t elems = std::count(data.begin(), data.end(), first);
// Resize so we'll add without reallocating the elements.
data.resize(data.size() + elems);
vector<T>::reverse_iterator end = data.rbegin() + elems;
// Iterate from the end. Move elements from the end to the new end (and so elements to insert will have some place).
for(vector<T>::reverse_iterator new_end = data.rbegin(); end != data.rend() && elems > 0; ++new_end,++end)
{
// If the current element is the one we search, insert second first. (We iterate from the end).
if(*end == first)
{
*new_end = second;
++new_end;
--elems;
}
// Copy the data to the end.
*new_end = *end;
}
This algorithm may be buggy but the idea is to copy only once each elements by:
Firstly count how much elements we'll need to insert.
Secondly by going though the data from the end and moving each elements to the new end.
This is what I probably would do:
typedef ::std::vector<int> MyList;
typedef MyList::iterator MyListIter;
MyList data;
// ... fill data ...
const int searchValue = 2;
const int addValue = 3;
// Find first occurence of searched value
MyListIter iter = ::std::find(data.begin(), data.end(), searchValue);
while(iter != data.end())
{
// We want to add our value after searched one
++iter;
// Insert value and return iterator pointing to the inserted position
// (original iterator is invalid now).
iter = data.insert(iter, addValue);
// This is needed only if we want to be sure that out value won't be used
// - for example if searchValue == addValue is true, code would create
// infinite loop.
++iter;
// Search for next value.
iter = ::std::find(iter, data.end(), searchValue);
}
but as you can see, I couldn't avoid the incrementation you mentioned. But I don't think that would be bad thing: I would put this code to separate functions (probably in some kind of "core/utils" module) and - of course - implement this function as template, so I would write it only once - only once worrying about incrementing value is IMHO acceptable. Very acceptable.
template <class ValueType>
void insertAfter(::std::vector<ValueType> &io_data,
const ValueType &i_searchValue,
const ValueType &i_insertAfterValue);
or even better (IMHO)
template <class ListType, class ValueType>
void insertAfter(ListType &io_data,
const ValueType &i_searchValue,
const ValueType &i_insertAfterValue);
EDIT:
well, I would solve problem little different way: first count number of the searched value occurrence (preferably store in some kind of cache which can be kept and used repeatably) so I could prepare array before (only one allocation) and used memcpy to move original values (for types like int only, of course) or memmove (if the vector allocated size is sufficient already).
In place, O(1) additional memory and O(n) time (Live at Coliru):
template <typename T, typename A>
void do_thing(std::vector<T, A>& vec, T target, T inserted) {
using std::swap;
typedef typename std::vector<T, A>::size_type size_t;
const size_t occurrences = std::count(vec.begin(), vec.end(), target);
if (occurrences == 0) return;
const size_t original_size = vec.size();
vec.resize(original_size + occurrences, inserted);
for(size_t i = original_size - 1, end = i + occurrences; i > 0; --i, --end) {
if (vec[i] == target) {
--end;
}
swap(vec[i], vec[end]);
}
}
I need a binary search algorithm that is compatible with the C++ STL containers, something like std::binary_search in the standard library's <algorithm> header, but I need it to return the iterator that points at the result, not a simple boolean telling me if the element exists.
(On a side note, what the hell was the standard committee thinking when they defined the API for binary_search?!)
My main concern here is that I need the speed of a binary search, so although I can find the data with other algorithms, as mentioned below, I want to take advantage of the fact that my data is sorted to get the benefits of a binary search, not a linear search.
so far lower_bound and upper_bound fail if the datum is missing:
//lousy pseudo code
vector(1,2,3,4,6,7,8,9,0) //notice no 5
iter = lower_bound_or_upper_bound(start,end,5)
iter != 5 && iter !=end //not returning end as usual, instead it'll return 4 or 6
Note: I'm also fine using an algorithm that doesn't belong to the std namespace as long as its compatible with containers. Like, say, boost::binary_search.
There is no such functions, but you can write a simple one using std::lower_bound, std::upper_bound or std::equal_range.
A simple implementation could be
template<class Iter, class T>
Iter binary_find(Iter begin, Iter end, T val)
{
// Finds the lower bound in at most log(last - first) + 1 comparisons
Iter i = std::lower_bound(begin, end, val);
if (i != end && !(val < *i))
return i; // found
else
return end; // not found
}
Another solution would be to use a std::set, which guarantees the ordering of the elements and provides a method iterator find(T key) that returns an iterator to the given item. However, your requirements might not be compatible with the use of a set (for example if you need to store the same element multiple times).
You should have a look at std::equal_range. It will return a pair of iterators to the range of all results.
There is a set of them:
http://www.sgi.com/tech/stl/table_of_contents.html
Search for:
lower_bound
upper_bound
equal_range
binary_search
On a separate note:
They were probably thinking that searching containers could term up more than one result. But on the odd occasion where you just need to test for existence an optimized version would also be nice.
If std::lower_bound is too low-level for your liking, you might want to check boost::container::flat_multiset.
It is a drop-in replacement for std::multiset implemented as a sorted vector using binary search.
The shortest implementation, wondering why it's not included in the standard library:
template<class ForwardIt, class T, class Compare=std::less<>>
ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp={})
{
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
first = std::lower_bound(first, last, value, comp);
return first != last && !comp(value, *first) ? first : last;
}
From https://en.cppreference.com/w/cpp/algorithm/lower_bound
int BinarySearch(vector<int> array,int var)
{
//array should be sorted in ascending order in this case
int start=0;
int end=array.size()-1;
while(start<=end){
int mid=(start+end)/2;
if(array[mid]==var){
return mid;
}
else if(var<array[mid]){
end=mid-1;
}
else{
start=mid+1;
}
}
return 0;
}
Example: Consider an array, A=[1,2,3,4,5,6,7,8,9]
Suppose you want to search the index of 3
Initially, start=0 and end=9-1=8
Now, since start<=end; mid=4; (array[mid] which is 5) !=3
Now, 3 lies to the left of mid as its smaller than 5. Therefore, we only search the left part of the array
Hence, now start=0 and end=3; mid=2.Since array[mid]==3, hence we got the number we were searching for. Hence, we return its index which is equal to mid.
Check this function, qBinaryFind:
RandomAccessIterator qBinaryFind ( RandomAccessIterator begin, RandomAccessIterator end, const T & value )
Performs a binary search of the range
[begin, end) and returns the position
of an occurrence of value. If there
are no occurrences of value, returns
end.
The items in the range [begin, end)
must be sorted in ascending order; see
qSort().
If there are many occurrences of the
same value, any one of them could be
returned. Use qLowerBound() or
qUpperBound() if you need finer
control.
Example:
QVector<int> vect;
vect << 3 << 3 << 6 << 6 << 6 << 8;
QVector<int>::iterator i =
qBinaryFind(vect.begin(), vect.end(), 6);
// i == vect.begin() + 2 (or 3 or 4)
The function is included in the <QtAlgorithms> header which is a part of the Qt library.
std::lower_bound() :)
A solution returning the position inside the range could be like this, using only operations on iterators (it should work even if iterator does not arithmetic):
template <class InputIterator, typename T>
size_t BinarySearchPos(InputIterator first, InputIterator last, const T& val)
{
const InputIterator beginIt = first;
InputIterator element = first;
size_t p = 0;
size_t shift = 0;
while((first <= last))
{
p = std::distance(beginIt, first);
size_t u = std::distance(beginIt, last);
size_t m = p + (u-p)/2; // overflow safe (p+u)/2
std::advance(element, m - shift);
shift = m;
if(*element == val)
return m; // value found at position m
if(val > *element)
first = element++;
else
last = element--;
}
// if you are here the value is not present in the list,
// however if there are the value should be at position u
// (here p==u)
return p;
}