c++ How to erase an element without changing its end() - c++

I'm trying to erase an element from a vector by index as follows, but why does the first output differ from the second output? Is there any way of avoiding this or a better way of removing elements?
int main() {
vector<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << *test.end() << endl;
test.erase(test.begin() + 2);
cout << *test.end() << endl;
return 0;
}

std::vector::end returns the iterator to the element following the last element, dereference on it leads to UB, means anything is possible, it's just meaningless.
Returns an iterator to the element following the last element of the container.
This element acts as a placeholder; attempting to access it results in undefined behavior.
You might use std::vector::back to get the last element; (and better to check whether the vector is empty or not in advance.)
int main() {
vector<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << test.back() << endl;
test.erase(test.begin() + 2);
cout << test.back() << endl;
return 0;
}

normally you can not print the *test.end() , because it is not the last pointer, it is after the last pointer. it's value is not valid for you,and may cause an exception.
vector::end() == vector::begin() + vector::size()
you can use vector::back
I checked the stl code of vector::erase()
iterator erase(const_iterator _Where) noexcept(is_nothrow_move_assignable_v<value_type>) /* strengthened */ {
const pointer _Whereptr = _Where._Ptr;
auto& _My_data = _Mypair._Myval2;
pointer& _Mylast = _My_data._Mylast;
#if _ITERATOR_DEBUG_LEVEL == 2
_STL_VERIFY(
_Where._Getcont() == _STD addressof(_My_data) && _Whereptr >= _My_data._Myfirst && _Mylast > _Whereptr,
"vector erase iterator outside range");
_Orphan_range(_Whereptr, _Mylast);
#endif // _ITERATOR_DEBUG_LEVEL == 2
_Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr);
_Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1)); // here after move it destroy the last element.
--_Mylast;
return iterator(_Whereptr, _STD addressof(_My_data));
}
so use *end() may cause a exception of wild pointer.

try list
#include <vector>
#include <list>
int main() {
list<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto pbeg=test.begin(),
pend=test.end(),
a = pbeg, b = pend;
cout << *(--b) << endl;
++(++a);
test.erase(a);
cout << *(--b) << endl;
for ( a=pbeg; a!=pend; ++a)
cout<< *a <<" ";
return 0;
}
10
10
1 2 4 5 6 7 8 9 10

Your choice of the container needs to be revisited if you want avoid iterator invalidation during erase or removal of items from container.
Below link can provide the overview of iterator invalidation:
http://kera.name/articles/2011/06/iterator-invalidation-rules-c0x/

Related

Is there an algorithm that checks whether a container contains only distinct elements?

Hello I want to find some algorithm that checks whether a container contains all distinct elements.
Here is what I've tried:
template <typename It>
bool hasAllDistinctElems(It first, It last){
for(auto i = first; i != last; ++i)
for(auto j = i; ++j != last; )
if(*i == *j )
return false;
return true;
}
int main(){
std::vector<int> vi{5, 7, 3, 8, 2, 7, 5};
std::deque<int> di{1, 5, 7, 2, 3, 8, 6};
std::cout << hasAllDistinctElems( vi.cbegin(), vi.cend() ) << '\n'; // 0
std::cout << hasAllDistinctElems( di.cbegin(), di.cend() ) << '\n'; // 1
}
It works just fine but I've found another method:
copy all the elements of the first container into a container that guarantees the uniqueness of its elements like the STL associative containers like std::set, std::map.
Compare the sizes of the containers so if the sizes are equal it means all the elements from the first one has been copied to the set which means they are all unique otherwise they are not unique thus not all of them have been copied into the set:
#include <unordered_set>
#include <deque>
#include <vector>
#include <iostream>
int main(){
std::vector<int> vi{5, 7, 3, 8, 2, 7, 5};
std::deque<int> di{1, 5, 7, 2, 3, 8, 6};
std::unordered_set<int> usi(vi.cbegin(), vi.cend());
std::unordered_set<int> usi2(di.cbegin(), di.cend());
std::cout << std::boolalpha;
std::cout << "vi's all distinct? " << (vi.size() == usi.size()) << '\n'; // false
std::cout << "di's all distinct? " << (di.size() == usi2.size()) << '\n'; // true
}
Both work fine but I'd like to know which one should I use? Is there an algorithm that does that?? or another workaround? Thank you!

Can vector erase() function delete two consecutive same elements?

Below is a snippet of my program. I iterate through a vector and try to delete all the element which has
the value of three. But after I print the content of the vector,it's 1-2-4-3-5,it still has a number 3. Why? Do we have to deal with iterator specially if we want to delete two consecutive same elements?
int main()
{
std::vector<int> a = {1, 2, 3, 4, 3, 3, 5}; // a has size 5
for(auto it=a.begin();it!=a.end();)
{
if(*it == 3) it=a.erase(it);
it++;
}
for(auto x:a) cout<<x<<endl;
}
We can break down your example and only look at the last three numbers 3, 3, 5.
After erasing the first 3, an iterator two the second 3 is returned in
it = a.erase(it);
But immediately after this it is incremented in
it++;
and now points to the 5.
Here you can use the erase-remove-idiom. Example code:
a.erase(std::remove(a.begin(), a.end(), 3), a.end());
There is a method in <algorithm> named remove_if(first, last, func) which will solve your purpose. It is safer. Please check the following code snipped -
#include <iostream>
#include <vector>
#include <algorithm>
bool isThree(int k){
return (k == 3);
}
int main(){
std::vector<int> v {1,2,3,4,5,6,7,3,3,3};
std::vector<int>::iterator it;
v.erase(std::remove_if(v.begin(), v.end(), isThree), v.end());
for(int i=0;i<v.size(); i++){
std::cout << v[i] << " ";
}
return 0;
}
The bug is that you skip checking an element whenever you delete one. This is because it = a.erase(it); moves all elements backwards past it and returns it - to fix the issue just don't increment it when you erase:
for(auto it=a.begin();it!=a.end();)
{
if(*x == 3) it=a.erase(it); else it++;
}
Also this code is slow because it is potentially o(n^2) operations - you'd better use std::remove then trigger erase/resize to make your vector be of correct size.

How can I find the index of the highest value in a vector, defaulting to the greater index if there are two "greatest" indices?

I have been using std::max_element(vec), but from what I can tell, it returns the smallest index if two "greatest" indices are equal.
Example:
vector<int> v = {1, 2, 3, 4, 5, 3, 3, 2, 5};
std::max_element(v) would reference v[4], but for the purposes of my project I need it to reference v[8] instead. What would be the best way to do this?
You can use this
max_element(v.rbegin(), v.rend());
to refer to the greatest index of the greatest value.
For example,
#include "iostream"
#include "vector"
#include "algorithm"
using namespace std;
int main()
{
vector<int> v = {1, 2, 3, 4, 5, 3, 3, 2, 5};
*max_element(v.rbegin(), v.rend())=-1;
for (auto i: v) cout << i << ' ';
}
produces output
1 2 3 4 5 3 3 2 -1
The method mentioned above returns a reverse iterator, as pointed out by #BoBTFish. To get a forward iterator, you could do this:
#include "iostream"
#include "vector"
#include "algorithm"
using namespace std;
int main()
{
vector <int> v = {1, 2, 3, 4, 5, 3, 3, 2, 5};
reverse_iterator < vector <int> :: iterator > x (max_element(v.rbegin(), v.rend()));
vector <int> :: iterator it=--x.base(); // x.base() points to the element next to that pointed by x.
*it=-1;
*--it=0; // marked to verify
for (auto i: v) cout << i << ' ';
}
produces output
1 2 3 4 5 3 3 0 -1
^
It can be seen that the iterator it is a forward iterator.
It's very easy to make your own function:
/* Finds the greatest element in the range [first, last). Uses `<=` for comparison.
*
* Returns iterator to the greatest element in the range [first, last).
* If several elements in the range are equivalent to the greatest element,
* returns the iterator to the last such element. Returns last if the range is empty.
*/
template <class It>
auto max_last(It first, It last) -> It
{
auto max = first;
for(; first != last; ++first) {
if (*max <= *first) {
max = first;
}
}
return max;
}

How can I check if vector elements are in order consecutively?

I need to check if in my vector the elements are in order consecutively?
for(i=1; i<=K; i++)
if(v[i]=v[i+1]-1)
If the statement would be true I want to return the biggest integer.
ex. 4 5 6 7
7
There's an algorithm for that: std::is_sorted:
if (std::is_sorted(v.begin(), v.end()) {
return v.back(); // the largest element would be the last one
}
else {
// ??
}
I need to check if in my vector the elements are in order
Use C++11's std::is_sorted algorithm:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v1 { 4, 5, 7, 6 };
std::vector<int> v2 { 4, 5, 6, 7 };
using std::begin;
using std::end;
std::cout << std::is_sorted(begin(v1), end(v1)) << "\n";
std::cout << std::is_sorted(begin(v2), end(v2)) << "\n";
}
If the statement would be true I want to return the biggest integer.
That's a job for std::max_element:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v1 { 4, 5, 7, 6 };
std::vector<int> v2 { 4, 5, 6, 7 };
using std::begin;
using std::end;
std::cout << *std::max_element(begin(v1), end(v1)) << "\n";
std::cout << *std::max_element(begin(v2), end(v2)) << "\n";
}
Note that std::max_element does not require its input to be sorted.
Or if it's sorted anyway, just use v1.back().
I would use:
is_sorted(begin(v), end(v));
If the sequence is not sorted then use:
max_element(begin(v), end(v));
to get the max.
Use std::is_sorted algorithm of STL.
bool test(vector<int> v) {
for(int i = 0; i < v.size()-1; i++)
if(v[i] > v[i+1])
return false;
return true;
}
If the vector is in order, so the biggest is the last one, that is, v[v.size()-1].
From the question it seems you are interested if the vector elements are consective integers. If that is the case you can use a flag to indicate if the condition holds and do a single iteration:
bool consecutive = false;
for (int i = 1; i < v.size(); ++i) {
if (v[i] != v[i - 1] + 1) {
consecutive = false;
break;
}
}
if (consecutive) {
cout << v.back() << endl; // the last element is the biggest.
}
However your question title suggests you are interested in checking if the vector is sorted. If that is the case you can use the built-in function std::is_sorted that is present since c++11.
Traverse through the vector starting from the second element and keep a flag variable initialized to 0, for each iteration check the following condition :-
if(v[i]<v[i-1])
if this condition is true, then set the flag to 1 and break out of the loop. After the control moves out of the loop, check the value of flag. If flag is 1, then the vector elements are not in order. Otherwise, if the flag is 0, then the vector elements are in order and you have to print the largest element which is v[v.size()-1].

Are there STL functions to split/splice C-style arrays into smaller arrays?

Let's say I have a C-style array (int numbers[10]). I want to split the array into an array of odd numbers and an array of even numbers. Further, I'd like to use a predicate to determine if a number is odd.
Question: I am curious - are there STL functions that can do this?
The closest thing I can find is list::splice, but that's not for C-style arrays and doesn't take a predicate.
std::partition() would work.
Indeed, Example 1 on that page is separating even and odd numbers. It's doing it on a vector, but there's no reason it wouldn't work on native arrays.
Here's a quick example I worked up:
#include <algorithm>
#include <iostream>
int main()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto mid = std::partition(std::begin(a), std::end(a),
[](int n){return n%2;});
std::cout << "Odd: " << std::endl;
for (auto p = std::begin(a); p < mid; ++p)
{
std::cout << *p << std::endl;
}
std::cout << "Even: " << std::endl;
for (auto p = mid; p < std::end(a); ++p)
{
std::cout << *p << std::endl;
}
}
Indeed you can: std::partition partitions a sequence according to a predicate.
auto begin = std::begin(array);
auto end = std::end(array);
auto part = std::partition(begin, end, [](int n){return n%2;});
Now [begin,part) contains the odd values (for which the predicate is true), and [part,end) contains the even values (for which the predicate is false).