Erasing() an element in a vector doesn't work - c++

I have a vector. I need to delete the last 3 elements in it.
Described this logic. The program crashes. What could be the mistake?
vector<float>::iterator d = X.end();
for (size_t i = 1; i < 3; i++) {
if (i == 1) X.erase(d);
else X.erase(d - i);
}

If there are at least 3 items in the vector, to delete the last 3 items is simple -- just call pop_back 3 times:
#include <vector>
#include <iostream>
int main()
{
std::vector<float> v = { 1, 2, 3, 4, 5 };
for (int i = 0; i < 3 && !v.empty(); ++i)
v.pop_back();
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
}
Output:
1 2

It is undefined behavior to pass the end() iterator to the 1-parameter erase() overload. Even if it weren't, erase() invalidates iterators that are "at and after" the specified element, making d invalid after the 1st loop iteration.
std::vector has a 2-parameter erase() overload that accepts a range of elements to remove. You don't need a manual loop at all:
if (X.size() >= 3)
X.erase(X.end()-3, X.end());
Live Demo

You could use a reverse_iterator:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<float> X = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};
// start the iterator at the last element
vector<float>::reverse_iterator rit = X.rbegin();
// repeat 3 times
for(size_t i = 0; i < 3; i++)
{
rit++;
X.erase(rit.base());
}
// display all elements in vector X
for(float &e: X)
cout << e << '\n';
return 0;
}
There are few things to mention:
reverse_iterator rit starts at the last element of the vector X. This position is called rbegin.
erase requires classic iterator to work with. We get that from rit by calling base. But that new iterator will point to the next element from rit in forward direction.
That's why we advance the rit before calling base and erase
Also if you want to know more about reverse_iterator, I suggest visiting this answer.

First, X.end() doesn't return an iterator to the last element of the vector, it rather returns an iterator to the element past the last element of the vector, which is an element the vector doesn't actually own, that's why when you try to erase it with X.erase(d) the program crashes.
Instead, provided that the vector contains at least 3 elements, you can do the following:
X.erase( X.end() - 3, X.end() );
Which instead goes to the third last element, and erases every element after that until it gets to X.end().
EDIT: Just to clarify, X.end() is a LegacyRandomAccessIterator which is specified to have a valid - operation which returns another LegacyRandomAccessIterator.

This statement
if (i == 1) X.erase(d);
has undefined behavior.
And this statement tries to remove only the element before the last element
else X.erase(d - i);
because you have a loop with only two iterations
for (size_t i = 1; i < 3; i++) {
You need something like the following.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<float> v = { 1, 2, 3, 4, 5 };
auto n = std::min<decltype( v.size() )>( v.size(), 3 );
if ( n ) v.erase( std::prev( std::end( v ), n ), std::end( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
The program output is
1 2

The definition of end() from cppreference is:
Returns an iterator referring to the past-the-end element in the vector container.
and slightly below:
It does not point to any element, and thus shall not be dereferenced.
In other words, the vector has no element that end() points to. By dereferencing that non-element thru the erase() method, you are possibly altering memory that does not belong to the vector. Hence ugly things can happen from there on.
It is the usual C++ convention to describe intervals as [low, high), with the “low” value included in the interval, and the “high” value excluded from the interval.

A comment (now deleted) in the question stated that "there's no - operator for an iterator." However, the following code compiles and works in both MSVC and clang-cl, with the standard set to either C++17 or C++14:
#include <iostream>
#include <vector>
int main()
{
std::vector<float> X{ 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f };
for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
std::vector<float>::iterator d = X.end();
X.erase(d - 3, d); // This strongly suggest that there IS a "-" operator for a vector iterator!
for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
return 0;
}
The definition provided for the operator- is as follows (in the <vector> header):
_NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
_Vector_iterator _Tmp = *this;
return _Tmp -= _Off;
}
However, I'm certainly no C++ language-lawyer, and it is possible that this is one of those 'dangerous' Microsoft extensions. I would be very interested to know if this works on other platforms/compilers.

Related

How to increment the C++list's Iterator inside a for loop to iterate over specific pattern say for example each second element?

Usually in C++ one can iterate over each second element by doing so:
for (int i = 0; i < 10; i = i + 2 ){
//do Somthing
}
and for a C++ list (from library list) one can iterate over each element like so:
std::list<int> dY = {5,4,5,8,9,7,10,4};
std::list<int>::iterator iterdY;
for (iterdY = dY.begin(); iterdY != dY.end(); iterdY++) {
// do Something
}
Now my question is what should I write instead of iterdY++ to iterate over each second element ??
Probably the easiest way is to introduce a second iteration variable.
std::list<int> dY;
int n = 0;
std::list<int>::iterator iterdY = {5,4,5,8,9,7,10,4};
for (iterdY = dY.begin(); iterdY != dY.end(); iterdY++, ++n) {
if ((n % 2) == 0) {
// do Something
}
}
std::advance(iterdY, 2)
But you can no longer use the comparison iterdY != dY.end() because if the list has an odd number of elements you will skip over the end iterator, and into the territory of undefined behavior.
You must make sure that the list have an even number of elements first, if using iterators, or use some other way to keep track of the iterator not passing beyond the end.
If your compiler supports C++23 you can use ranges and stride_view to skip over every second element in the container.
See example here (using the earlier Ranges v3 library, which is the base for the C++20 standard library ranges).
Using the original ranges v3 library with earlier C++ standards is possible as well.
For starters I think you mean a list in this declaration
std::list<int>::iterator iterdY = {5,4,5,8,9,7,10,4};
like
std::list<int> dY = {5,4,5,8,9,7,10,4};
instead of the iterator.
You can do something like the following
for ( auto it = dY.begin(); it != dY.end(); ++it == dY.end() ? it : ++it ) {
//...
Here is a demonstration program.
#include <iostream>
#include <list>
int main()
{
std::list<int>dY = {5,4,5,8,9,7,10,4};
for ( auto it = dY.begin(); it != dY.end(); ++it == dY.end() ? it : ++it )
{
std::cout << *it << ' ';
}
std::cout << '\n';
}
The program output is
5 5 9 10
I'd use two steps and a function:
auto iterdY = dY.begin();
while (iterDy != dy.end()) {
do_something(*iterDy++);
if (iterDy == dY.end())
break;
do_something(*iterDy++);
}
After understanding all of suggested answers I would like to propose this solution:
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main()
{
std::list<int> dY = {5,4,5,8,9,10,11};
std::list<int>::iterator it = dY.begin();
int step = 3; // Here one can change the step to print each third element
for (int i = 0 ; i < dY.size() ; i+= step )
{
std::cout << *it <<'\n';
std::advance(it, step);
}
std::cout << '\n';
}
output is:
5 8 11

How to remove duplicated items in a sorted vector

I currently have a vector<int> c which contains {1,2,2,4,5,5,6}
and I want to remove the duplicated numbers so that c will have
{1,4,6}. A lot of solutions on the internet seem to just remove one of the duplicates, but I'm trying to remove all occurrences of the duplicated number.
Assume c is always sorted.
I currently have
#include <iostream>
#include <vector>
int main() {
vector<int> c{1,2,2,4,5,5,6};
for (int i = 0; i < c.size()-1; i++) {
for (int j=1; j<c.size();j++){
if(c[i] == c[j]){
// delete i and j?
}
}
}
}
I tried to use two for-loops so that I can compare the current element and the next element. This is where my doubt kicked in. I'm not sure if I'm approaching the problem correctly.
Could I get help on how to approach my problem?
This code is based on the insight that an element is unique in a sorted list if and only if it is different from both elements immediately adjacent to it (except for the starting and ending elements, which are adjacent to one element each). This is true because all identical elements must be adjacent in a sorted array.
void keep_unique(vector <int> &v){
if(v.size() < 2){return;}
vector <int> unique;
// check the first element individually
if(v[0] != v[1]){
unique.push_back(v[0]);
}
for(unsigned int i = 1; i < v.size()-1; ++i){
if(v[i] != v[i-1] && v[i] != v[i+1]){
unique.push_back(v[i]);
}
}
// check the last item individually
if(v[v.size()-1] != v[v.size()-2]){
unique.push_back(v[v.size()-1]);
}
v = unique;
}
Almost any time you find yourself deleting elements from the middle of a vector, it's probably best to sit back and think about whether this is the best way to do the job--chances are pretty good that it isn't.
There are a couple of obvious alternatives to that. One is to copy the items you're going to keep into a temporary vector, then when you're done, swap the temporary vector and the original vector. This works particularly well in a case like you've shown in the question, where you're keeping only a fairly small minority of the input data.
The other is to rearrange the data in your existing vector so all the data you don't want is at the end, and all the data you do want is at the beginning, then resize your vector to eliminate those you don't want.
When I doubt, I tend to go the first route. In theory it's probably a bit less efficient (poorer locality of reference) but I've rarely seen a significant slow-down in real use.
That being the case, my initial take would probably be something on this general order:
#include <vector>
#include <iostream>
#include <iterator>
std::vector<int> remove_all_dupes(std::vector<int> const &input) {
if (input.size() < 2) // zero or one element is automatically unique
return input;
std::vector<int> ret;
// first item is unique if it's different from its successor
if (input[0] != input[1])
ret.push_back(input[0]);
// in the middle, items are unique if they're different from both predecessor and successor
for (std::size_t pos = 1; pos < input.size() - 2; pos++)
if (input[pos] != input[pos-1] && input[pos] != input[pos+1])
ret.push_back(input[pos]);
// last item is unique if it's different from predecessor
if (input[input.size()-1] != input[input.size()-2])
ret.push_back(input[input.size() - 1]);
return ret;
}
int main() {
std::vector<int> c { 1, 2, 2, 4, 5, 5, 6 };
std::vector<int> uniques = remove_all_dupes(c);
std::copy(uniques.begin(), uniques.end(), std::ostream_iterator<int>(std::cout, "\n"));
}
Probably a little longer of code than we'd really prefer, but still simple, straightforward, and efficient.
If you are going to do the job in place, the usual way to do it efficiently (and this applies to filtering in general, not just this particular filter) is to start with a copying phase and follow that by a deletion phase. In the copying phase, you use two pointers: a source and a destination. You start them both at the first element, then advance through the input with the source. If it meets your criteria, you copy it to the destination position, and advance both. If it doesn't meet your criteria, advance only the source.
Then when you're done with that, you resize your vector down to the number of elements you're keeping.
void remove_all_dupes2(std::vector<int> & input) {
if (input.size() < 2) { // 0 or 1 element is automatically unique
return;
}
std::size_t dest = 0;
if (input[0] != input[1])
++dest;
for (std::size_t source = 1; source < input.size() - 2; source++) {
if (input[source] != input[source-1] && input[source] != input[source+1]) {
input[dest++] = input[source];
}
}
if (input[input.size()-1] != input[input.size()-2]) {
input[dest++] = input[input.size() - 1];
}
input.resize(dest);
}
At least in my view, the big thing to keep in mind here is the general pattern. You'll almost certainly run into a lot more situations where you want to filter some inputs to those that fit some criteria, and this basic pattern of tracking source and destination, and copying only those from the source to the destination that fit your criteria works well in a lot of situations, not just this one.
Generally one has to be very careful when deleting from containers while iterating over them. C++ STL can do this easily and faster (on average) than using nested loops.
#include <vector>
#include <algorithm>
#include <unordered_set>
int main() {
std::vector<int> c{1,2,2,4,5,5,6};
std::unordered_multiset<int> unique( c.begin(), c.end() );
c.erase(std::remove_if(c.begin(),c.end(),[&](const auto& e){return unique.count(e)>1;}),c.end());
for(auto e: c){
std::cout<<e<<' ';
}
}
//Output: 1 4 6
Alternatively, you could use std::map<int,std::size_t> and count the occurences this way.
Similarly to std::unique/std::copy_if, you might do:
void keep_unique(std::vector<int>& v){
auto it = v.begin();
auto w = v.begin();
while (it != v.end())
{
auto next = std::find_if(it, v.end(), [&](int e){ return e != *it; });
if (std::distance(it, next) == 1) {
if (w != it) {
*w = std::move(*it);
}
++w;
}
it = next;
}
v.erase(w, v.end());
}
Demo
Use std::remove_if to move items occurring multiple times to the rear, then erase them.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> V {1,2,2,4,5,5,6};
auto it = std::remove_if(V.begin(), V.end(), [&](const auto& val)
{
return std::count(V.begin(), V.end(), val) > 1;
});
V.erase(it, V.end());
for (const auto& val : V)
std::cout << val << std::endl;
return 0;
}
Output:
1
4
6
For demo: https://godbolt.org/z/j6fxe1
Iterating in reverse ensures an O(N) operation and does not cause element shifting when erasing because we are only ever erasing the last element in the vector. Also, no other data structures need to be allocated.
For every element encountered, we check if the adjacent element is equal, and if so, remove all instances of that element.
Requires the vector to be sorted, or at least grouped by duplicates.
#include <iostream>
#include <vector>
int main()
{
std::vector<int> c {1, 2, 2, 4, 5, 5, 6};
for (int i = c.size() - 1; i > 0;)
{
const int n = c[i];
if (c[i - 1] == n)
{
while (c[i] == n)
{
c.erase(c.begin() + i--);
}
}
else
{
i--;
}
}
//output result
for (auto it : c)
{
std::cout<<it;
}
std::cout << std::endl;
}
Output: 146
Update
An actual O(N) implementation using a sentinel value:
#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
int main()
{
std::vector<int> c { 1, 2, 2, 4, 5, 5, 6 };
const int sentinel = std::numeric_limits<int>::lowest(); //assumed that no valid member uses this value.
for (int i = 0; i < c.size() - 1;)
{
const int n = c[i];
if (c[i + 1] == n)
{
while (c[i] == n)
{
c[i++] = sentinel;
}
}
else
{
i++;
}
}
c.erase(std::remove(c.begin(),c.end(),sentinel), c.end());
for (auto it : c) std::cout<< it << ' ';
}
This can be achieved with the proper use of iterators to avoid runtime errors.
Have a look at the following code:
#include <iostream>
#include <vector>
int main() {
std::vector<int> c{1,2,2,4,5,5,6};
for (auto it = c.begin(); it != c.end(); ){
auto it2 = it;
advance(it2, 1);
bool isDuplicate = false;
for(; it2 != c.end(); ++it2){
if(*it == *it2){
c.erase(it2);
isDuplicate = true;
}
}
if(isDuplicate){
auto it3 = it;
advance(it3, 1);
c.erase(it);
it = it3;
}else{
it++;
}
}
for (auto it = c.begin(); it != c.end(); it++){
std::cout<<*it<<" ";
}
}
Output:
1 4 6

bubble sort in c++ with an array of 10 random ints isnt working

Run code here
this is my code for my bubble sort in c++ that for some reason only bubble sorts the last few but not the first 5 ints in the array. Help please
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
/* Tom Dresner
* 3/17/17
*/
int tom[10];
int b;
int x;
void sort()
{
cout << "\n";
for(int b=0;b<10;b++)
{
for(int x=0;x<9;x++)
{
if(tom[x]>tom[x + 1])
{
int t = tom[x];
tom[x] = tom[x+1];
tom[x+1] = t;
}
}
cout<< tom[b] << endl;
}
}
int main()
{
cout << "\n";
for(int i=0;i<10;i++)
{
tom[i] = rand()%10;
cout<< tom[i] << endl;
}
sort();
}
Not a complete answer, because that looks like homework, but here is a solution to a related problem that shows what kind of techniques you can use. In particular, a way to generate and return data without using globals, and a way to print it.
You may want to sort in place instead of what I do below. If so, you will find std::swap(v[i],v[j]) from the header <algorithm> helpful. You would also want to take a std::vector<int>& reference as your argument and return the same reference, like I do for std::ostream& os.
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::size_t;
std::vector<int> merge( const std::vector<int>& a, const std::vector<int>& b )
/* Given two sorted vectors, a and b, returns a sorted vector that merges the
* elements of a and b. Maybe you can figure out how this would be useful
* for sorting faster than bubble sort? Or you can ask your professor.
*/
{
std::vector<int> output; // Will hold the results;
/* If you haven't seen iterators yet, they're like indices inside the array.
* The expression *it++ returns the value at position it, then increments
* it.
*/
std::vector<int>::const_iterator it = a.begin();
std::vector<int>::const_iterator jt = b.begin();
// Reserve as much memory as we will need, because resizing vectors is slow.
output.reserve( a.size() + b.size() );
while ( it < a.end() || jt < b.end() ) {
if ( it == a.end() ) // We've reached the end of a, but not b.
while ( jt < b.end() ) // So we can stop checking a.
output.push_back(*jt++); // Add each element to the end.
else if ( jt == b.end() ) // We've reached the end of b, but not a.
while ( it < a.end() )
output.push_back(*it++);
else if ( *jt >= *it ) // Always add the lowest remaining element.
output.push_back(*it++);
else // What is the only remaining case?
output.push_back(*jt++);
} // end while
/* Do we know for sure that we've added each element from a or b to output
* exactly once, and in the right order? We assume a and be were sorted
* already. On each invocation of the loops, we copied exactly one element
* from either a or b to output, and stepped past it. The element we added
* was the smallest remaining element of a, unless there were no more in a
* or the smallest remaining element of b was smaller. Otherwise, it was
* the smallest remaining element of b. We stopped when we ran out of
* elements in both a and b.
*/
output.shrink_to_fit(); // Maybe save a few bytes of memory.
// Check that we have the correct number of elements:
assert( output.size() == a.size() + b.size() );
return output;
}
bool is_sorted( const std::vector<int>& v )
// Returns true if and only if v is sorted.
{
// Check that the elements are sorted.
for ( size_t i = 1; i < v.size(); ++i )
if( v[i] < v[i-1] )
return false;
/* If we escape the loop, we tested each pair of consecutive elements of v,
* and none were out of order.
*/
return true;
}
std::ostream& operator<< ( std::ostream& os, const std::vector<int>& v )
// Boilerplate to serialize and print a vector to a stream.
{
const size_t size = v.size();
os << '[';
if (size > 0)
os << v[0];
for ( size_t i = 1; i < size; ++i )
os << ',' << v[i];
os << ']';
return os;
}
int main(void)
{
// Our sorted input lists:
const std::vector<int> a = {0, 2, 4, 6, 8};
const std::vector<int> b = {-3, -2, -1, 0, 1, 2, 3};
assert(is_sorted(a)); // Input validation.
assert(is_sorted(b));
//Our output:
const std::vector<int> sorted = merge(a, b);
assert(is_sorted(sorted)); // Output validation.
cout << sorted << endl;
return EXIT_SUCCESS;
}

removing last elements from vector until condition

I ran into a problem where i need to delete the last elements of a vector until a certain condition is met (for sake of this example let it be the element is not zero)
I wrote this code, it does the trick -
auto next = vec.rbegin();
while (next != vec.rend())
{
auto current = next++;
if (*current == 0)
vec.pop_back();
else
break;
}
But i would much rather find an stl algorithm that i can use (i can use find_if and then erase, but i'd like to loop once through the elements that i remove...)
Also, i'm afraid i may be invoking some UB here, should i be worried?
Your code can be simplier:
while( !vec.empty() && vec.back() == 0 )
vec.pop_back();
Using std::remove or std::remove_if would remove all elements based by criteria, so you should use std::find_if as Vlad provided in his answer.
Here is an example. It uses the general idiom for erasing vectors
v.erase( std::remove( /*...*/ ), v.end() )
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5, 0, 0, 0 };
v.erase(
std::find_if( v.rbegin(), v.rend(),
[]( int x ) { return x != 0; } ).base(), v.end() );
for ( int x : v ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
The output is
1 2 3 4 5

squeeze table: same row overlap together, and count the number

suppose I have a table like this: (table is a 2-d array in C++)
number is the count for each row.
1 a b c
1 a b c
1 c d e
1 b c d
1 b c d
with be squeezed to:
2 a b c
1 c d e
2 b c d
My algorithm is O(n*n), can some one improve it?
suppose t1 is original one;
initial another t2;
row_num = 1;
copy first row of t1 to t2;
foreach row in t1 (1 to n)
search each row in t2 (0 to row_num);
if equal, then add the number;
break;
if not found, then copy current t1's row to t2;
row_num++
If your data's sorted like in the example, then it's just O(n).
Use a std::sort(or any other O(nlogn) sort) to order your arrays. Then it's just another pass and it's done :)
Here's a working example of O(N log N) complexity. It first sorts the data, then loops over each element and counts the number occurances by looking for the first mismatch, and then storing the sum of the counts from the current element in a result vector. Note that you can also have counts different from 1 in your initial arrays. The code works without having to specify a specific comparison function because std::array already has a lexicographic operator<.
The code below uses C++11 features (auto, lambda) that might not work on your compiler. You might also use initalizer lists to initialize the vector in one statement, but withthe nested vector of pair of int and array, I got a little confused on how many braces I needed to write :-)
#include <algorithm>
#include <array>
#include <iostream>
#include <utility>
#include <vector>
typedef std::pair<int, std::array<char, 3> > Element;
std::vector< Element > v;
std::vector< Element > result;
int main()
{
v.push_back( Element(1, std::array<char, 3>{{'a', 'b', 'c'}}) );
v.push_back( Element(2, std::array<char, 3>{{'a', 'b', 'c'}}) );
v.push_back( Element(1, std::array<char, 3>{{'c', 'd', 'e'}}) );
v.push_back( Element(1, std::array<char, 3>{{'b', 'c', 'd'}}) );
v.push_back( Element(3, std::array<char, 3>{{'b', 'c', 'd'}}) );
// O(N log(N) ) complexity
std::sort(v.begin(), v.end(), [](Element const& e1, Element const& e2){
// compare the array part of the pair<int, array>
return e1.second < e2.second;
});
// O(N) complexity
for (auto it = v.begin(); it != v.end();) {
// find next element
auto last = std::find_if(it, v.end(), [=](Element const& elem){
return it->second != elem.second;
});
// accumulate the counts
auto count = std::accumulate(it, last, 0, [](int sub, Element const& elem) {
return sub + elem.first;
});
// store count in result
result.push_back( Element(count, it->second) );
it = last;
}
for (auto it = result.begin(); it != result.end(); ++it) {
std::cout << it->first << " ";
for (std::size_t i = 0; i < 3; ++i)
std::cout << it->second[i] << " ";
std::cout << "\n";
}
}
Output on Ideone
NOTE: the loop over the sorted elements might seem O(N^2) (a linear std::find_if nested inside a linear for), but it is O(N) because of the last loop statement it = last that skips over already searched elements.