Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I am attempting to solve the interview problem from leet code to remove duplicates from a vector of ints.
Below is my my code for the answer:
#include <vector>
#include <set>
using namespace std;
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
vector<int>::iterator itr;
set<int> temp;
for (itr = nums.begin(); itr != nums.end(); itr++) {
if (temp.insert(*itr).second == false) {
nums.erase(itr);
}
}
return nums.size();
}
};
I've added includes for completion.
The problem I've run into is if I have an input vector as so [0,0,1,1,2,2,3,3,4,4,5,5] my function will only erase the duplicates that are not 1 or 2.
Thus my answer will be [0,1,1,2,2,3,4,5]. My understanding is that set will not allow duplicate values but I don't understand why 1 and 2 is still duplicated.
This is because this is undefined behavior.
nums.erase(itr);
std::vector's erase() method invalidates all existing iterators "at or after the point of the erase". Since itr is the "point of the erase", itr is no longer a valid iterator when erase() returns. Subsequent attempt to increment it, in the for loop's iteration results in undefined behavior.
Your C++ textbook will have a more complete explanation of how to use the value that erase() returns, and what it means, in order to correctly avoid undefined behavior; but the capsule summary is:
itr=nums.erase(itr);
Note that now itr points to the value in the vector that's already after what was erase()d, which might be end(); whether it's end() or not you obviously don't want to increment it immediately. It should be obvious that you want to check if the immediately following value in the vector is another duplicate too, don't you agree?
So what you'll need to slightly rework your loop so that it:
Uses erase() correctly
Only increments the iterator if it does not erase() the duplicate value.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
This post was edited and submitted for review 7 months ago and failed to reopen the post:
Original close reason(s) were not resolved
Improve this question
for (auto& i : just_a_vec )
Or an iterator for loop.
for (std::vector<std::string>::iterator it = just_a_vec.begin(); it < just_a_vec.end(); it++)
Iterators predate range-based for loops, so they used to be the only of these two alternatives available. Nowadays, range-based for has mostly replaced iterators when dealing with a simple loop.
However, iterators can be used in various other contexts as well. For example, you can do std::sort(v.begin(), std::next(v.begin(), 5)) to sort the first five elements of a vector while leaving the rest of it alone.
Going back to iterating over a whole container:
If you can accomplish what you want with a range-based for, then it leads to more legible code, so they are preferable by default.
If you need iterators for some reason, such as using an algorithm that requires them, or because you need to jump ahead or back while iterating, then use those instead.
Also: In the later case, you can/should still use auto when declaring the iterator:
for(auto it = just_a_vec.begin(); it < just_a_vec.end(); it++) {
}
Edit: as asked: here's a simple, if a bit contrived, example where an iterator-based loop can still be useful:
// adds all values in the vector, but skips over twos values when encountering a 0
// e.g.: {1,2,0,4,5,2} => 5
int my_weird_accum(const std::vector<int>& data) {
int result = 0;
for(auto it = data.begin(); it != data.end(); ++it) {
auto v = *it;
result += v;
if(v == 0) {
// skip over the next two
assert(std::distance(it, data.end()) > 2);
std::advance(it, 2);
}
}
return 0;
}
Quick personal answer: Its somewhat sylistic and based on what version of c++ you are using. I typically prefer range based, but there are certainly moments iterators shine as well. Add both to your toolchest. For further reading.
Here is a list of other SO answers that get more into the performance and use cases.
What's the difference between iterator syntax in range-based loops for STL containers
Is the ranged based for loop beneficial to performance?
range based for loop vs regular iterator for loop
For further information. Google
Range vs iterator loop c++
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
so i have DEBUG_ERROR("vector iterators incompatible"); whenever i have collision detection between blackHole & planet. but when i have collision detection between anything else like asteroid & planet or blackHole & asteroid, then its work with no problem the code is basically the same but yet with this one i have crash
//Blackhole& Planet Collide
for (vector<Entity*>::iterator it = gameEntities.begin(); it < gameEntities.end(); ++it) // This loop usses an iterator (google c++ iterator) to go through all game entites objects
{
BlackHole* blackHole1 = dynamic_cast<BlackHole*> (*it); // As the iterator is of generic type Entity we need to convert it to a type we can use, Ball. Dynamic cast performs the conversion only if the entity is actually a Ball otherwise it gives back a null pointer
if (blackHole1 != nullptr) // we check if the conversion was successful
{
for (vector<Entity*>::iterator it1 = gameEntities.begin(); it1 < gameEntities.end(); ++it1) // and we iterate on the remaining elements in the container
{
if (it != it1)
{
Planet* planet = dynamic_cast<Planet*> (*it1); // we convert the remaining element
if (planet != nullptr) // check if the conversion happended
{
// collision detection: black hole & planet
if (blackHolePlanetCollision(*blackHole1, *planet))
{
blackHole1->increaseMass(blackHole1->getRadius(), planet->getRadius());
delete *it1;
it1 = gameEntities.erase(it1);
//--it1;
any idea what is wrong with it? with any other collision this code works
it1 = gameEntities.erase(it1);
Here you have correctly reset it1 after erasing from the vector (which potentially invalidates existing iterators) … but you did not do the same for it, which is now potentially invalidated.
When it is invalidated and you later try to compare it against the new it1, your standard library implementation has caught the bug and is trying to tell you the comparison is invalid. In a release build I'd expect this to just silently do weird things.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have a std::vector<string> called v. I'd like to read in a string from the user and remove it from v. Here's what I have so far:
string rnames;
cout << "Enter the string to remove " << endl;
cin >> rnames;
v.erase(rnames);
This produces an error.
Any ideas how we can make this to work? Do we first need to find the rnames and then erase it?
The std::vector's erase method takes in an iterator saying which entry in the vector you want to remove. It doesn't work like the map or set where you can call erase to remove a specified key.
To remove a single copy of an element from a std::vector, you can use std::find and the std::vector's erase member function like this:
auto itr = std::find(v.begin(), v.end(), rnames);
if (itr != v.end()) v.erase(itr);
The above code assumes you're using C++11, which all major modern compilers support. However, it seems like you're using an older compiler that doesn't support C++11, so you'd have to write something like this:
std::vector<string>::iterator itr = std::find(v.begin(), v.end(), rnames);
if (itr != v.end()) v.erase(itr);
To remove all copies of an element from a vector, you can use std::remove like this:
v.erase(std::remove(v.begin(), v.end(), rnames), v.end());
(This requires the <algorithm> header.) That being said, if you find that you are often removing elements from a std::vector and you have a large number of elements in the vector, you may want to consider changing data structures to something that more efficiently supports removals.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
I'm writing a function to find element in the vector and return an iterator to that element:
vector<int>::iterator findelement(vector<int> &ivec, int target)
{
auto it=ivec.begin();
while(it!=ivec.end())
{
if(*it=target)
return it;
else ++it;
}
return ivec.end();
}
Then I copy the return iterator to another iterator. I can dereference the iterator correctly:
vector<int> numbers{1,2,3,4,5,6,7,8,9,10};
auto iter=findelement(numbers,5);
cout<<*iter;
The output is 5.
But,when I increment/decrement the iterator, there are somethings wrong:
--iter;
cout<<*iter; //I think the output should be 4, but the output is 134281917!
Is the return value of the findelement function wrong?
The first element of numbers is being overwritten by your search value. When the assignment happens, the assignment expression is true and returns the iterator to the first element.
if(*it=target) {
return it;
}
Because you are already at the beginning, the iterator is not decrementable (or depending on your compiler settings, will print out the value of the location just before your vector). To fix this, do a comparison instead of assignment (*it == target).
if(*it=target)
return it;
This assigns target to *it. Since target is 5, the value of the expression is 5 as well, which is equivalent to true. Thus, with a nonzero target, your function will assign target to the first element of ivec and return ivec.begin(). It should be obvious that decrementing that iterator results in undefined behavior.
Note that both g++ and clang emits a warning on this code with -Wall (clang emits a warning even without it). You should always compile your code at high warning settings.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
all
I am using vector in C++ STL to store my data. I pass and return them into and from functions. However, as the data size grows, the program is slower and slower. Thus I am updating the codes to an "iterator version".
What I want to archieve is that use iterators to pass, return and iterate STL vectors.
I am now ok with the operations with 1-dimensional vector, just like manipulating the arrays. However, when it comes to 2-dimensional vector, I am a bit confused.
Can anyone show me a simple code example that how to iterate a 2D vector using STL iterator?
Many thanks in advance.
Regards
Long
You state that your basic problem is performance, right?
You assume that this is caused due to copying.
Perhaps there could be simpler solutions for your problem:
Check if vectors can be passed by (const) reference
Check if shared_ptr makes sense
Consider if move semantics can help
Perhaps compiler version or implementation prevent return value optimization
If you need to know the size of a vector, and have two iterators it1 it2,
std::distance(it1, it2);
will tell you the distance between them. This will happen to be the size if they are begin and end
If you have a function like
int work(std::vector<int> items)
{
//...
}
this copies the vector items, so will use more RAM and take longer.
Sending a const ref instead will not copy the vector. Making it const stops you changing it, which might not help you, but you haven't posted any code so I don't know what you want to do.
int work(const std::vector<int> & items)
{
//...
}
Well its already somewhere on stackoverflow
But if you don't want to search here it is :
std::vector<std::vector<int> > vec{ {1,2,3},{4,5,6}};
//Simplest Way:- (C++11)
for(auto row:vec)
{
for(auto col:row)
std::cout<<col<< " ";
std::cout<<std::endl;
}
//OR Using iterator
std::vector<std::vector<int> >::iterator r;
std::vector<int>::iterator c;
for (r = vec.begin(); r != vec.end(); r++) {
for (c = r->begin(); c != r->end(); c++) {
std::cout<<*c<< " ";
}
std::cout<<std::endl;
}
Can get distance only between two iterators of same container
std::vector<int>::iterator s = v2.begin(); //Can be any start
std::vector<int>::iterator e = v2.end(); // Can be any end
std::cout<<"Distance :"<<std::distance(s,e)<<std::endl;