I have a list iterator that goes through a list and removes all the even numbers. I can use the list iterator to print out the numbers fine but I cannot use the list's remove() and pass in the dereferenced iterator.
I noticed that when the remove() statement is in effect, *itr gets corrupted? Can somebody explain this?
#include <iostream>
#include <list>
#define MAX 100
using namespace std;
int main()
{
list<int> listA;
list<int>::iterator itr;
//create list of 0 to 100
for(int i=0; i<=MAX; i++)
listA.push_back(i);
//remove even numbers
for(itr = listA.begin(); itr != listA.end(); ++itr)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
listA.remove(*itr); //comment this line out and it will print properly
}
}
}
There are a few issues with your code above. Firstly, the remove will invalidate any iterators that are pointing at the removed elements. You then go on to continue using the iterator. It is difficult to tell which element(s) remove would erase in the general case (although not in yours) since it can remove more than one.
Secondly, you are probably using the wrong method. Remove will iterate through all of the items in the list looking for any matching elements - this will be inefficient in your case because there is only one. It looks like you should use the erase method, you probably only want to erase the item at the position of the iterator. The good thing about erase is it returns an iterator which is at the next valid position. The idiomatic way to use it is something like this:
//remove even numbers
for(itr = listA.begin(); itr != listA.end();)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
itr=listA.erase(itr);
}
else
++itr;
}
Finally, you could also use remove_if to do the same as you are doing:
bool even(int i) { return i % 2 == 0; }
listA.remove_if(even);
You can't use an iterator after you delete the element it referred to.
However, list iterators which refer to non-deleted items after a remove() should remain valid.
Could we use something like this:
container.erase(it++);
I tried on this example:
int main(){
list<int>*a=new list<int>;
a->push_back(1);
a->push_back(2);
a->push_back(3);
list<int>::iterator I;
I=a->begin(); ++I;
a->erase(I++);
cout<<*I<<endl;
}
and it displayed 3, as I wanted. Now I don't know if this is valid or one of those which "sometimes work and sometimes not".
EDIT: Maybe it is because of compiler. For example, compiler I am using (GNU gcc-g++) is treating lists (std::) as circular, ie if I increase iterator after list->end() it puts you at the beginning.
Since iterators depend on the length of the structure remaining the same, most iterators do not allow a list to be changed while the iterator is in use. If you want to go through and change the list, you're going to have to use a loop independent of the iterator.
Related
I have a program where I have to find the most common element in a list of integers. I do this with the program below, but the problem is, I suspect that the erase function messes up with the iterator incrementation in the countRepetition() function. My question is how can I fix the problem or if this is not the issue what is it?
Thanks in advance.
You have a couple issues. First, as you suspected, was the incorrect use of erase. When you erase an iterator it invalidates the iterator. Any use of the iterator afterwards is undefined behavior. Since erase returns the next valid iterator what you can do is restructure the loop like
for (START = l.begin(); START != l.end();) { // do not increment here
if (*START) {
counter++;
START = l.erase(START); // erase and get next
}
else
{
++START; // go to next
}
}
So now at least you loop through the list. Unfortunately you will still have an invalid iterator in main. You pass START from main to countRepetition and when that iterator is erased from the list you then have an invalid iterator. What you need to do is get a new begin iterator from the list each iteration since you are always erasing the first element. That would make your for loop look like
for (START = l.begin(); START != l.end(); START = l.begin()) {
m.push_back(countRepetition(START));
}
Another issue is you just check if the character is not 0. If you are counting repetitions you need to make sure you are checking that the iterator is the same character. I'll leave that for you to implement.
I would also like to point out there is an easier way to do all of this. A std::map lets you build a histogram very easily. Combine that with std::max_element and you could write your entire program as
int main()
{
std::map<char, int> histogram;
while ('0' != (number = getchar()))
++histogram[number]; // add to map, increment count of occurances
auto most_frequent = *std::max_element(histogram.begin(),
histogram.end(),
[](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }).first;
std::cout << most_frequent;
return 0;
}
Your problem is that you use global variables everywhere.
The global START is changed in two loops, so you only access the first loop once, then it is changed again in the second function and you don't execute the first loop a second time.
Why do you use the global variables? You should not use them but use local variables.
This is probably what you are looking for:
#include <iostream>
#include <list>
#include <vector>
#include <map>
using namespace std;
list <char> l;
map<char, int> ans;
int main()
{
char c;
do{
c = getchar();
l.push_back(c);
}while(c != '0');
for(auto chr: l){
ans[chr]++;
}
char ch;
int mx = 0;
for(auto k: ans){
if(k.second > mx)
{
ch = k.first;
mx = k.second;
}
}
cout<<ch<<" : "<<mx;
}
I want to reverse the list a={1,2,3,4,5} using an iterator.According to my knowledge i know an iterator is a pointer that points to an element in the list.
So there are two questions which are as follows:
Why does this code fail even though I am pointing to the last element in the list and then decrementing the pointer to the previous address?
list<int>::iterator i;
for(i=l.end();i!=l.begin();i--)
cout<<(*i);
Second question is how do you access the third element in the list using an iterator without a for loop.Is it possible to access it without using a for loop?Please state the syntax if it is possible.
l.end() points after the last element, so in principle you need:
for(i=l.end()-1; i!=l.begin(); i--) // invalid
but then you'll not get the first element (l.begin()) as you stop when you reach it.
Further it is not valid to subtract 1 from a list iterator so it must be:
for(i=std::prev(l.end()); i!=l.begin(); i--) // valid
But still you have the problem with the first element.
A better way is to use reverse iterator. Something like:
for (std::list<int>::reverse_iterator i = l.rbegin(); i != l.rend(); ++i)
For your second question you could do:
auto it = l.begin();
cout << *(std::next(it, 2));
Here is a list of valid operations depending on iterator type: http://www.cplusplus.com/reference/iterator/
And here you can see that list has a bidirectional iterator: http://www.cplusplus.com/reference/list/list/
You can use reverse_iterator to travel a container in reverse.
list<int>::reverse_iterator i;
for(i=l.rbegin();i!=l.rend();i++)
cout<<(*i);
As for acessing the 3rd element of the list you have to iterate to it. There is no direct access to elements in a list
You can do it also By list:
for (std::list<int>::reverse_iterator rit=mylist.rbegin(); rit!=mylist.rend(); ++rit)
std::cout << ' ' << *rit;
You can do it By Vector in C++.
int temp[] = {1,2,3,4,5};
std::vector<int>v(temp, temp+3);
std::vector<int> n_v(v.rbegin(), v.rend());
Also, if you want to reverse a list in place, you can use this std::reverse.
#include <iostream>
#include <list>
#include <algorithm>
int main()
{
std::list<int> values { 1, 2, 3, 4, 5 };
std::reverse(values.begin(), values.end());
for (int i : values)
std::cout << i << " ";
}
I have a vector<Suggestions> finalSuggestions that contains a string word and some int num.
If this word meets some condition, I want to move that object to the front of the vector, removing it from wherever it was.
I am able to insert to the beginning of the list with vector::insert
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
break;
}
}
But I do not know how to remove it from where it is in the list.
For example, for some arbitrary vector { 1,2,3,4,50,6,7,8,9 },
if 50 meets the criteria, move it to the front of the list and delete it from where it was, returning { 50,1,2,3,4,6,7,8,9 }. The code above returns { 50,1,2,3,4,50,6,7,8,9 }
I was looking into vector::erase, but I'm having problems, and its taking longer than it should.
I envision a simple solution (but this obviously doesn't work)
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
finalSuggestions.erase(x);
break;
}
}
I read up on the erase-remove idiom (here is my implementation):
finalSuggestions.erase( remove( begin(finalSuggestions), end(finalSuggestions), x ), end(finalSuggestions) );
but am getting an error that I don't understand:
In instantiation of '_FIter std::remove(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<Suggestion*, std::vector<Suggestion> >; _Tp = Suggestion]':|
Use std::rotate. It's a lot faster than deleting and reinserting.
Eg:
for (auto it = finalSuggestions.begin(), lim = finalSuggestions.end();
it != lim;
++it) {
if (it->num < 50 * topword.num) {
std::rotate(finalSuggestions.begin(), it, it + 1);
break;
}
}
Even better, as #JerryCoffin suggests in a comment, use std::find_if to find the pivot:
auto pivot = std::find_if(finalSuggestions.begin(),
finalSuggestions.end(),
[&topword](const Suggestions& s) -> bool {
return s.num < 50 * topword.num;
});
if (pivot != finalSuggestions.end()) {
std::rotate(finalSuggestions.begin(), pivot, pivot + 1);
}
For vector::erase you need an iterator, so range-based for can't be used. Use simple for loop instead. First erase an element, and then insert it, because insert invalidates iterators:
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (some_condition(*it)) {
auto x = *it; // or std::move(*it)
finalSuggestions.erase(it);
finalSuggestions.insert(finalSuggestions.begin(), x /* or std::move(x) */);
break;
}
}
Using std::move will allow you to move an element around instead of copying it, which may save you some cycles.
Your iterator makes it difficult to know the position of the element in question. You might want to try using a standard for iterator which allows access to the position (used by std::vector::erase)
int len=finalSuggestions.size();
for (int i=0, ; i<len; ++i) {
// Save a copy of the item
auto item = finalSuggestions.at(i);
if (double((item.num)/(topword.num)) < 50) {
// Erase the item in the list
finalSuggestions.erase(i);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
... or using a std::iterator ...
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (double((*it->num)/(topword.num)) < 50) {
// Save a copy of the item
auto item = *it;
// Erase the item in the list
finalSuggestions.erase(it);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
std::vector objects use contiguous memory for their elements, which means actually moving memory around during altering of the container. If you are going to be moving elements around you may want to look into std::list or std:deque. The definition of these containers are nearly identical (read: drop in replacements) to each other making it fairly straight-forward to replace them.
Suggestion:
The std::deque is designed for optimal insertions at both the beginning and the end of the container. Taken from the site cplusplus.com:
... they provide a functionality similar to vectors, but with efficient insertion and deletion of elements also at the beginning of the sequence, and not only at its end. But, unlike vectors, deques are not guaranteed to store all its elements in contiguous storage locations: ...
Anton's answer is correct. However if you do this sort of thing a lot you should consider a different data structure. Both the erase and the insert are O(N) operations, where N is the size of the vector. A list would be better if this is a common operation.
It is functionally equivalent to Anton's answer, but I would use std::find_if to get the an iterator to the element you are looking for instead of a loop.
//add #include <algorithm> to your source file
auto result = std::find_if(finalSuggestions.begin(), finalSuggestions.end(), condition_func);
if(result != finalSuggestions.end())
{
auto resultValue = *result;
finalSuggestions.erase(result);
finalSuggestions.insert(finalSuggestions.begin(), resultValue);
}
condition_func should be a function returning bool that takes a parameter matching the type of the elements in your vector (in this case, Suggestion):
bool condition_func(Suggestion elementValue) { /*condition here*/ }
More information on find_if is available here.
Maybe using std::iter_swap could solve your problem.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main () {
vector<int> myvector{};
for(int io{}; io<7; ++io) myvector.push_back(io+1);
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
cout << ' ' << *it;
cout << '\n';
iter_swap(myvector.begin(),myvector.begin()+2);//exchange the third element with the first.
cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
I'm trying to insert objects into a list in an ordered manner, based on the objects data member family_id
The first object created skips this function, so there will be at least one object in the list named 'families' before this function is executed.
Running into some trouble with the List insert function... How can you insert before or behind the iterator (itr) using this function? I'm not sure whether to use some push_front / push_back methods, but the if/else statement can get quite long this way...
Would appreciate ideas on how to create this in a simple way.
Family Class
class Family
{
private:
int family_id;
public:
int get_family_id()
{
return family_id;
}
};
Main
list <Family> families;
void insertFamily(int input_id)
{
list<Family>::iterator itr;
for(itr = families.begin(); itr != families.end(); itr++)
{
if (input_id < itr->get_familyid())
{
Family *fam = new Family(input_id);
families.insert(itr, *fam);
// Does this insert the object into the first position of the list,
// bumping the original to the second position?
} else
itr++
}
};
If you're asking what I think you're asking, the insert function will put the value into the position given, and push everything else back. For example:
#include <list>
#include <iostream>
int main(){
std::list<int> my_list;
for(int i = 0; i < 5; i++)
my_list.push_back(i);
std::list<int>::iterator itr = my_list.begin();
++itr;
my_list.insert(itr, 5);
for(itr = my_list.begin(); itr != my_list.end(); ++itr)
std::cout << *itr << " ";
}
Prints
0 5 1 2 3 4
So if you want it to insert BEFORE whatever you stop on, just call the insert as given; if you want to insert AFTER whatever you stop on, call
++itr
and then do the insert.
Edit: If I'm reading your code right, you're also missing a break statement
Family *fam = new Family(input_id);
families.insert(itr, *fam);
break;
^^^^
This breaks the for loop when you're done; without it, you'll keep adding new Families with the same id for every family with a lower index. Also, you don't need the itr++ in the else statement, since it's already being called in your for loop (unless you want to increase it twice when it's not matched?). Then your code might look like:
Main
list <Family> families;
void insertFamily(int input_id)
{
list<Family>::iterator itr;
for(itr = families.begin(); itr != families.end(); itr++)
{
if (input_id < itr->get_familyid())
{
Family *fam = new Family(input_id);
families.insert(itr, *fam);
break; //Added break line
} //Removed else
};
If you want faster insertion, you might look into binary searching, but this won't do much unless you have a very large list. Other than that, your code looks good!
#include<iostream>
#include<list>
using namespace std;
void compute(int num)
{
list<int> L;
list<int>::iterator i;
list<int>::iterator i2;
int p;
cout<<"Enter the number of numbers\n";
cin>>p;
int a;
for(int k=1;k<=p;k++)
{
cin>>a;
L.push_back(k);
}
cout<<endl;
for(i=L.begin() ; i!=L.end() ; ++i)
{
cout<<*i<<endl;
}
long int k=1;
for(i=L.begin() ; i!=L.end() ; ++i )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i2=L.erase(i);
}
k++;
}
for(i=L.begin() ; i!=L.end() ; ++i )
{
cout<<*i<<endl;
}
}
int main()
{
// int testcases, sailors;
//cin>>testcases;
//for(int i=1 ; i<=testcases ; i++)
{
// cin>>sailors;
}
//for(int i=1;i<=testcases;i++)
{
// int num;
//cin>>num;
//compute(num);
}
compute(0);
return 0;
}
I am trying to erase elements using L.erase() function in Lists. But I get an error saying
"Debug assertion failed! ......Expression:list iterator not incrementable"
but we CAN increment iterator right?
erase invalidates the iterator that was passed in as parameter - since the element at the position the iterator was pointing to was just erased! And on that same iterator, an increment is attempted in the next for loop in your code! That's why it fails.
However, erase it will return an iterator pointing to the new position, which we can use; a loop where you erase something from an STL container should therefore look something like the following; I show it with the type you use, list, but you could just as well use e.g. vector:
list<int> L;
// ...
list<int>::iterator it=L.begin();
while (it!=L.end())
{
if(eraseCondition)
{
it=L.erase(it);
}
else
{
++it;
}
}
Or, if possible, it's even better to use std::remove_if:
container.erase(std::remove_if(L.begin(), L.end(), predicate), L.end());
In your case that will be hard - if not impossible - to use since the predicate would need state information (the information whether the index is odd or even). So I'd recommend going with a loop structure as mentioned above; just keep in mind the remove_if for the general case of removing all elements where a certain predicate returns true!
Adding to what wOOte said, you may want to used a reverse iterator to get around the issue.
Technically not in this case.
When you use erase() you delete the node that was pointed to, so you actually invalidate the iterator you were on. So when you increment it it's undefined behavior.
It might be best to create a second list with just the iterators to the positions you'd like to delete, and you can cycle through those and call erase afterward. You wouldn't be erasing the iterators from the second list, so it'd work.
Something like this:
List<IteratorType> deleteList;
//Populate deleteList with every other element from original list.
for (List<IteratorType>::iterator iter = deleteList.begin();
iter !=deleteList.end; ++iter)
{
originalList.erase(*iter);
}
The iterator i is invalidated by the call to erase; however, in the next iteration of the for loop, you try to increment it - this is invalid.
Try
for(i=L.begin() ; i!=L.end() ; )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i=L.erase(i);
} else {
++i;
}
k++;
}
instead - only increment the iterator if you don't erase (erase basically "advances" the iterator because it yields an iterator to the element following the one you erased).
You can actually exploit this behaviour of erase to write your function without requiring k:
i = L.begin();
while ( i != L.end() ) {
i = L.erase( i ); // Delete one
if ( i != L.end() ) { // Skip next, if there's an element
++i;
}
}
So you delete the first element, skip the second, delete the third, and so on.