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"));
Related
I read in cppreference that erase does not affect iterators and references other than erased ones. But if I don't understand why following code does not work:
#include <iostream>
#include <list>
int main()
{
std::list<int> lst;
lst.push_back(5);
lst.push_back(10);
lst.push_back(20);
int i = 0;
for (auto it = lst.begin(); it != lst.end(); ++it) // FIRST LOOP
{
if (i == 1) { lst.erase(it); }
++i;
}
for (auto el : lst) // SECOND LOOP
{
std::cout << el << std::endl;
}
return 0;
}
First loop never stops which causes Process finished with exit code 139 (interrupted by signal 11: SIGSEGV). But I don't erase everything works fine.
So what is the problem?
UPD:
Tried to change
...
for (auto it = lst.begin(); it != lst.end(); ++it) // FIRST LOOP
...
to
...
auto end = lst.end();
for (auto it = lst.begin(); it != end; ++it) // FIRST LOOP
...
but it did not help
When you delete the element at it, you cannot increment it anymore. Therefore, you need to use this instead:
if (i == 1) { it = lst.erase(it); }
Also, you need to solve not to increment it in this case. So likely the increment would go inside the loop:
int i = 0;
for (auto it = lst.begin(); it != lst.end(); ) // FIRST LOOP
{
if (i == 1)
{
it = lst.erase(it);
}
else
{
++it;
}
++i;
}
I am trying to use STL-style iterators in Qt. When I build for loop:
for(auto i = list.rbegin(); i != list.rend(); ++i)
{ }
it works but if I build like:
for(auto i = list.rend(); i != list.rbegin(); --i)
{ }
it doesn't work and gives an error. I wonder why can't i use reverse iterator from start to end. Any explanation or source will be helpful.
#include <QString>
#include <QDebug>
#include <QList>
int main() {
QList<QString> list;
list << "A" << "V" << "C" << "D";
// this works
for(auto i = list.rbegin() ; i != list.rend(); ++i)
{ }
// error: this doesn't work
for(auto i = list.rend(); i != list.rbegin(); --i)
{
*i = i-> toLower();
qDebug() << *i;
}
}
You can't dereference the past-the-end iterator. This is not unique to reverse iterators.
This would be correct:
if (!list.empty())
{
for (i = list.rend()-1; ; --i)
{
// your code here
if (i == list.rbegin())
break;
}
}
… but it's pretty ugly.
If you want to reverse the direction of your loop, switch from reverse iterators back to normal ones:
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
{
// your code here
}
This ugliness is the reason we have reverse iterators in the first place.
#include <bits/stdc++.h>
using namespace std;
#include <unordered_set>
#include <queue>
struct word {
string s;
int level;
word(string a, int b)
: s(a)
, level(b)
{
}
};
bool isadj(string s1, string s2)
{
int len = s1.length(), count = 0;
for (int i = 0; i < len; i++) {
if (s1[i] != s2[i])
count++;
if (count > 1)
return false;
}
return count == 1 ? true : false;
}
int ladderLength(string beginWord, string endWord, vector<string>& wordList)
{
unordered_set<string> st;
for (string s : wordList)
st.insert(s); // adding elements into a set
if (st.find(endWord) == st.end())
return 0;
queue<word> q;
q.push(word(beginWord, 0)); // initialising the queue
while (!q.empty()) {
word temp = q.front(); // pop the current string
q.pop();
if (temp.s == endWord)
return temp.level;
for (auto it = st.begin(); it != st.end(); it++) { // loop over the set to find strings at a distance of 1 and add them to the queue
if (isadj(temp.s, *it)) // i have inserted code here to print the string *it
{
q.push(word(*it, temp.level + 1));
st.erase(*it); // delete the element to avoid looping
}
}
}
return 0;
}
int main()
{
// make dictionary
vector<string> D;
D.push_back("poon");
D.push_back("plee");
D.push_back("same");
D.push_back("poie");
D.push_back("plie");
D.push_back("poin");
D.push_back("plea");
string start = "toon";
string target = "plea";
cout << "Length of shortest chain is: "
<< ladderLength(start, target, D);
return 0;
}
The problem i am trying to solve is https://leetcode.com/problems/word-ladder/
I am unable to trace where I am using a memory that was deallocated again in my program?
The following are my attempts to debug :
I tried to run it on another online ide where the code compiles and runs successfully but gives a wrong answer . in order to debug it I have inserted some lines into my code in order to print all the strings which are at a distance of 1 for my current string. surprisingly an empty string is appearing to be in the set. Please help me in understanding where am I doing a mistake.
unordered_set::erase returns a value, and this returned value is important. You should not ignore it.
In your case, once you erase something from the set, it is invalid. Trying to increment it results in Undefined Behavior.
The correct approach is to replace the current iterator with the returned one, then not increment during the loop.
for (auto it = st.begin(); it != st.end(); )
if (...) {
// ...
it = st.erase(*it);
} else
++it;
After the line:
st.erase(*it); // delete the element to avoid looping
the it iterator is not valid and should not be used.
Your problem seems to be already addressed, but if you'd be interested, this'd also pass without using std::queue, only using std::unordered_set:
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
std::cout.tie(NULL);
return 0;
}();
// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_set>
#include <algorithm>
using ValueType = std::int_fast16_t;
static const struct Solution {
static const int ladderLength(
const std::string start,
const std::string end,
const std::vector<std::string>& words
) {
std::unordered_set<std::string> words_map(std::begin(words), std::end(words));
std::unordered_set<std::string> head;
std::unordered_set<std::string> tail;
std::unordered_set<std::string>* curr_head;
std::unordered_set<std::string>* curr_tail;
if (words_map.find(end) == std::end(words_map)) {
return 0;
}
head.insert(start);
tail.insert(end);
ValueType ladder = 2;
while (!head.empty() && !tail.empty()) {
if (head.size() < tail.size()) {
curr_head = &head;
curr_tail = &tail;
} else {
curr_head = &tail;
curr_tail = &head;
}
std::unordered_set<std::string> temp_word;
for (auto iter = curr_head->begin(); iter != curr_head->end(); iter++) {
std::string word = *iter;
for (ValueType index_i = 0; index_i < word.size(); index_i++) {
const char character = word[index_i];
for (ValueType index_j = 0; index_j < 26; index_j++) {
word[index_i] = 97 + index_j;
if (curr_tail->find(word) != curr_tail->end()) {
return ladder;
}
if (words_map.find(word) != std::end(words_map)) {
temp_word.insert(word);
words_map.erase(word);
}
}
word[index_i] = character;
}
}
ladder++;
curr_head->swap(temp_word);
}
return 0;
}
};
You might want to break it into more methods, a bit too long for a function.
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.
Here i am trying to print the frequency of each word in the sentence, which is stored in the vector of string
void display_by_word (vector<string> vs) //pass by value is necessary because we need to delete the elements.
{
vector<string> :: size_type vec_size, i;
string to_cmp = vs[0];
int occ = 0;
for ( i = 0; i < vs.size(); ++i){
vector <string> :: iterator it = vs.begin() + 1;
occ = 1;
for ( it ; it != vs.end(); ++it){
if ( vs[i] == *it){
vs.erase(it);
occ++;
}
}
cout << vs[i] << " " << occ << endl;
}
}
Sometimes it works fine but sometimes it crashes.what is wrong?
See http://en.cppreference.com/w/cpp/container/vector/erase
Invalidates iterators and references at or after the point of the erase [...]
After the erase has happened, you cannot reuse it because it has been invalidated. It's undefined behaviour, which can include random crashes.
However, erase returns an iterator to the element following the erased one, or to end() if it was the last element, which is why the solution with it = vs.erase(it); works.
Alternatively, consider using std::remove_if, followed by the two-argument erase, which is known as the Erase-Remove Idiom. It may turn out to be more elegant and more readable than a hand-written loop. Or just rewrite the whole function to use std::count_if.
You may want to rewrite the loop as something like this
while(it != vs.end())
{
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else
it++;
}
You may should do as below:
`
for ( it ; it != vs.end(); ){
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else{
++it;
}
}
`
I am currently programming a little game for the console with an 2D map. 2 Elements of my game are: destroying fields and an enemy, which spreads in a random direction (its getting bigger). These two "entities" are saved in a structure which contains two vectors (X and Y). I am now trying to erase an element of "_Enemy"(<-private instance of the structure in a class, same as "_DestroyedFields") if you destroy the field where the enemy is.
I tried a lot of different variations to do so and whats giving me the error least is this method (I already searched the internet for a while now an couldn't find a answer to my question):
for (std::vector<int>::iterator itEX = _Enemys.X.begin(), itEY = _Enemys.Y.begin();
itEX != _Enemys.X.end() && itEY != _Enemys.Y.end();
++itEX, ++itEY) {
for (std::vector<int>::iterator itX = _DestroyedFields.X.begin(),
itY = _DestroyedFields.Y.begin();
itX != _DestroyedFields.X.end() && itY != _DestroyedFields.Y.end();
++itX, ++itY) {
if (*itY == *itEY && *itX == *itEX){
itEY = _Enemys.Y.erase(itEY);
itEX = _Enemys.X.erase(itEX);
}
}
}
PS: sorry if my english isn't the best, im german ^^
PSS: if you wanna watch over my whole code, you can find it on Github: https://github.com/Aemmel/ConsoleGame1
After erasing using iterator it, you cannot use it further as it is invalidated. You should use a result of a call to erase which is new, valid iterator.
for( it = v.begin(); it != v.end();)
{
//...
if(...)
{
it = v.erase( it);
}
else
{
++it;
}
...
}
I fixed the bug with first: making a "simple structure"(struct Entity{int X; intY} and then std::vector [insert name here]) and then with adding an break; if the condition is true.
for (Uint itE = 0; itE < _Enemys.size(); ++itE){
for (Uint it = 0; it<_DestroyedFields.size(); ++it){
if (_Enemys.at(itE).Y == _DestroyedFields.at(it).Y
&& _Enemys.at(itE).X == _DestroyedFields.at(it).X){
_Enemys.erase(_Enemys.begin()+itE);
break;
}
}
}
With struct Position {int x; int y;}; and some utility operators,
you may do one of the following: (https://ideone.com/0aiih0)
void filter(std::vector<Position>& positions, const std::vector<Position>& destroyedFields)
{
for (std::vector<Position>::iterator it = positions.begin(); it != positions.end(); ) {
if (std::find(destroyedFields.begin(), destroyedFields.end(), *it) != destroyedFields.end()) {
it = positions.erase(it);
} else {
++it;
}
}
}
Or, if input are sorted, you may use a 'difference':
std::vector<Position> filter2(const std::vector<Position>& positions, const std::vector<Position>& destroyedFields)
{
std::vector<Position> res;
std::set_difference(positions.begin(), positions.end(),
destroyedFields.begin(), destroyedFields.end(),
std::back_inserter(res));
return res;
}