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

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());

Related

Erasing duplicates from two vectors using only iterators

How can I delete duplicates from two vectors of strings (delete them from both vectors) using only iterators?
I suppose it doesn't work because if values are already deleted they can't be compared, but I can not think of any other solution, only if I had one function to erase both elements at the same time.
void obrisiIsteRijeci(std::vector<std::string>& v1, std::vector<std::string>& v2){
for(auto it = v1.begin(); it != v1.end(); it++){
auto it1 = it;
for(auto it2 = v2.begin(); it2 != v2.end(); it2++){
if((*(it2) == *(it1)) && (*(it1) == *(it2))){
v1.erase(it1);
v2.erase(it2);
}
}
}
}
I can suggest the following approach. In the demonstration program below I am using vectors of the type std::vector<int> for simplicity.
#include <iostream>
#include <vector>
#include <iterator>
$include <algorithm>
int main()
{
std::vector<int> v1 = { 1, 2, 1, 2, 3, 4 }, v2 = { 1, 2, 3, 5 };
for (auto first = std::begin( v1 ); first != std::end( v1 ); )
{
auto it = std::find( std::begin( v2 ), std::end( v2 ), *first );
if (it != std::end( v2 ))
{
v2.erase( std::remove( it, std::end( v2 ), *first ), std::end( v2 ) );
auto value = *first;
auto offset = std::distance( std::begin( v1 ), first );
v1.erase( std::remove( first, std::end( v1 ), value ), std::end( v1 ) );
first = std::next( std::begin( v1 ), offset );
}
else
{
++first;
}
}
for (const auto &item : v1)
{
std::cout << item << ' ';
}
std::cout << '\n';
for (const auto &item : v2)
{
std::cout << item << ' ';
}
std::cout << '\n';
}
The program output is
4
5

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;
}
}

Comparing iterator with ptrdiff

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

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';

c++ general int array and vector iterator

In the following code, I need to define an iterator which can iterate on both vector<int> and int[100]. How to define mi here?
template<class arraytype>
void array_show(arraytype array, size_t arraySize)
{
// how to define mi????
for (mi = array; array != m.end(); array++)
std::cout << " " << *mi << std::endl;
}
Try the following
#include <iostream>
#include <vector>
#include <iterator>
template<class arraytype>
void array_show( const arraytype &array )
{
for ( const auto &x : array ) std::cout << x << ' ';
std::cout << std::endl;
}
int main()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
std::vector<int> v( std::begin( a ), std::end( a ) );
array_show( a );
std::endl( std::cout );
array_show( v );
std::endl( std::cout );
return 0;
}
The program output is
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
Another approach is to use iterators. For example (Here are shown the both function definitions)
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
template<class arraytype>
void array_show( const arraytype &array )
{
for ( const auto &x : array ) std::cout << x << ' ';
std::cout << std::endl;
}
template <class InputIterator>
void array_show( InputIterator first, InputIterator last )
{
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
std::copy( first, last,
std::ostream_iterator<value_type>( std::cout, " ") );
std::cout << std::endl;
}
int main()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
std::vector<int> v( std::begin( a ), std::end( a ) );
array_show( std::begin( a ), std::end( a ) );
std::endl( std::cout );
array_show( std::begin( v ), std::end( v ) );
std::endl( std::cout );
return 0;
}
The program output is the same as above
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
Instead of the algorithm std::copy you can write a loop yourself. For example
template <class InputIterator>
void array_show( InputIterator first, InputIterator last )
{
for ( ; first != last; ++first )
{
std::cout << *first << ' ';
}
std::cout << std::endl;
}
Define your function to accept the start and the end iterators. You can then use your function for any form of iterables
template<typename Iter>
void array_show(Iter begin, Iter end)
{
// how to define mi????
for (Iter it = begin; it != end; it++)
std::cout << " " << *it << std::endl;
}
You can then call your function as follows
int main(int argc, char *argv[]) {
std::vector<int> vec= { 3, 1, 4, 1, 5, 9, 2, 6 };
int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6 };
array_show(vec.begin(), vec.end());
array_show(std::begin(arr), std::end(arr));
}
In case you would want to pass the array as a reference as well as a vector, you should create two different function overloads to support both. This probably looks like your intention as your function signature intended to accept the size as well as the object.
template<typename Ty, size_t size>
void array_show(Ty(&arr)[size])
{
for (size_t i = 0; i < size; i++)
std::cout << " " << arr[i] << std::endl;
}
and subsequently call it as
int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6 };
array_show(arr);
If you need to use it the way you showed:
template<class arraytype>
void array_show(arraytype array, size_t arraySize)
{
auto end = &(array[arraySize]);
for (mi = &(array[0]); array != end; ++array)
std::cout << " " << *mi << std::endl;
}
As vector has defined operator[], construct &(array[...]) will work for both vector and plain array.
But preferably:
template <class Iter>
void array_show(Iter begin, Iter end)
{
for (Iter it = begin, it != end; ++it)
std::cout << " " << *it << std::endl;
}
and then:
vector<int> vi;
int ai[100];
//fill with data
array_show(vi.begin(). vi.end());
array_show(ai, ai + 100);
Here is an example below. The importantpart is to use arraytype& - a reference, this way regular array is not decayed to pointer - this way it is possible to read its size inside array_show.
template<class arraytype>
void array_show(arraytype& array, size_t arraySize)
{
// how to define mi????
for (auto mi = std::begin(array); mi != std::end(array); mi++)
std::cout << " " << *mi << std::endl;
}