如何在迭代集合时擦除集合中的所有相邻条目。在我的例子中,我有一个自定义比较器,它将相邻条目定义为从左到右相差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;
}
答案 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
。
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);
}
答案 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;
}