Vector::erase() меняет позиции?

Вот мой вопрос, у меня есть двойной вектор, и мне нужно устранить некоторые из них при определенном условии. Вот пример кода:

 vector <double> appo;
 for(int i=0;i<appo.size();i++){
       for(int j=i+1;j<appo.size();j++){
         if( condition(appo[i],appo[j]) ){
           appo.erase(appo.begin()+j);
           j--;
         }
       }
    }

Поскольку после стирания () мой размер уменьшился на 1, а все элементы сместились влево, правильно ли уменьшить j?

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

vector <double> *point;
for(int i=0;i<point->size();i+=3){
     for(int j=i+3;j<point->size();j+=3){
       if(distance((*point)[i],(*point)[i+1],(*point)[i+2],(*point)[j],(*point)[j+1],(*point)[j+2]) < treshold){
         point->erase(point->begin()+j,point->begin()+j*3);
         j-=3;
       }
     }
  }

точка - это вектор координат, например (x1,y1,z1,x2,y2,z3,...,xn,yn,zn). Есть идеи?


person user1544128    schedule 04.09.2012    source источник
comment
Все элементы после стертого. std::remove_if может оказаться более эффективным в зависимости от ряда факторов.   -  person eq-    schedule 04.09.2012


Ответы (1)


Правильно уменьшать j, потому что после стирания элемента с индексом j элемент, который ранее был с j+1, теперь находится с j, поэтому вы хотите снова повторить цикл с тем же значением j. Уменьшение его имеет такой эффект, поскольку сам цикл увеличивает его.

Вы также можете рассмотреть возможность использования итератора вместо индекса:

vector<double>::iterator j = appo.begin() + i + 1;
while (j != appo.end()) {
    if (condition(appo[i], *j)) {
        j = appo.erase(j);
    } else {
        ++j;
    }
}

Сделав это, вы также можете использовать итератор для i.

Как говорится в комментарии «eq-», есть стандартный алгоритм, который может вам помочь. Выбирайте сами, предпочитаете ли вы его циклу с точки зрения удобства использования, но, как правило, он более эффективен, потому что повторный вызов «стирания» перемешивает каждый элемент по одному шагу за раз, тогда как remove_if отслеживает «позицию чтения» и «позицию записи». position", поэтому он копирует каждый элемент не более одного раза.

appo.erase(
    appo.remove_if(
        appo.begin() + i + 1,
        appo.end(),
        ShouldRemove(appo[i])
    ),
    appo.end()
);

В С++ 03 вы должны определить ShouldRemove аналогично:

struct ShouldRemove {
    double lhs;
    ShouldRemove(double d) : lhs(d) {}
    bool operator()(double rhs) {
        return condition(lhs, rhs);
    }
};

В С++ 11 вы можете использовать лямбду вместо ShouldRemove:

appo.erase(
    appo.remove_if(
        appo.begin() + i + 1,
        appo.end(),
        [&](double d) { return condition(appo[i], d); }
    ),
    appo.end()
);

У вас также есть некоторые варианты использования std::bind1st или boost::bind (в C++03) или std::bind (в C++11), но их довольно сложно правильно понять.

person Steve Jessop    schedule 04.09.2012