Проблема со стиранием элементов из набора - PullRequest
0 голосов
/ 06 августа 2011

У меня проблемы со стиранием элементов из наборов.Я получаю BUILD FAILED от:

n2Ar.erase(it);
n3Ar.erase(it);

, где it - указатель, полученный от функции find(): например, it = n2Ar.find(*i);

Полный список программы:

#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;
}

Надеюсь, вы сможете помочь мне понять, что я здесь делаю неправильно.Я только начинаю с C ++.

Ответы [ 3 ]

2 голосов
/ 06 августа 2011

В вашем коде есть две проблемы.

Сначала , вам необходимо вернуть значение в следующей функции или просто сделать его возвращаемым недействительным.

// you should return a value here or make it return void
int outputSet(int i)
{
    cout << i << endl;
}

Second , итераторы в следующих итерациях цикла for аннулированы после удаления текущего.Как только элемент удален, его итератор i также становится недействительным, поэтому для следующих итераторов, основанных на ++ i;

, вы получите ошибку во время выполнения, потому что итератор i теперь указывает на то, что вам нужно "reset "it.

Реализация MSVC

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);
            }
        }
    }

Редактировать : обратите внимание, что возвращение нового итератора из set :: erase () неСтандартный способ.Это в основном для повышения производительности.

A Более портативное решение

Основная идея - правильно установить следующий итератор перед удалением текущего.

   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;
         }
      }
   }
0 голосов
/ 06 августа 2011
  1. Метод стирания делает недействительным итератор i.
  2. Метод Erase не возвращает итератор.

РЕДАКТИРОВАТЬ : повторяя идею Эрика, вы можете использовать следующий код:

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++;

Также эту проблему можно решить с помощью стандартных алгоритмов. Но это решение кажется менее эффективным:

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()) );

Наконец, я предлагаю использовать stl для вашего вывода (вы должны включить библиотеку итераторов):

std::copy( list.begin(), list.end(), std::ostream_iterator<int>(std::cout,"\n"));
0 голосов
/ 06 августа 2011
n1Ar.erase(i);

Функция std :: set :: erase делает недействительным итератор i и вызывает проблему.Обратите внимание на следующее:

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 {

Проверка if(i == n1Ar.cend()) break; помогает убедиться, что недействительный итератор не разрушит цикл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...