This question already has answers here:
How to remove from a map while iterating it?
(6 answers)
Closed 5 years ago.
I am trying to determine the maximum number of items I can remove from a list using std list to get the minimum size. However, it keeps ending up in bad memory access.
This is my recursive function:
int step (list<int> mylist) {
int count = mylist.size();
// Terminations
if (!checkRemaining(mylist)) {
return mylist.size();
}
if (mylist.empty()) {
return 0;
}
//printf("mysize: %d\n", mylist.size());
// Else we do not terminate first
for (auto i=mylist.begin(); i != prev(mylist.end()); ++i)
{
if ((*i + *next(i))%2 == 0) // Problem starts from here, bad access
{
mylist.erase(next(i));
mylist.erase(i);
printf("this size %lu\n", mylist.size());
list<int> tempList = mylist;
for (auto it = tempList.begin(); it != tempList.end(); it++) {
printf("%d ", *it);
}
printf("\n");
int temp = step (tempList);
if (temp < count) count = temp;
}
}
return count;
}
It managed to get down to the desired size but the program would crash due to bad memory access.
Once you do mylist.erase(i);, i is invalided, so your ++i in the loop is UB.
Your code should look like:
for (auto i = mylist.begin(); i != mylist.end() && i != prev(mylist.end()); /* Empty */)
{
if ((*i + *next(i)) % 2 == 0)
{
mylist.erase(next(i));
i = mylist.erase(i);
// maybe you want prev(i) if i != mylist.begin()
#ifdef DEBUG
std::cout << "this size " << mylist.size() << "\n";
for (const auto& e : myList) {
std::cout << e << " ";
}
std::cout << "\n";
#endif
count = std::min(count, step(myList));
} else {
++i;
}
}
In addition, final check should handle correctly when you remove last elements.
Related
I'm trying to remove duplicate combinations of integer vectors stored in a list using a hash table. Iterating over each integer vector in the list, I:
Calculate the hash_value (thash)
See if the hash value is already in the hash table (pids)
If it's in the hash table, erase that vector from the list.
Otherwise, add that value to the hash_table and increment the list
iterator
Print statements seem to confirm my logic, but the loop hangs at the fourth step of iteration. I've commented the it++ and vz.remove(it) that cause the problem and only show the logic in the code below. The code is also available through ideone: https://ideone.com/JLGA0f
#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes) {
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++) {
pid += lprimes[*it];
}
return pid;
}
int main(){
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back({2,1});
vz.push_back({1,2});
vz.push_back({1,3});
vz.push_back({1,2,3});
vz.push_back({2, 1});
// vector of log of prime numbers
std::vector<double> lprimes {2, 3, 5, 7};
for (auto it = lprimes.begin(); it != lprimes.end(); it++) {
*it = std::log(*it);
}
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); ) {
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end()) {
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
}
else {
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
}
it++;
}
for (auto it = vz.begin(); it != vz.end(); it++) {
for (auto j = it -> begin(); j != it -> end(); j++) {
std::cout << *j << ' ';
}
std::cout << std::endl;
}
return 0;
}
Problem is this line of code:
vz.erase(it);
It keeps iterator where it was ie leaves it invalid. It should be either:
vz.erase(it++);
or
it = vz.erase( it );
Note: std::unoredered_set::insert() return value tells you if insert was succesfull or not (if the same value element is there already), you should call it and check result. In your code you do lookup twice:
if (pids.insert(thash).second ) {
// new element added
++it;
} else {
// insertion failed, remove
it = vz.erase( it );
}
As std::list provides remove_if() your code can be simplified:
vz.remove_if( [&pids,&lprimes]( auto &v ) {
return !pids.insert( hash_cz(v, lprimes) ).second );
} );
instead of whole loop.
If the element has already been seen, you erase() the it node and then increment it at the end of the loop: undefined behaviour. Try erase(it++) instead.
If the element has not been seen, you increment it and then do it again at the end of for, yielding UB if it was end() - 1 as it moves past end.
In the below Code-snippet, I am trying to manipulate the contents of each of the lists present in the MAP mp but by returning a pointer to list corresponding map's key whose list needs modification. I am aware that a direct modification of the list contents is possible instead of calling getlist and then modifiying it, but I am new to STL and C++ and trying to learn STL by playing around a bit with Iterators and Lists.
When the below code is executed, a Segmentation fault is thrown at the line "(*lit) = 10". Can anyone help me understand what's going wrong here?
static void getlist(int num,map<int,list<int>> mp, list<int>** l_ptr )
{
map<int,list<int>>::iterator it = mp.begin();
while( it != mp.end())
{
if(it->first == num )
{
*l_ptr = &(it->second);
return;
}
it++;
}
}
int main()
{
map<int,list<int>> mp;
mp[1] = {2,2,2};
mp[2] = {3,3,3};
mp[3] = {4,4,4};
map<int,list<int>>::iterator it = mp.begin();
list<int>::iterator lit;
list<int>* r_l = new list<int>;
//getlist(it->first,mp,r_l);
while( it != mp.end())
{
getlist(it->first,mp,&r_l);
lit = r_l->begin();
while(lit != r_l->end())
{
(*lit) = 10;
lit++;
}
it++;
}
it = mp.begin();
while( it != mp.end())
{
lit = (it->second).begin();
while(lit != (it->second).end())
{
cout<<(*lit);
lit++;
}
it++;
}
return 0;
}
So i need to be able to do this in c++. what i was able to do is using "abaaacc" as a string and i got the right answer, but i don't know how to solve it when "a b a a a c c" are in a linked list. can someone help me with a code:
here is my code
#include <iostream>
using namespace std;
const int SIZE=20;
int main ()
{
int numbs[SIZE], value, idx,n;
cout<<"PLease enter size of an array"<<endl;
cin>>n;
cout << "Please enter in a series of numbers "<<endl;
for(idx = 0; idx < n; idx++)
cin >>numbs[idx];
cout<< numbs[0] << " ";
for (int i = 1; i < n; i++)
{
bool matching = false;
for (int j = 0; (j < i) && (matching == false); j++)if (numbs[i] == numbs[j]) matching = true;
if (!matching) cout<< numbs[i] << " ";
}
}
now i want it to remove the duplicate adjacent and give me 1 copy of it
like the ex but using numbers so how can i be able to edit my code to do that.
I had some time and so I tried to solve this.
It is indeed a tricky thing. You need to take care of the edge cases first and last item, and 1-2 item lists.
In between you need to iterate with three iterators at the same time to find the unique item in the middle of these subsets. And as you are dealing with a list, you need to work around the missing random access iterators.
I'm more used to Python these days, where complex iteration is comfortable and you can get away with zips and slices nicely in this case. Maybe the new ranges could have been used to nicen up this code. Maybe I'll try this some time.
#include <list>
#include <iostream>
std::list<char> remove_adjacent_duplicates(std::list<char> input) {
std::list<char> output;
std::list<char>::iterator first = input.begin();
if (first == input.end()) {
// no first, item, exit
return output;
}
// let second point to second element
std::list<char>::iterator second = input.begin();
++second;
if (second == input.end()) {
// no second item, insert first, then exit
output.push_back(*first);
return output;
}
// check first item
if (*first != *second) {
// first and second are different, insert first
output.push_back(*first);
}
// let third point to third item
std::list<char>::iterator third = input.begin();
++third; ++third;
// check items up until the last
while (third != input.end()) {
if (*first != *second && *second != *third) {
// the second item neither equals the first, nor the third
// insert second
output.push_back(*second);
}
// increment iterators
++first; ++second; ++third;
}
// finally, check the last item
if (*first != *second) {
// the last two items differ, insert the latter
output.push_back(*second);
}
// done
return output;
}
void test_case(std::list<char> l) {
std::list<char> output = remove_adjacent_duplicates(l);
for (char i : l) {
std::cout << i << ' ';
}
std::cout << " -> ";
for (char i : output) {
std::cout << i << ' ';
}
std::cout << '\n';
}
int main() {
test_case({'a'});
test_case({'a', 'b'});
test_case({'a', 'b', 'a', 'a', 'a', 'c', 'c'});
}
The output is:
$ g++ test.cc -std=c++11 && ./a.out
a -> a
a b -> a b
a b a a a c c -> a b
Simple enough. First you std::sort the container and then you use std::unique (combined with erase) to remove all but one instance of each value.
I have the following method in my program.
Weird thing is the data is not removed after I call erase.
Any idea?
map<int,obj>::iterator it = this->indexMap.find(id);
if(it != this->indexMap.end())
{
int mapSize = this->indexMap.size();
int dataSize = (*it).second.getDataMap().size();
//copy data to another node | even when it doesn't get into this if condition, it does not remove the data
if(mapSize> 1 && dataSize != 0)
{
it++;
this->copyData(id,it->first);
it--;
}
//remove peer | i've tried id and it, both does not work
this->indexMap.erase(it);
map<int,obj>::iterator iter = this->indexMap.find(id);
if(iter == this->indexMap.end())
{
cout << "ERROR" << endl;
}
}
Output:
ERROR
Thanks! :)
This block:
map<int,obj>::iterator iter = this->indexMap.find(id);
if(iter == this->indexMap.end())
{
cout << "ERROR" << endl;
}
prints out ERROR if an element with key id is not found in the map. Hence it has been removed.
I am having troubles with erasing elements from sets. I get BUILD FAILED from:
n2Ar.erase(it);
n3Ar.erase(it);
where it is a pointer received from find() function: e.g. it = n2Ar.find(*i);
The whole listing of the program:
#include <stdio.h>
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
#define TESTING_FILE_IN
//#define TESTING_FILE_OUT
//#define DEBUG
//#define SHOW_TIMING
int outputSet(int i) {
cout << i << endl;
}
/*
*
*/
int main() {
int n1, n2, n3;
set<int> list, n1Ar, n2Ar, n3Ar;
set<int>::iterator it;
scanf("%d", &n1);
scanf("%d", &n2);
scanf("%d", &n3);
int val = 0;
// Getting lists of voters
for (unsigned i = 0; i < n1; i++) {
cin >> val;
n1Ar.insert(val);
}
for (unsigned i = 0; i < n2; i++) {
cin >> val;
n2Ar.insert(val);
}
for (unsigned i = 0; i < n3; i++) {
cin >> val;
n3Ar.insert(val);
}
// Processing lists
for (set<int>::iterator i = n1Ar.begin(); i != n1Ar.end(); ++i) {
it = n2Ar.find(*i);
if (it != n2Ar.end()) {
list.insert(*i);
n1Ar.erase(i);
n2Ar.erase(it);
} else {
it = n3Ar.find(*i);
if (it != n3Ar.end()) {
list.insert(*i);
n1Ar.erase(i);
n3Ar.erase(it);
}
}
}
// Outputting the final list
cout << list.size() << endl;
for_each(list.begin(), list.end(), outputSet);
return 0;
}
I hope you'll be able to help me understand what I am doing wrong in here. I am only starting with C++.
There are two problems in your code.
First, you need return a value in the following function, or simply make it return void.
// you should return a value here or make it return void
int outputSet(int i)
{
cout << i << endl;
}
Second, the iterators in the following iterations of your for-loop are invalidated once you remove the current one. Once an element is removed, its iterator i is also invalidated, so as to the following iterators based on ++i;
And you'll get run-time error because iterator i now points to You need somehow "reset" it.
MSVC Implementation
for (set<int>::iterator i = n1Ar.begin(); i != n1Ar.end(); ++i) {
it = n2Ar.find(*i);
if (it != n2Ar.end()) {
list.insert(*i);
// the following iterators become invalidated after the
// current one is removed. You need reset it like
// i = n1Ar.erase(i);
n1Ar.erase(i);
n2Ar.erase(it);
} else {
it = n3Ar.find(*i);
if (it != n3Ar.end()) {
list.insert(*i);
// the following iterators become invalidated after the
// current one is removed. You need reset it like
// i = n1Ar.erase(i);
n1Ar.erase(i);
n3Ar.erase(it);
}
}
}
Edit: Note that returning a new iterator from set::erase() is not a Standard way. That's mainly for the purpose of performance.
A More Portable Solution
The basic idea is to correctly set the next iterator before removing the current one.
set<int>::iterator i = n1Ar.begin();
while (i != n1Ar.end())
{
it = n2Ar.find(*i);
if (it != n2Ar.end())
{
// the trick is to use "i++" where i is incremented by one while "old" i
// is removed.
list.insert(*i);
n1Ar.erase(i++);
n2Ar.erase(it);
}
else
{
it = n3Ar.find(*i);
if (it != n3Ar.end())
{
list.insert(*i);
n1Ar.erase(i++);
n3Ar.erase(it);
}
else
{
++i;
}
}
}
n1Ar.erase(i);
The std::set::erase function invalidates the iterator i and causes the problem. Consider change to the following:
for (set<int>::iterator i = n1Ar.begin(); i != n1Ar.end(); ++i) {
it = n2Ar.find(*i);
if (it != n2Ar.end()) {
list.insert(*i);
i = n1Ar.erase(i);
if(i == n1Ar.cend())
break;
n2Ar.erase(it);
} else {
The if(i == n1Ar.cend()) break; check helps to ensure the invalidated iterator will not ruin the loop.
Erase method invalidates the iterator i.
Erase method does not return iterator.
EDIT: repeating Eric idea you can use the following code:
for (set<int>::iterator i = n1Ar.begin(); i != n1Ar.end(); )
if ( n2Ar.erase(*i) || n3Ar.erase(*i) ) {
list.insert(*i);
n1Ar.erase(i++);
} else i++;
Also this problem could be solved using standard algorithms. But this solution seems to be less efficient:
set<int> tmp;
std::set_union( n2Ar.begin(), n2Ar.end(),
n3Ar.begin(), n3Ar.end(), std::inserter(tmp,tmp.begin()) );
std::set_intersection( n1Ar.begin(), n1Ar.end(),
tmp.begin(), tmp.end(), std::inserter(list,list.begin()) );
Finally i suggest to use stl for your output (you have to include iterator library):
std::copy( list.begin(), list.end(), std::ostream_iterator<int>(std::cout,"\n"));