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;
}
Related
I'm working on some c++ problems from a book and my task is to count number occurrences in a list. The task can be solved with just one iterator walking on each item of the list. However, I've done this before with other other task and I'm trying something different with this one. The idea is basically to use two iterators that increase a counter whenever the step on a value until they meet each other. I'm done with that for just the first value of the list but now I want to do it for the rest of the different numbers of the list. The main problem currently is that I can't find out why the programs hangs when I pass an iterator as an argument since creating the start iterator within the findNumberOccurrences and initialize it with the input.begin() value doesn't have any problem if that makes sense.
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
int findNumberOccurences(std::list<int> input, std::list<int>::iterator& start) {
std::cout << *start << '\n';
std::list<int>::iterator end = input.end();
int count = 1;
while (std::distance(input.begin(), start) != std::distance(input.begin(), end)) {
while (*end != *start) {
--end;
}
++count;
++start;
if (std::distance(input.begin(), start) == std::distance(input.begin(), end)) {
break;
}
while (*start != *end) {
std::cout << *start << '\n';
++start;
}
++count;
--end;
}
return count;
}
int main() {
std::list<int> input = {3, 4, 4, 2, 3, 3, 4, 3, 2};
std::list<int>::iterator start = input.begin();
int count = findNumberOccurences(input, start);
std::cout << count << '\n';
return 0;
}
I've written some cout logs within each while loop but I don't anything that gives me a clue about what's going on. Weird thing is that my terminal cursor keeps blinking without throwing exception like when having an infinite loop.
Can anyone shed some light on what I'm doing wrong please?
Cheers!
Alright, I've just found the answer to this question. Basically, I must pass a reference of the list as a function argument like this:
int findNumberOfOccurrences(std::list<int> &input, std::list<int>::iterator &start)
I did that intuitively but I want to know why this solves the problem so I'm gonna read about references. If anyone wants add anything to the answer, please by all means.
Passing the container by reference "fixes" the immediate problem in the code, but the code itself is flawed. When you're dealing with iterators you don't need the underlying container; indeed, there often isn't an underlying container. That's the point of iterators: they provide an abstraction that gives your code more flexibility.
In this case, just write the function to take two iterators:
int findNumberOccurences(std::list<int>::iterator first,
std::list<int>::iterator last) {
// code goes here
}
It's not clear to me what the code in the question is actually doing, so I haven't reproduced it. But in general, you can use first and last as the two ends of the data sequence and manipulate them appropriately.
while (first != last) {
// do something with *first
++first;
}
I'm trying to make a while loop for some code that has two iterators, one that starts at the beginning of the list, and is incremented and another that started at the end and is decremented, wanting them to stop once the middle of the list is reached and the whole list has been covered. When doing a similar thing with vector iterators I was able to just do while (limit > first), but this doesn't work and gives a compiler error when done with list iterators. I'm working on a book problem that has a task requirement of not allocating any new memory in the code aside from the two given iterators and am having trouble figuring out how to move through the elements of the list properly.
Reverse iterators dereference to the prior element than the one they internally refer to (the "base"). (See this image for a visual explanation.)
What this means is that if you have:
auto forward = a_list.begin();
auto backward = a_list.rbegin();
And you increment forward and backward alternately, there will be a time when forward == backward.base(). You must test this after incrementing each iterator, not both otherwise they could cross each other before you test them.
You can use std::list::size:
#include <list>
using namespace std;
void handle(int v) { cout << v << endl; }
void main() {
list<int> l = { 1,2,3,4,5,6,7,8,9 };
auto f = l.cbegin();
auto r = l.crbegin();
const int stepsCount = l.size() / 2;
for (int i = 0; i < stepsCount; ++i) {
handle(*f); handle(*r);
f++; r++;
}
if (l.size() % 2) handle(*r);
}
A number of posts I've read lately claim for(const auto &it : vec) is the same as using the longer iterator syntax for(std::vector<Type*>::const_iterator it = vec.begin(); it != vec.end(); it++). But, I came upon this post that says they're not the same.
Currently, I'm trying to erase an element in a for loop, after it is used, and wondering if there is any way to convert const auto &it : nodes to std::vector<txml::XMLElement*>::iterator?
Code in question:
std::vector<txml2::XMLElement *> nodes;
//...
for (const auto &it : nodes)
{
//...
nodes.erase(it);
}
I pretty sure I could just rewrite std::vector<txml2::XMLElement*> as a const pointer, but would prefer not to since this code is just for debugging in the moment.
You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.
You should use the Erase-remove idom.
You can use it with remove_if.
It would look something like:
nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){
//decide if the element should be deleted
return true || false;
}), nodes.end() );
Currently in the technical specifications, is erase_if.
This is a cleaner version of the same behaviour shown above:
std::erase_if(nodes,[](auto it){
//decide if the element should be deleted
return true || false;
});
You don't get an iterator but a reference to the element. Unless you want to do a std::find with it, it's pretty hard to get an iterator out of it.
Vectors are nice, so you could increase a counter per element and do nodes.begin() + counter to get the iterator, but it'd sort of defeat the point.
Also erasing the iterator in the for loop will result in you iterating after the end of the vector, you can test this code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {0,1,2,3,4,5,6};
for (int x : v) {
cout << x << endl;
if (x == 2) {
v.erase(v.begin() + 2);
}
}
return 0;
}
If you want to use iterators, just do a loop with them, if in addition you want to erase one mid-loop you have to follow this answer:
for (auto it = res.begin() ; it != res.end(); ) {
const auto &value = *it;
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
Note that you don't need to specify the whole type of the iterator, auto works just as well.
I'm trying to detect whether every single element of the vector is fullfilling given condition, let's say it must even number.
#include <iostream>
#include <vector>
#include <algorithm>
bool isOdd(int i)
{
return i%2==0;
}
int main()
{
int arr[5]={1,2,3,4,5};
std::vector<int> myVec(arr, arr + sizeof(arr)/sizeof(arr[0]));
std::vector<int>::iterator it = std::find_if(myVec.begin(), myVec.end(),
isOdd());
// This piece of code is probably causing some problems;
while(myVec.empty()!=false) // while my vector IS NOT EMPTY
{
std::cout << *it << " "; // print out the value of elements that
// fullfiled the condition given in isOdd
}
return 0;
}
What is wrong with my way of thinking ? Is the condition in while loop wrong or maybe I've completely missed the logic ?
Can you please provide me with some complex explanation of what is wrong with this piece of code ?
Thank you in advance.
P.S. I know that there is a possibility to use lambda function instead, but I don't want to get too confused :)
The problem with your approach is that you are finding the odd number only once, and then for some reason you expect the vector to change, without making any modifications.
You should make a loop that calls find_if repeatedly, like this:
bool isOdd(int i) {
return i%2!=0;
}
...
std::vector<int>::iterator it = myVec.begin();
for (;;) {
it = std::find_if(it, myVec.end(), isOdd);
if (it == myVec.end()) {
break;
}
std::cout << *it << " ";
++it;
}
Demo.
Note: I changed your isOdd function to return true for odd numbers. The original version was returning true for even numbers.
find_if returns the iterator pointing to the first value which meets the given condition. It stops there. You can put this in a loop to find all such elements, until it returns the end iterator.
The following line does the exact opposite of what you meant:
while(myVec.empty()!=false) // while my vector IS NOT EMPTY
Either write
while(myVec.empty()==false)
or
while(myVec.empty()!=true)
or simpler
while(!myVec.empty())
You could write it as a for-loop:
for (auto it = find_if(begin(myVec), end(myVec), isOdd);
it != end(myVec);
it = find_if(it, end(myVec), isOdd))
{
// do something with "it"
}
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.