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.
Related
I'm trying to generate all permutations of a vector v using backtracking.
The basic idea of my algorithm is:
at each recursive step, iterate through the remaining elements of v, and pick one to add the resulting permutation. I then delete it from the vector v. I'm trying to speed up the deletion operation by using a std::list. However, this seems to produce an infinite recursive loop that outputs only the first possible permutation.
I can only suspect that it's some problem with my handling of the iterator, but I'm not sure how to fix it.
Here's my code below:
#include <vector>
#include <list>
#include <iostream>
using namespace std;
vector<int> res;
list<int> v = {1, 2, 3, 4};
void permute() {
if (v.empty()) {
for (int d : res) cout << d << " ";
cout << endl;
return;
}
for (auto it = v.begin(); it != v.end(); it ++) {
int d = *it;
res.push_back(d);
it = v.erase(it);
permute();
v.insert(it, d);
res.pop_back();
}
}
int main() {
permute();
}
This piece of code just prints "1 2 3 4" forever.
Any help is appreciated. Thanks!!
The problem is here: v.insert(it, d);
You will need to insert the value d back where it was, but you are not doing it.
list is not suitable for what you're trying to do. Use a vector, and instead of deleting, use swaps.
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
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.
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].
I am trying to find a way to copy elements of a vector to another vector.
int main()
{
std::vector<int> aVec{0,1,2,3,4};
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin()+3;
std::vector<int> aVecNew;
// How to insert elements ranging from itBegin till itEnd(including itEnd) to
// the vector aVecNew
return 0;
}
Most of the insert methods appear not to include itEnd. Is there a clean way to do this?
EDIT: If I am not sure ++itEnd is the end iterator or not. In such case it would fail. Is there any safer way without the mess ?
You can use std::copy from <algorithms> and std::back_inserter from <iterator>:
int main(int a, char**){
std::vector<int> aVec{ 0, 1, 2, 3, 4 };
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin() + 3;
std::vector<int> aVecNew;
std::copy(itBegin, itEnd, std::back_inserter(aVecNew));
return 0;
}
PS: Also, as it was mentioned in the comment, this code copies excluding itEnd. If you want to copy elements including itEnd, just increment its value by 1:
int main(int a, char**){
std::vector<int> aVec{ 0, 1, 2, 3, 4 };
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin() + 3;
std::vector<int> aVecNew;
std::copy(itBegin, ++itEnd, std::back_inserter(aVecNew));
return 0;
}
Some documentation on back_inserter and copy.
Beyond the ways already mentioned, std::vector has a constructor that will do exactly what you want (take the elements from range given begin and end iterators).
std::vector<int> aVecNew(itBegin, ++itEnd);
In the general case - the target vector does already exist - the copy onto a back_insert_iterator until just before ++itEnd is the right way, but in your case,
std::vector<int> aVecNew(itBegin, ++itEnd);
is the appropriate measure. std::vector has ctor #(4) for that; no reason to first create and then populate the vector here.
#include <vector>
#include <iostream>
#include <iterator> // ostream_iterator
#include <algorithm> // copy
int main()
{
std::vector<int> aVec{0,1,2,3,4}; // this is c++ of 2011 or later...
// ... thus this
std::vector<int>::iterator itBegin = aVec.begin();
std::vector<int>::iterator itEnd = aVec.begin() + 3;
// (if used) would at least be a case for "auto"
std::vector<int> aVecNew(itBegin, ++itEnd);
std::copy(aVecNew.begin(), aVecNew.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
return EXIT_SUCCESS;
}
output:
0 1 2 3
live at Coliru's