bubble sort implementation in c++ - c++

the code below is used to implement bubble sort. why is template used in this case? and what is the purpose of swapped variabe. even if i remove swapped variable and swapped condition from loop code still works fine
#include <algorithm>
#include <iostream>
#include <iterator>
template <typename RandomAccessIterator>
void bubble_sort(RandomAccessIterator begin, RandomAccessIterator end) {
bool swapped = true;
while (begin != end-- && swapped) {
swapped = false;
for (auto i = begin; i != end; ++i) {
if (*(i + 1) < *i) {
std::iter_swap(i, i + 1);
swapped = true;
}
}
}
}
int main() {
int a[] = {100, 2, 56, 200, -52, 3, 99, 33, 177, -199};
bubble_sort(std::begin(a), std::end(a));
copy(std::begin(a), std::end(a), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
}
ANOTHER IMPLEMENTATION:
template<typename Iterator>
void bubbleSort(Iterator first, Iterator last)
{
Iterator i, j;
for (i = first; i != last; i++)
for (j = first; j < i; j++)
if (*i < *j)
std::iter_swap(i, j); // or std::swap(*i, *j);
}

The template is there simply for convenience of using the function with different types. Implementing the function to use iterators and not a simple array saves you a lot of trouble with pointers and sizes.
The swapped variable indicates to the algorithm that the last run from begin to the end did not result in any swaps. This means that the array is sorted already in this range (and past the end it's sorted as well because that's what was handled in the previous passes) and there is no need to loop until the begin and end iterator are the same. The algorithm will work if you remove this check but could result in a time waste in case of partially sorted arrays.
Let's look at this example:
0: {1 2 5 3 4} (begin = 0, end = 4)
1: {1 2 3 4 5} (begin = 0, end = 3)
2: {1 2 3 4 5} (begin = 0, end = 2)
3: {1 2 3 4 5} (begin = 0, end = 1)
4: {1 2 3 4 5} (begin = 0, end = 0)
You can see that after 0: the array is already sorted, but without the swapped flag the algorithm would not break and continue checking. If the flag is there after 1: the swapped flag is false and the algorithm quits.

Different containers have their own iterator types. For example for one-dimensional arrays there are used pointers as iterators while for objects of the type std::vector there are used iterators defined in this template class.
The variable swapped is used as a criteria whether the elements are already sorted. If there was not swapping of elements of the sequence when it was traversed then it means that the sequence is already sorted.
Take into account that the implementation you showed has undefined behavior due to this statement
while (begin != end-- && swapped) {
^^^^
because there is an attempt to decrement the last iterator when the range can be empty. So the implementation is incorrect.
Moreover the algorithm is not efficient. For example a tail of the array can be already sorted after some iteration of the internal loop. However in the external loop the last iterator is moved left only one position.
It is enough to use forward iterators for the bubble sort. In this case you can use the algorithm even with std::forward_list and others containers that do not have random access iterators.
Here is a demonstrative program that shows how the algorithm can be implemented using forward iterators.
#include <iostream>
#include <algorithm>
#include <iterator>
#include <forward_list>
template <typename ForwardIterator>
void bubble_sort( ForwardIterator first, ForwardIterator last )
{
for ( ForwardIterator sorted = first; first != last; last = sorted )
{
sorted = first;
for ( ForwardIterator current = first, prev = first; ++current != last; ++prev )
{
if ( *current < *prev )
{
std::iter_swap( current, prev );
sorted = current;
}
}
}
}
int main()
{
int a[] = { 100, 2, 56, 200, -52, 3, 99, 33, 177, -199 };
bubble_sort( std::begin( a ), std::end( a ) );
std::copy( std::begin( a ), std::end( a ),
std::ostream_iterator<int>( std::cout, " " ) );
std::cout << "\n";
std::forward_list<int> lst = { 100, 2, 56, 200, -52, 3, 99, 33, 177, -199 };
bubble_sort( std::begin( lst ), std::end( lst ) );
std::copy( std::begin(lst ), std::end( lst ),
std::ostream_iterator<int>( std::cout, " " ) );
std::cout << "\n";
}
The program output is
-199 -52 2 3 33 56 99 100 177 200
-199 -52 2 3 33 56 99 100 177 200
Here in the program there are used an array and an object of the type std::forward_list and the algorithm can be applied to the both containers.

Related

Arranging odd and even numbers in a vector C++

I have this problem: Given a vector with n numbers, sort the numbers so that the even ones will be on odd positions and the odd numbers will be on even positions. E.g. If I have the vector 2 6 7 8 9 3 5 1, the output should be 2 7 6 9 8 3 5 1 . The count should start from 1. So on position 1 which is actually index 0 should be an even number, on position 2 which is actually index 1 should be an odd number and so on. Now this is easy if the odd and even numbers are the same, let's say 4 even number and 4 odd numbers in the vector, but what if the number of odd numbers differs from the number of even numbers like in the above example? How do I solve that. I attached the code with one of the tries I did, but it doesn't work. Can I get some help please. I ask you to keep it simple that means only with vectors and such. No weird methods or anything cause I'm a beginner and I only know the basics. Thanks in advance!
I have to mention that n initial is globally declared and is the number of vector elements and v_initial is the initial vector with the elements that need to be rearranged.
The task says to add the remaining numbers to the end of the vector. Like if there are 3 odd and 5 even numbers, The 2 extra even numbers should be thrown at the end of the vector
void vector_pozitii_pare_impare(int v_initial[])
{
int v_pozitie[50],c1=0,c2=1;
for (i = 0; i < n_initial; i++)
{
if (v_initial[i] % 2 == 0)
{
bool isTrue = 1;
for (int k = i + 1; k < n_initial; k++)
{
if (v_initial[k] % 2 != 0)
isTrue = 0;
}
if (isTrue)
{
v_pozitie[c1] = v_initial[i];
c1++;
}
else
{
v_pozitie[c1] = v_initial[i];
c1 += 2;
}
}
else
{
bool isTrue = 1;
for (int j = i + 1; j < n_initial; j++)
{
if (v_initial[j] % 2 == 0)
{
isTrue = 0;
}
if (isTrue)
{
v_pozitie[c2] = v_initial[i];
c2++;
}
else
{
v_pozitie[c2] = v_initial[i];
c2 += 2;
}
}
}
}
This may not be a perfect solution and it just popped out right off my mind without being tested or verified, but it's just to give you an idea.
(Let A,B,C,D be odd numbers and 0,1,2 even numbers correspondingly)
Given:
A 0 B C D 1 2 (random ordered list of odd/even numbers)
Wanted:
A 0 B 1 C 2 D (input sequence altered to match the wanted odd/even criteria)
Next, we invent the steps required to get from given to wanted:
// look at 'A' -> match, next
// Result: A 0 B C D 1 2
// look at '0' -> match, next
// Result: A 0 B C D 1 2
// look at 'B' -> match, next
// Result: A 0 B C D 1 2
// look at 'C' -> mismatch, remember index and find first match starting from index+1
// Result: A 0 B C D ->1<- 2
// now swap the numbers found at the remembered index and the found one.
// Result: A 0 B 1 D C 2
// continue until the whole list has been consumed.
As I said, this algorithm may not be perfect, but my intention is to give you an example on how to solve these kinds of problems. It's not good to always think in code first, especially not with a problem like this. So you should first think about where you start, what you want to achieve and then carefully think of how to get there step by step.
I feel I have to mention that I did not provide an example in real code, because once you got the idea, the execution should be pretty much straight forward.
Oh, and just a small remark: Almost nothing about your code is C++.
A simple solution, that is not very efficient would be to split the vector into 2 vectors, that contain even and uneven numbers and then always take one from the even, one from the uneven and then the remainder, from the one that is not completely entered.
some c++ (that actually uses vectors, but you can use an array the same way, but need to change the pointer arithmetic)
I did not test it, but the principle should be clear; it is not very efficient though
EDIT: The answer below by #AAAAAAAAARGH outlines a better algorithmic idea, that is inplace and more efficient.
void change_vector_even_uneven(std::vector<unsigned>& in_vec){
std::vector<unsigned> even;
std::vector<unsigned> uneven;
for (auto it = in_vec.begin(); it != in_vec.end(); it++){
if ((*it) % 2 == 0)) even.push_back(*it);
else uneven.push_back(*it);
}
auto even_it = even.begin();
auto uneven_it = uneven.begin();
for (auto it = in_vec.begin(); it != in_vec.end(); it++){
if (even_it == even.end()){
(*it) = (*uneven_it);
uneven_it++;
continue;
}
if (uneven_it == uneven.end()){
(*it) = (*even_it);
even_it++;
continue;
}
if ((it - in_vec.begin()) % 2 == 0){
(*it) = (*even_it);
even_it++;
}
else{
(*it) = (*uneven_it);
uneven_it++;
}
}
}
The solutions is simple. We sort the even and odd values into a data structure. In a loop, we iterate over all source values. If they are even (val & 2 == 0) we add them at the end of a std::deque for evens and if odd, we add them to a std::deque for odds.
Later, we we will extract the the values from the front of the std::deque.
So, we have a first in first out principle.
The std::deque is optimized for such purposes.
Later, we make a loop with an alternating branch in it. We, alternatively extract data from the even queue and then from the odd queue. If a queue is empty, we do not extract data.
We do not need an additional std::vector and can reuse the old one.
With that, we do not need to take care for the same number of evens and odds. It will of course always work.
Please see below one of millions of possible solutions:
#include <iostream>
#include <vector>
#include <deque>
int main() {
std::vector testData{ 2, 6, 7, 8, 9, 3, 5, 1 };
// Show initial data
std::cout << "\nInitial data: ";
for (const int i : testData) std::cout << i << ' ';
std::cout << '\n';
// We will use a deques to store odd and even numbers
// With that we can efficiently push back and pop front
std::deque<int> evenNumbers{};
std::deque<int> oddNumbers{};
// Sort the original data into the specific container
for (const int number : testData)
if (number % 2 == 0)
evenNumbers.push_back(number);
else
oddNumbers.push_back(number);
// Take alternating the data from the even and the odd values
bool takeEven{ true };
for (size_t i{}; !evenNumbers.empty() && !oddNumbers.empty(); ) {
if (takeEven) { // Take even numbers
if (not evenNumbers.empty()) { // As long as there are even values
testData[i] = evenNumbers.front(); // Get the value from the front
evenNumbers.pop_front(); // Remove first value
++i;
}
}
else { // Now we take odd numbers
if (not oddNumbers.empty()) { // As long as there are odd values
testData[i] = oddNumbers.front(); // Get the value from the front
oddNumbers.pop_front(); // Remove first value
++i;
}
}
// Next take the other container
takeEven = not takeEven;
}
// Show result
std::cout << "\nResult: ";
for (const int i : testData) std::cout << i << ' ';
std::cout << '\n';
return 0;
}
Here is yet another solution (using STL), in case you want a stable result (that is, the order of your values is preserved).
#include <algorithm>
#include <vector>
auto ints = std::vector<int>{ 2, 6, 7, 8, 9, 3, 5, 1 };
// split list to even/odd sections -> [2, 6, 8, 7, 9, 3, 5, 1]
const auto it = std::stable_partition(
ints.begin(), ints.end(), [](auto value) { return value % 2 == 0; });
auto results = std::vector<int>{};
results.reserve(ints.size());
// merge both parts with equal size
auto a = ints.begin(), b = it;
while (a != it && b != ints.end()) {
results.push_back(*a++);
results.push_back(*b++);
}
// copy remaining values to end of list
std::copy(a, it, std::back_inserter(results));
std::copy(b, ints.end(), std::back_inserter(results));
The result ist [2, 7, 6, 9, 8, 3, 5, 1]. The complexity is O(n).
This answer, like some of the others, divides the data and then reassembles the result. The standard library std::partition_copy is used to separate the even and odd numbers into two containers. Then the interleave function assembles the result by alternately copying from two input ranges.
#include <algorithm>
#include <iostream>
#include <vector>
template <typename InIt1, typename InIt2, typename OutIt>
OutIt interleave(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt dest)
{
for (;;) {
if (first1 == last1) {
return std::copy(first2, last2, dest);
}
*dest++ = *first1++;
if (first2 == last2) {
return std::copy(first1, last1, dest);
}
*dest++ = *first2++;
}
}
void reorder_even_odd(std::vector<int> &data)
{
auto is_even = [](int value) { return (value & 1) == 0; };
// split
std::vector<int> even, odd;
std::partition_copy(begin(data), end(data), back_inserter(even), back_inserter(odd), is_even);
// merge
interleave(begin(even), end(even), begin(odd), end(odd), begin(data));
}
int main()
{
std::vector<int> data{ 2, 6, 7, 8, 9, 3, 5, 1 };
reorder_even_odd(data);
for (int value : data) {
std::cout << value << ' ';
}
std::cout << '\n';
}
Demo on Compiler Explorer
As suggested, I am using vectors and STL.
No need to be a great mathematician to understand v_pozitie will start with pairs of odd and even and terminate with the integers not in the initial pairs.
I am then updating three iterators in v_positie (no need of temporary containers to calculate the result) : even, odd and end,(avoiding push_back) and would code this way :
#include <vector>
#include <algorithm>
void vector_pozitii_pare_impare(std::vector<int>& v_initial, std::vector<int>& v_pozitie) {
int nodd (0), neven (0);
std::for_each (v_initial.begin (), v_initial.end (), [&nodd] (const int& n) {
nodd += n%2;
});
neven = v_initial.size () - nodd;
int npair (neven < nodd ?neven:nodd);
npair *=2;
std::vector<int>::iterator iend (&v_pozitie [npair]), ieven (v_pozitie.begin ()), iodd (&v_pozitie [1]);
std::for_each (v_initial.begin (), v_initial.end (), [&iend, &ieven, &iodd, &npair] (const int& s) {
if (npair) {
switch (s%2) {
case 0 :
*ieven++ = s;
++ieven;
break;
case 1 :
*iodd++ = s;
++iodd;
break;
}
--npair;
}
else *iend++ = s;
});
}
int main (int argc, char* argv []) {
const int N = 8;
int tab [N] = {2, 6, 7, 8, 9, 3, 5, 1};
std::vector<int> v_initial (tab, (int*)&tab [N]);
std::cout << "\tv_initial == ";
std::for_each (v_initial.begin (), v_initial.end (), [] (const int& s) {std::cout << s << " ";});
std::cout << std::endl;
std::vector<int> v_pozitie (v_initial.size (), -1);
vector_pozitii_pare_impare (v_initial, v_pozitie);
std::cout << "\tv_pozitie == ";
std::for_each (v_pozitie.begin (), v_pozitie.end (), [] (const int& s) {std::cout << s << " ";});
std::cout << std::endl;
}

How to add elements to set while traversing it?

We have a set S {1,10,100,1000,10000}. Now we input an integer x (say x = 4).
Now we have to add every element of set's product with x to the set itself. So finally
S={1,10,100,1000,10000,4,40,400,4000,40000}
[S is not limited to only 5 entries initially]
We have to visit only the initial elements in the set.
I tried an approach like:
for(auto i=s.begin();i!=s.end();i++)
{
s.insert((*i)*x);
}
This doesnt give the desired result, as the size of set keeps increasing.
Another approach I tried was to store all the multiples (*i)*x in another temporary set/vector and to merge it with s later.
But since the original dataset is huge, it worsens the time complexity.
Any optimizations ?
Since the std::set is ordered, and iterators are not invalidated by insertion, you can simply insert while iterating as long as you don't insert to the range that is still left to be iterated.
If we can assume all numbers to be positive, then we can iterate in reverse direction, because the result of multiplication is always going to be greater than the inputs:
for (auto it = S.rbegin(); it != S.rend(); ++it)
S.insert(*it*x);
If x is negative and set contains only positive, then order of iteration doesn't matter. If the set may contain negative numbers, this becomes more challenging.
But since the original dataset is huge, it worsens the time complexity.
Inserting N elements into std::set is O(N log N). Merging std::sets is O(N log N). The merge approach does not worsen asymptotic time complexity.
If you were to use an std::unordered_set though, the merge approach would be O(N) in average case. It's still O(N log N) in worst case however. I recommend using the merge approach with an unordered set.
Here you are.
#include <iostream>
#include <set>
#include <iterator>
std::set<int> & update( std::set<int> &s, int value )
{
for ( auto it = rbegin( s ); it != rend( s ); ++it )
{
s.insert( *it * value );
}
return s;
}
int main()
{
std::set<int> s = { 1, 10, 100, 1000, 10000 };
for ( const auto &value : s )
{
std::cout << value << ' ';
}
std::cout << '\n';
for ( const auto &value : update( s, 4 ) )
{
std::cout << value << ' ';
}
std::cout << '\n';
return 0;
}
The program output is
1 10 100 1000 10000
1 4 10 40 100 400 1000 4000 10000 40000
According to the C++ 20 Standard (22.2.6 Associative containers)
9 The insert and emplace members shall not affect the validity of
iterators and references to the container, and the erase members shall
invalidate only iterators and references to the erased elements.
Or a more general approach
#include <iostream>
#include <set>
#include <iterator>
std::set<int> & update( std::set<int> &s, int value )
{
auto partition = s.lower_bound( 0 );
for ( auto it = rbegin( s ); it != std::set<int>::reverse_iterator( partition ); ++it )
{
s.insert( *it * value );
}
for ( auto it = begin( s ); it != partition; ++it )
{
s.insert( *it * value );
}
return s;
}
int main()
{
std::set<int> s = { -10000, -1000, -100, -10, -1, 1, 10, 100, 1000, 10000 };
for ( const auto &value : s )
{
std::cout << value << ' ';
}
std::cout << '\n';
for ( const auto &value : update( s, 4 ) )
{
std::cout << value << ' ';
}
std::cout << '\n';
return 0;
}
The program output is
-10000 -1000 -100 -10 -1 1 10 100 1000 10000
-40000 -10000 -4000 -1000 -400 -100 -40 -10 -4 -1 1 4 10 40 100 400 1000 4000 10000 40000
As mentioned in the comments, it's simplest to use a temporary set. With C++17, you can use std::set::merge with the temporary set casted to an rvalue:
#include <algorithm>
std::set<int> s2;
std::transform(s1.cbegin(), s1.cend(), std::inserter(s2, s2.end()),
[](int orig){ return 4*orig; });
s1.merge(std::move(s2));
Otherwise, note that iterators into the set aren't invalidated while inserting. Take this together with the fact that the set is ordered and in case of the scenario you described (scaling an existing value is larger than the original value, but less or equal than the next existing one), you can do it in a loop like this:
for (auto it = s1.begin(); it != s1.end(); ++it)
it = s.insert(*it*4).first;
For fewer restrictions, you can use this more verbose loop:
for (std::set<int>::iterator it1 = s.begin(), it2; it1 != s.end();
it1 = std::next(it1) == it2 ? std::next(it1, 2) : it2)
it2 = s.insert(*it1*4).first;

How to sort std::vector ignoring certain numbers?

I am trying to sort a vector of numbers and ignore a certain number, i.e. leave it in place. This answer does not actually leave the element where it was found.
For example if I have the following
std::vector<int> test{5, 3, 8, 4, -1, 1, 11, 9, 6};
std::sort(test.begin(),
std::partition(test.begin(), test.end(), [](int n)
{return n != -1;}));
Sorts test into 1 3 4 5 6 8 9 11 -1. I searched for a couple hours, and tinkered with both custom comparators and using std::partition, but I cannot come up with a solution that sorts the test vector into 1 3 4 5 -1 6 8 9 11.
Is this just practically very difficult?
As per #Bathsheba 's remedy mentioned in his answer, and fooling std::sort()'s predicate, one can achieve the solution something like follows:
DEMO
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> test{5, 3, 8, 4, -1, 1, 11, 9, 6};
// get the position of -1
auto itr = std::find(test.begin(), test.end(), -1);
// sort all elements so that -1 will be moved to end of vector
std::sort(test.begin(), test.end(), [](const int& lhs, const int& rhs )
{
if( lhs == -1 ) return false;
if( rhs == -1 ) return true;
return lhs < rhs;
});
test.erase(test.end()-1); // now erase it from end
test.insert(itr, -1); // insert to the earlier position
for(const auto& it: test) std::cout << it << " ";
return 0;
}
Yes, it's tricky to do this using std::sort: you'd somehow have to fool your comparator into inserting the invariant number into the correct place, and that's difficult without examining the other elements beforehand.
A simple remedy is to use an insertion sort; omitting the out-of-place number (but recording the position) when you get to it, and inserting it manually at the end at that recorded position.
Given a vector.
Find the location of the element you want to leave.
Swap it out to the end.
Partial sort the vector (without the last element) - all elements before the selected location will be sorted, after that there will be a random order.
Swap the element back into the found location
sort the rest of the vector
The code:
std::vector< int > data{ 5, 3, 8, 4, -1, 1, 11, 9, 6 };
auto chosen_iter = std::find( data.begin(), data.end(), -1 );
std::swap( *chosen_iter, *( data.end() - 1 ) );
std::partial_sort( data.begin(), chosen_iter, data.end() - 1 );
std::swap( *chosen_iter, *( data.end() - 1 ) );
std::sort( chosen_iter + 1, data.end() );
Without swapping the element to the end :
Find the location of the element.
Partial sort the vector up to and excluding this location, using a comparator that makes this element greater than the other elements of the vector, so that the element does not appear in the partial sorted part.
Sort the rest of the vector from this location to the end, using a comparator that makes this element lesser than the other elements of the rest of the vector, this reput this element at this location.
Code :
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
constexpr int ignored_number = 100;
int main()
{
vector<int> test{5, 3, 8, 4, ignored_number, 1, 11, 9, 6};
auto it = find(test.begin(), test.end(), ignored_number);
partial_sort(test.begin(), it, test.end(), [](int lhs, int rhs) {
return lhs == ignored_number ? false :
(rhs == ignored_number ? true : lhs < rhs);
});
sort(it, test.end(), [](int lhs, int rhs) {
return rhs == ignored_number ? false :
(lhs == ignored_number ? true : lhs < rhs);
});
for (const auto& x: test) {
cout << x << ' ';
}
cout << endl;
}

Extract elements from a given stl container with given predicate to another container

What is the best way to extract and move elements that meet certain criteria from a STL container to another STL container (e.g., vector). For example:
std::vector<int> original {1, 2, 6, 7, 9, 34, 9, 7, 3}
// For example, I only need event numbers
auto criteria = [](const int a) -> bool { return a%2 == 0? }
std::vector<int> newvec = ...;
So, what I want after the operation is
original = {1, 7, 9, 9, 7, 3}
newvec = {2, 6, 34}
An elegant solution will be appreciated.
I'd go with customized erase/remove predicate, that would add the removed elements to newvec:
original.erase(std::remove_if(original.begin(), original.end(), [&](int n){
bool match = criteria(n);
if(match){
newvec.push_back(n);
}
return match;
}));
demo
You might want to consider throwing vector<T>::reserve into the mix if you know the approximate number of elements meeting your criteria.
There are no such algorithm in STL but it is short to write:
template <typename FIterator, typename OIterator, typename Pred>
FIterator splice_if( FIterator first, FIterator last, OIterator out, Pred p )
{
FIterator result = first;
for ( ; first != last; ++first ) {
if ( p( *first ) ) {
*result++ = *first;
} else {
*out++ = *first;
}
}
return result;
}
For objects of type int there is no great sense to use move iterators however in general case you can use move iterators.
Here is a demonstrative program that shows an approach to the task
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> original {1, 2, 6, 7, 9, 34, 9, 7, 3};
auto odd_value = []( int x ) { return x & 1; };
auto n = std::count_if( original.begin(), original.end(), odd_value );
std::vector<int> odd;
odd.reserve( n );
std::vector<int> even;
even.reserve( original.size() - n );
std::partition_copy( std::make_move_iterator( original.begin() ),
std::make_move_iterator( original.end() ),
std::back_inserter( odd ),
std::back_inserter( even ),
odd_value );
original = odd;
for ( int x : original ) std::cout << x << ' ';
std::cout << std::endl;
for ( int x : even ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
Its output is
1 7 9 9 7 3
2 6 34

How to remove a pair of repeated occurrences in a list in c++? (keep elements that appear only once)

I've tried using unique but unique removes the duplicate only.
What my program is suppose to do is for example the list contains 1,2,2,2,3,4,4. I am suppose to only remove the the pair of the number that is repeated and the output should be 1,2,3 (a pair of 2 and 4 is removed).
Iterate through data and remove pairs (Live code):
list<int> data{1, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 6};
for (auto i = data.begin(); i != data.end();)
{
auto n = std::next(i);
if (n == data.end())
break;
if (*i == *n)
{
i = data.erase(i);
i = data.erase(i);
}
else
i++;
}
Output
1 2 3 4 5 6
Also 1 2 3 for 1 2 2 2 3 4 4.
Above code works since C++11, if you don't have it, try this:
for (list<int>::iterator i = data.begin(); i != data.end();)
{
list<int>::iterator n = i;
n++;
if (n == data.end())
break;
if (*i == *n)
{
i = data.erase(i);
i = data.erase(i);
}
else
i++;
}
I would recommend using sort and unique functions to do this.
std::sort (my_vector.begin(), my_vector.end() );
std::vector<int>::iterator it;
it = std::unique (my_vector.begin(), my_vector.end() );
my_vector.resize( std::distance(my_vector.begin(),it) );
Reference: http://www.cplusplus.com/reference/algorithm/unique/ -- this has an example where you can use predicate comparison to customize the behavior of unique.
EDIT -- You may also want to look at std::adjacent_find if you are interested in removing consecutive elements.
EDIT -- If all you care is to delete consecutive elements, first sort the list, and then iterate over it. If two elements are consecutive, delete both of them using std::remove_if or something like that.
You can easily loop through the list. Get the first item and search for other items for an exact match, if any found remove both. When the loop ends you should have a list without any pairs.
If you can come up with a sentinel value that's not in your list, here's a slightly more generic way to handle this problem. As a side benefit, this probably works much better on vectors, since vector::erase is pretty slow.
#include <cassert>
#include <vector>
#include <iterator>
#include <iostream>
#include <algorithm>
template <class ITR>
void replace_pairs_with_sentinel(ITR begin, ITR end,
const typename std::iterator_traits<ITR>::value_type& sentinel)
{
// handle empty sequence
if ( begin == end ) return;
// ensure no sentinel values exist
assert ( std::find(begin, end, sentinel) == end );
ITR prev = begin++;
while ( begin != end ) {
if ( *begin == *prev ) {
*prev = *begin = sentinel;
}
prev = begin++;
}
}
int main (int argc, char* argv[])
{
int data[] = {1, 2, 2, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 6};
std::vector<int> v( data, data + sizeof(data) / sizeof(int) );
replace_pairs_with_sentinel( v.begin(), v.end(), -INT_MAX );
std::vector<int>::iterator end_itr = std::remove( v.begin(), v.end(), -INT_MAX );
v.resize( end_itr - v.begin() );
std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, " " ) );
return 0;
}