Comparing iterator with ptrdiff - c++

Looking at the following code
template <typename Itr>
constexpr auto foo(Itr first, Itr last)
{
for (; first != std::prev(last); std::advance(first, 1))
{
for (auto j{ first }; j != (std::prev(last) - first); std::advance(j, 1)) // Error here
{
// Do stuff
}
}
}
In the second for loop I get the error:
no operator found which takes a right-hand operand of type '__int64' (or there is no acceptable conversion)
I am not allowed to compare the iterator with a ptrdiff_t apperently. So how can I accomplish this task? I have tried using every cast available, to both j and the ptrdiff_t - but nothing is allowed.
The reason I need this, is because I only want the inner loop to iterate over a smaller subset of the container for each iteration of the outer loop.
An example where I need this is in the implementation of the bubble sort algorithm
template <typename Itr>
constexpr auto bubble_sort(Itr first, Itr last)
{
for (; first != std::prev(last); std::advance(first, 1))
{
auto swapped{ false };
for (auto j{ first }; j != (std::prev(last) - first); std::advance(j, 1))
{
if (*j > *std::next(j)) // Pred
{
std::iter_swap(j, std::next(j));
swapped = true;
}
}
if (!swapped) break;
}
}

If you want to write the method bubble_sort for forward iterators then it can look the following way as it is shown in the demonstrative program below. Neither ptrdiff_t is required.
Here you are.
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>
template <typename ForwardIterator>
void bubble_sort( ForwardIterator first, ForwardIterator last )
{
for ( auto sorted = last; first != last && std::next( first ) != last; last = sorted )
{
for ( auto next = sorted = first, prev = next++; next != last; ++next, ++prev )
{
if ( *next < *prev )
{
std::iter_swap( prev, next );
sorted = next;
}
}
}
}
template <typename ForwardIterator, typename Comparison>
void bubble_sort( ForwardIterator first, ForwardIterator last, Comparison cmp )
{
for ( auto sorted = last; first != last && std::next( first ) != last; last = sorted )
{
for ( auto next = sorted = first, prev = next++; next != last; ++next, ++prev )
{
if ( cmp( *next, *prev ) )
{
std::iter_swap( prev, next );
sorted = next;
}
}
}
}
int main()
{
const size_t N = 10;
int a[N];
std::srand( ( unsigned int )std::time( nullptr ) );
for ( auto &item : a ) item = std::rand() % N;
for ( const auto &item : a ) std::cout << item << ' ';
std::cout << '\n';
bubble_sort( std::begin( a ), std::end( a ) );
for ( const auto &item : a ) std::cout << item << ' ';
std::cout << '\n';
bubble_sort( std::begin( a ), std::end( a ), std::greater<>() );
for ( const auto &item : a ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
The program output might look like
1 5 4 4 0 9 7 3 3 1
0 1 1 3 3 4 4 5 7 9
9 7 5 4 4 3 3 1 1 0

Related

Remove duplicate elements from std::vector but starting from the front?

How can I delete duplicate elements from a vector but starting from the front?
So
2 3 4 5 2 5 would become 3 4 2 5
1 5 3 1 would become 5 3 1
I would like the solution to be easy to read, and it would be nice if it had good performance as well.
If a container supports bidirectional iterators then it is unimportant from which side of the container you are trying to remove duplicate elements because you can use reverse iterators.
Here is a demonstrative program.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
template <typename ForwardIterator>
ForwardIterator remove_duplicates( ForwardIterator first, ForwardIterator last )
{
for ( ; first != last; ++first )
{
last = std::remove( std::next( first ), last, *first );
}
return last;
}
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5, 4, 3, 2, 1 };
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
v.erase( remove_duplicates( std::begin( v ), std::end( v ) ), std::end( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
std::cout << '\n';
v.assign( { 1, 2, 3, 4, 5, 4, 3, 2, 1 } );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
v.erase( std::begin( v ), remove_duplicates( std::rbegin( v ), std::rend( v ) ).base() );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
}
The program output is
1 2 3 4 5 4 3 2 1
1 2 3 4 5
1 2 3 4 5 4 3 2 1
5 4 3 2 1
An asymptotically efficient algorithm (N log N):
Create a range of iterators each pointing to the original container
Sort the iterators with a stable sort. Use a comparison function that indirects through the iterator.
Remove consecutive duplicates (std::unique) using reverse iterator and similar custom comparison function.
std::remove_if from the vector with a predicate function that removes elements whose iterator isn't in the secondary container.
It's a bit more complex than the O(N*N) solution. Here is an example implementation. I cannot guarantee that it is correct:
template<class It, class Sentinel>
auto remove_duplicates(It first, Sentinel last)
{
std::vector<It> iterators;
auto iterator_generator = [it = first]() mutable {
return it++;
};
std::generate_n(
std::back_inserter(iterators),
last - first,
iterator_generator);
auto iterator_compare = [](const auto& l, const auto& r) {
return *l < *r;
};
std::stable_sort(
iterators.begin(),
iterators.end(),
iterator_compare);
auto iterator_eq = [](const auto& l, const auto& r) {
return *l == *r;
};
auto last_unique = std::unique(
iterators.begin(),
iterators.end(),
iterator_eq);
iterators.erase(last_unique, iterators.end());
auto keep_generator = [it = first]() mutable {
return it++;
};
std::vector<bool> remove(last - first, true);
for(auto it : iterators) {
auto index = it - first;
remove[index] = false;
}
auto remove_predicate = [index = 0, remove = std::move(remove)](const auto& el) mutable {
return remove[index++];
};
return std::remove_if(first, last, std::move(remove_predicate));
}
// usage with reverse iterators
v.erase(
v.rend().base(),
remove_duplicates(v.rbegin(), v.rend()).base());

How do I get the first layer index of 2D vector?

I need help getting the first layer index of a 2D vector. Each element is unique, so there are no repetitions of element. Here's what I mean bellow.
I have a vector defined as:
vector<vector<int>> vec { {0,1} ,
{2} ,
{3,4},
{5,6} }
Then, I want to get the index of where any of the numbers is, on the "first" layer.
By this, I mean if I say
index of 4, it should return 2.
If I say, index of 6, it should return 3.
Thank you in advance!
You can use std::find and std::find_if:
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<std::vector<int>> vec { {0,1} ,
{2} ,
{3,4},
{5,6} };
// Search for the 4
auto iter = std::find_if(vec.begin(), vec.end(), [](const std::vector<int>& v)
{return std::find(v.begin(), v.end(), 4) != v.end();});
// Output the distance between the item and the beginning of the vector
std::cout << std::distance(vec.begin(), iter);
}
Output:
2
The outer std::find_if searches the std::vector<vector<int>> and the argument to the lambda will be a reference to each inner vector. The inner std::find searches that inner vector for the value.
You could write a function that calculates the index like:
int findIndex(const std::vector<std::vector<int>> &vec, int val)
{
auto it = std::find_if(vec.cbegin(), vec.cend(), [val](const std::vector<int> &v) {
return std::find(v.cbegin(), v.cend(), val) != v.cend();
});
return it != vec.cend() ? std::distance(vec.cbegin(), it) : -1;
}
You can use the standard algorithm std::find_if along with the algorithm std::find.
Here is a demonstrative program.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<std::vector<int>> v =
{
{ 0, 1 }, { 2 }, { 3, 4 }, { 5, 6 }
};
auto present = []( const auto &v, const auto &value )
{
return std::find( std::begin( v ), std::end( v ), value ) != std::end( v );
};
int value = 4;
size_t i = std::distance( std::begin( v ),
std::find_if( std::begin( v ), std::end( v ),
[&, present, value]( const auto &item )
{
return present( item, value );
} ) );
if ( i != v.size() ) std::cout << value << ": " << i << '\n';
value = 6;
i = std::distance( std::begin( v ),
std::find_if( std::begin( v ), std::end( v ),
[&, present, value]( const auto &item )
{
return present( item, value );
} ) );
if ( i != v.size() ) std::cout << value << ": " << i << '\n';
return 0;
}
The program output is
4: 2
6: 3
You can use a hash-table data structure like unordered_map to do this in O(1) time.
unordered_map <int,int> m;
for(int i=0;i<vec.size();i++){
for(int j=0;j<vec[i].size();j++){
m[vec[i][j]] = i;
}
}

remove duplicates from sorted array, same solution with different code way has different output

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "hello_world.h"
using namespace std;
class Solution1 {
public:
int removeDuplicates(vector<int>& nums) {
return distance(nums.begin(), removeDuplicates(nums.begin(), nums.end(), nums.begin()));
}
template<typename InIt, typename OutIt>
OutIt removeDuplicates(InIt begin, InIt end, OutIt output){
while(begin != end){
*output++ = *begin;
begin = upper_bound(begin, end, *begin);
}
return output;
}
};
class Solution2 {
public:
int removeDuplicates(vector<int>& nums) {
vector<int>::iterator output = nums.begin();
while(nums.begin() != nums.end()){
*output++ = *nums.begin();
nums.begin() = upper_bound(nums.begin(), nums.end(), *nums.begin());
}
return distance(nums.begin(), output);
}
};
int main()
{
//helloworld test;
//test.print();
int num[3] = {1,1,2};
vector<int> nums(num, num + 3);
Solution2 so;
int a = so.removeDuplicates(nums);
cout<<a<<endl;
return 0;
}
In main function, when i use the class solution1, the code can remove duplicates numbers from the arrary [1 1 2] ,to output [1 2]. In order to simplify the code, I changed the solution1 to solution2, but the solution2 can not execute right output, anybody know the reason?
In this while loop
while(nums.begin() != nums.end()){
*output++ = *nums.begin();
nums.begin() = upper_bound(nums.begin(), nums.end(), *nums.begin());
}
you are always using the iterator nums.begin() in the condition and in this statement
*output++ = *nums.begin();
because this statement
nums.begin() = upper_bound(nums.begin(), nums.end(), *nums.begin());
does not change the iterator returned by a new call of nums.begin().
You need to introduce a variable of the iterator type before the loop like
auto it = nums.begin();
while( it != nums.end()){
*output++ = *it;
it = upper_bound( it, nums.end(), *it );
}
Here is a demonstrative program
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<int> v = { 1, 2 };
size_t i = 0;
while ( v.begin() != v.end() )
{
v.begin() = std::upper_bound( v.begin(), v.end(), *v.begin() );
if ( ++i == 10 ) break;
}
std::cout << "i = " << i << '\n';
i = 0;
auto it = v.begin();
while ( it != v.end() )
{
it = std::upper_bound( it, v.end(), *it );
if ( ++i == 10 ) break;
}
std::cout << "i = " << i << '\n';
return 0;
}
Its output is
i = 10
i = 2
To erase duplicates after the loop use the member function erase like
nums.erase( output, nums.end() );
The same can be done using the standard algorithm std::unique. For example
nums.erase( std::unique( nums.begin(), nums.end() ), nums.end() );

Writing a custom insert function for std::vector

I was writing the insert function for std::vector for my personal project.
Vector layout before insert:
position : 0 1 2 3 4
Value : a b c e f
Assuming there is enough capacity, I want to insert 'd' at position 3.
Vector layout after insert:
position : 0 1 2 3 4 5
Value : a b c d e f
I wrote a function to shift the values to the right after the given insert position (in the example it is 3) and then I assign the given value at the requested insert position.
The function I wrote to shift_right is as follows:
template <typename T>
void vector<T>::shift_right(typename vector<T>::iterator given_pos) {
for (auto iter = end() - 1; iter != given_pos - 1; iter--) {
*(iter + 1) = *iter;
}
}
Is there a std::algorithm, or variation of it that can help me get rid of my raw loop in the shift_right function?
You can use std::move_backward:
template< class BidirIt1, class BidirIt2 ><br>
BidirIt2 move_backward( BidirIt1 first, BidirIt1 last, BidirIt2 d_last );
Moves the elements from the range [first, last), to another range ending at d_last. The elements are moved in reverse order (the last element is moved first), but their relative order is preserved.
So your code might look like this (not tested):
template <typename T>
void vector<T>::shift_right(typename vector<T>::iterator given_pos) {
std::move_backward(given_pos, end()-2, end()-1);
}
This assumes that the capacity has already been increased if necessary and that end() returns the new end iterator (i.e. one past the last element after the new space was inserted). Change to std::move_backward(given_pos, end()-1, end()); if that's not the case.
It seems you mean something like the following
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
template <typename T>
typename std::vector<T>::iterator
shift_right( std::vector<T> &v, typename std::vector<T>::size_type pos )
{
v.resize( v.size() + 1 );
typename std::vector<T>::iterator result = std::end( v );
if ( pos < v.size() )
{
result = std::copy_backward( std::next( std::begin( v ), pos ),
std::prev( std::end( v )),
std::end( v ) );
}
return std::prev( result );
}
int main()
{
std::vector<int> v;
auto it = shift_right( v, v.size( ) );
*it = 2;
for ( const auto &item : v ) std::cout << item << ' ';
std::cout <<'\n';
it = shift_right( v, v.size() );
*it = 3;
for ( const auto &item : v ) std::cout << item << ' ';
std::cout <<'\n';
it = shift_right( v, 0 );
*it = 0;
for ( const auto &item : v ) std::cout << item << ' ';
std::cout <<'\n';
it = shift_right( v, 1 );
*it = 1;
for ( const auto &item : v ) std::cout << item << ' ';
std::cout <<'\n';
return 0;
}
The program output is
2
2 3
0 2 3
0 1 2 3
Pay attention to that it is better to use std::copy_backward instead of std::move_backward because in the first case the state of all elements of the vector will be consistent similarly to elements of an array of fundamental types after shifting them.
If to use std::move_backward then the corresponding function can look the following way as it is shown in the demonstrative program below.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
template <typename T>
typename std::vector<T>::iterator
shift_right( std::vector<T> &v, typename std::vector<T>::size_type pos )
{
v.resize( v.size() + 1 );
typename std::vector<T>::iterator result = std::end( v );
if ( pos < v.size() )
{
result = std::move_backward( std::next( std::begin( v ), pos ),
std::prev( std::end( v )),
std::end( v ) );
}
result = std::prev( result );
*result = T();
return result;
}
int main()
{
std::vector<int> v = { 1, 2, 4, 5, 6 };
for ( const auto &item : v ) std::cout << item << ' ';
std::cout <<'\n';
auto it = shift_right( v, 2 );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout <<'\n';
*it = 3;
for ( const auto &item : v ) std::cout << item << ' ';
std::cout <<'\n';
return 0;
}
The program output is
1 2 4 5 6
1 2 0 4 5 6
1 2 3 4 5 6
I would first define a convenience function template, right_shift_by_one(), that shifts the elements to the right by one position in the valid iterator range [first, last):
template<typename BidirIt>
void right_shift_by_one(BidirIt first, BidirIt last) {
std::move_backward(first, std::prev(last), last);
}
Then, your custom vector's member function, shift_right(), which eventually calls the convenience function defined above:
template<typename T>
auto shift_right(typename vector<T>::iterator pos) {
auto dist = std::distance(begin(), pos);
// this only compiles if T is default constructible
resize(size()+1);
// resize() may have invalidated the pos iterator due to vector's reallocation
// recompute it
pos = begin() + dist;
right_shift_by_one(pos, end());
return pos;
}
This member function returns an iterator pointing to the element corresponding to the new space created by right shifting. This works even if the vector doesn't have enough capacity because the invalidation of iterators on reallocation has been taken into account. You have to implement resize() in your custom vector implementation, though.
Use:
auto it = shift_right(vec.begin() + 2);
*it = 'c';

STL template function to return a pair

I'm trying to write a function that returns a pair of values from an STL container.
template <typename T>
std::pair<typename T::value_type,typename T::value_type> getMinMax(T &container) {
auto min = *(container.begin());
auto max = *(container.begin());
for (auto it : container) {
if (min > (*it) ) {
min = (*it);
}
if (max < (*it) ) {
max = (*it);
}
}
return std::make_pair(min, max);
};
int main() {
std::vector<int> c{1, 2, 3, 4, 5};
auto p = getMinMax(c);
std::cout << "min: " << p.first << " max: " << p.second << "\n";
}
I'm getting an error:
error: indirection requires pointer operand ('int' invalid)
if (min > (*it) ) {
I don't know how to deal with that.
Besides that error, is there a better way to implement the desired behavior?
for range returns element, not iterator. So your loop should be something like:
for (const auto& e : container) {
if (min > e) {
min = e;
}
if (max < e) {
max = e;
}
}
template <typename T>
std::pair<typename T::value_type, typename T::value_type> getMinMax(T &container) {
auto min = *(container.begin());
auto max = *(container.begin());
for (const auto& element : container) { /* ERROR WAS HERE, FOR RANGE LOOPS RETURN AN ELEMENT */
if (min > element) {
min = element;
}
if (max < element) {
max = element;
}
}
return std::make_pair(min, max);
};
Hey! This should work, you were setting min and max to a dereferenced element, which of course, isn't what we want. :)
Also, you can get undefined behavior with this, for example, if the container was empty. Perhaps you should add some checks that check for that.
For starters the function can have undefined behavior in case when the container is empty because there can be an attempt of dereferencing the iterator of an empty container.
In loops like this
for (auto it : container) {
if (min > (*it) ) {
min = (*it);
}
there is incorrectly used dereferencing.
You could use the standard algorithm std::minmax_element. However it does not do the same as your code. It returns the first minimum element and the last maximum element. So you should rewrite the algorithm std::minmax_element such a way that ir would return the first minimum element (the iterator pointing to the first minimum element) and the first maximum element (the iterator pointing to the first maximum element).
The function can be defined for example the following way
#include <iostream>
#include <utility>
#include <vector>
#include <iterator>
template <typename T>
auto getMinMax( T &container )
-> std::pair<decltype( container.begin() ), decltype( container.begin() )>
{
auto min = container.begin();
auto max = container.begin();
if ( !container.empty() )
{
for ( auto it = container.begin(); ++it != container.end(); )
{
if ( *it < *min ) min = it;
else if ( *max < *it ) max = it;
}
}
return { min, max };
}
int main()
{
std::vector<int> v = { 5, 2, 3, 7, 1, 4, 9, 8, 6 };
auto minmax = getMinMax( v );
std::cout << "Minimum = " << *minmax.first
<< " at position " << std::distance( v.begin(), minmax.first )
<< " and maximum = " << *minmax.second
<< " at position " << std::distance( v.begin(), minmax.second )
<< std::endl;
return 0;
}
The program output is
Minimum = 1 at position 4 and maximum = 9 at position 6