Как удалить лишние позиции из списка?

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

}

Я пытаюсь стереть элементы с помощью функции L.erase() в списках. Но я получаю сообщение об ошибке: «Утверждение отладки не удалось! ...... Выражение: итератор списка не увеличивается», но мы МОЖЕМ правильно увеличивать итератор?


person SLearner    schedule 12.09.2012    source источник
comment
вы удаляете после стирания?   -  person huseyin tugrul buyukisik    schedule 12.09.2012
comment
Где взять утверждение? Используйте отладчик, чтобы найти его. И если вы хотите знать, когда это происходит, выполните код построчно.   -  person Some programmer dude    schedule 12.09.2012
comment
Почему вы назначаете возвращаемое значение L.erase для i2?   -  person Bikush    schedule 12.09.2012


Ответы (4)


erase делает недействительным итератор, который был передан в качестве параметра, поскольку элемент в позиции, на которую указывал итератор, был просто стерт! И на том же самом итераторе в следующем цикле for в вашем коде предпринимается попытка приращения! Вот почему это не удается.

Однако стереть его вернет итератор, указывающий на новую позицию, которую мы можем использовать; поэтому цикл, в котором вы что-то стираете из контейнера STL, должен выглядеть примерно так: Я показываю это с типом, который вы используете, список, но вы могли бы также использовать, например. вектор:

list<int> L;
// ...
list<int>::iterator it=L.begin();
while (it!=L.end())
{
    if(eraseCondition)
    {
        it=L.erase(it);
    }
    else
    {
        ++it;
    }
}

Или, если возможно, даже лучше использовать std::remove_if:

container.erase(std::remove_if(L.begin(), L.end(), predicate), L.end());

В вашем случае это будет сложно - если не невозможно - использовать, поскольку predicate потребуется информация о состоянии (информация о том, является ли индекс нечетным или четным). Поэтому я бы рекомендовал использовать структуру цикла, как указано выше; просто имейте в виду remove_if для общего случая удаления всех элементов, где определенный предикат возвращает истину!

person codeling    schedule 12.09.2012
comment
стереть/удалить более идиоматично, но в этом случае предикату нужно какое-то состояние, которое быстро становится уродливым (и больше кода), чем простой цикл. - person Frerich Raabe; 12.09.2012
comment
@FrerichRaabe правда, и спасибо, я добавил соответствующую подсказку; но именно поэтому я также дал зацикленную форму в начале;) - person codeling; 12.09.2012
comment
remove_if здесь не очень надежен. Нет никаких обещаний, что он будет применять предикат в последовательном порядке по всему диапазону, поэтому отслеживание того, является ли индекс нечетным или четным, было бы невозможным или, по крайней мере, не переносимым. - person Benjamin Lindley; 12.09.2012
comment
@BenjaminLindley Вот почему я добавил часть, если это возможно, и примечание ниже;). Я просто подумал, что полезно знать альтернативы - в общем случае remove_if очень полезен - person codeling; 12.09.2012
comment
Спасибо нярлатхотеп и всем :) - person SLearner; 12.09.2012

В дополнение к тому, что сказал wOOte, вы можете использовать обратный итератор, чтобы обойти проблему.

person Brent Arias    schedule 12.09.2012
comment
+1 - не пробовал с std::list, но обычно это то, что я делаю на других языках с типами контейнеров динамических списков. - person Martin James; 12.09.2012

Технически не в этом случае.

Когда вы используете Erase(), вы удаляете узел, на который было указано, поэтому вы фактически аннулируете итератор, на котором вы были. Поэтому, когда вы увеличиваете его, это поведение undefined.

Возможно, будет лучше создать второй список только с итераторами для позиций, которые вы хотите удалить, и вы можете циклически перемещаться по ним и после этого вызывать стирание. Вы не будете стирать итераторы из второго списка, так что это сработает.

Что-то вроде этого:

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);
}
person John Humphreys    schedule 12.09.2012

Итератор i становится недействительным при вызове erase; однако на следующей итерации цикла for вы пытаетесь увеличить его — это недопустимо.

Пытаться

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

вместо этого - увеличивайте итератор только в том случае, если вы не стираете (стирание в основном «продвигает» итератор, потому что он дает итератор элементу, следующему за тем, который вы стерли).

Вы действительно можете использовать это поведение erase для написания своей функции, не требуя 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;
    }
}

Итак, вы удаляете первый элемент, пропускаете второй, удаляете третий и так далее.

person Frerich Raabe    schedule 12.09.2012