如何在迭代它时删除集合中的相邻条目

时间:2016-12-31 05:20:50

标签: c++ algorithm c++11 stl set

如何在迭代集合时擦除集合中的所有相邻条目。在我的例子中,我有一个自定义比较器,它将相邻条目定义为从左到右相差1的条目。因此,对于集合std::set<int> mySet = {1,2,3,4,5,7,9,10},我想删除条目{1,2,3,4,5,9,10},因为它们满足我的比较器。 (注意7是左边的,因为它是系列中唯一的一个不是相邻对中的一个元素。

下面的代码(也在coliru中)显示我可以找到正确添加相邻条目,但是如果我尝试擦除相邻对adjIter的左侧以及右侧*std::next(adjIter)代码与无效的迭代器崩溃。

int main() {    
    std::set<int> mySet = {1,2,3,4,5,7,9,10};
    static const auto gPred = [](const auto& lhs, const auto& rhs) {
        return rhs == lhs+1;
    };
    auto adjIter = mySet.begin();
    std::set<int> adjacentEntries;
    while ((adjIter = std::adjacent_find(adjIter, mySet.end(),
        gPred)) != mySet.end()) {
        adjacentEntries.insert(*adjIter);
        // peek at the second entry that should be 1 greater than adjIter        
        adjacentEntries.insert(*std::next(adjIter));
        // how do I erase both *std::next(adjIter) & adjIter
        ++adjIter;
    }
    std::cout << adjacentEntries << std::endl;
}

3 个答案:

答案 0 :(得分:1)

回答:

  

下面的代码(也在coliru中)显示我可以找到正确添加相邻条目,但是如果我尝试擦除相邻对的左侧和右侧* std :: next(adjIter)代码与无效的迭代器崩溃。

C ++ 11以后(正如您使用auto一样,我想这已经足够了):

set::erase(const_iterator first, const_iterator last) - 请参阅(2)nd表单,因为它返回指向已删除的最后一个元素的迭代器。

所以我猜它会是:

while ((adjIter = std::adjacent_find(adjIter, mySet.end(), gPred)) != mySet.end()) {
    adjacentEntries.insert(*adjIter);
    // peek at the second entry that should be 1 greater than adjIter        
    adjacentEntries.insert(*std::next(adjIter));

    // here's how to erase both *std::next(adjIter) & adjIter
    adjIter=mySet.erase(adjIter, std::next(std::next(adjIter)));
}

注意:上面的代码不再崩溃,但有一个算法错误,在提供的示例中, 5将不会删除(因为4之前将删除4 ),但这超出了问题(或者你是否也希望我为此找到解决方案?) - coliru

正确的解决方案:删除所有数字前面的num-1或成功的num + 1。在{1,2,3,4,5,7,9,10}的示例中,只有7将保留在mySet

See it on coliru

int main() {    
    std::set<int> mySet = {1,2,3,4,5,7,9,10};
    static const auto gPred = [](const auto& lhs, const auto& rhs) {
        return rhs == lhs+1;
    };
    std::set<int> adjacentEntries;

    auto adjIter = mySet.begin();
    auto prevDelete = mySet.end();

    while ((adjIter = std::adjacent_find(adjIter, mySet.end(), gPred)) != mySet.end()) {
        adjacentEntries.insert(*adjIter);
        // peek at the second entry that should be 1 greater than adjIter        
        adjacentEntries.insert(*std::next(adjIter));
        if(prevDelete!=adjIter && prevDelete!=mySet.end()) {
          // the prevDelete is the rhs of a pair which is followed by a "hole"
          mySet.erase(prevDelete, std::next(prevDelete));
        }
        prevDelete=mySet.end();

        // erase the lhs but delay the erasure of rhs,
        // let rhs participaye in the next round of search
        adjIter=mySet.erase(adjIter, std::next(adjIter));
        prevDelete=adjIter;
    }
    if(prevDelete!=mySet.end()) {
      mySet.erase(prevDelete, std::next(prevDelete));
    }
    std::cout << adjacentEntries << std::endl;
    std::cout << mySet << std::endl;
}

答案 1 :(得分:1)

您不需要使用额外空间来设置adjacentEntries。您可以使用标志del何时擦除以前的迭代器)来实现它,O(1)空间复杂度更简单:

std::set<int>::iterator prevItr = mySet.begin(), nextItr, currItr;
int prevVal = *prevItr;
int del = 0;
currItr = next(mySet.begin());
while (currItr != mySet.end()) {
    nextItr = std::next(currItr);
    if(prevVal+1 == *currItr || del == 1){
        mySet.erase(prevItr);
        if(prevVal+1 == *currItr) del = 1;
        else del = 0;
    }
    prevItr = currItr;
    prevVal = *currItr;
    currItr = next(currItr);
}
if(del == 1){
    mySet.erase(prevItr);
}

Demo

答案 2 :(得分:1)

在谓词为真时继续保存和删除元素的简单方法。

void remove_adjacent_entries()
{
    std::set<int> mySet = { 1,2,3,4,5,7,9,10 };
    static const auto gPred = [](const auto& lhs, const auto& rhs) {
        return rhs == lhs + 1;
    };

    auto adjIter = mySet.begin();
    std::set<int> adjacentEntries;
    while ((adjIter = std::adjacent_find(adjIter, mySet.end(), gPred)) != mySet.end()) {
        for (auto next = std::next(adjIter); next != mySet.end() && gPred(*adjIter, *next); ++next) {
            //save and delete the first of the pair of elements found
            adjacentEntries.insert(*adjIter);
            mySet.erase(adjIter++);
        }
        //save and delete the second element
        adjacentEntries.insert(*adjIter);
        mySet.erase(adjIter++);
    }
    //print
    for(auto& i : adjacentEntries)
        std::cout << i << std::endl;
}