如何在向其添加项目时迭代列表

时间:2015-04-11 15:30:55

标签: c++ vector

我有一个行段列表(一个std::vector<std::pair<int, int> >,我想迭代并细分。算法将是,在伪代码中:

for segment in vectorOfSegments:
    firstPoint = segment.first;
    secondPoint = segment.second;
    newMidPoint = (firstPoint + secondPoint) / 2.0
    vectorOfSegments.remove(segment);
    vectorOfSegments.push_back(std::make_pair(firstPoint, newMidPoint));
    vectorOfSegments.push_back(std::make_pair(newMidPoint, secondPoint));

我遇到的问题是我如何push_back新元素(并删除旧元素)而不会永远迭代此列表。

似乎最好的方法可能是首先复制此向量,然后使用副本作为参考,clear()原始向量,然后push_back新元素到最近倒空的矢量。

有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

插入新细分时,请勿删除元素。然后,完成插入后,您可以删除原件:

int len=vectorOfSegments.size();
for (int i=0; i<len;i++)
{
    std::pair<int,int>& segment = vectorOfSegments[i];
    int firstPoint = segment.first;
    int secondPoint = segment.second;
    int newMidPoint = (firstPoint + secondPoint) / 2;
    vectorOfSegments.push_back(std::make_pair(firstPoint, newMidPoint));
    vectorOfSegments.push_back(std::make_pair(newMidPoint, secondPoint));
}
vectorOfSegments.erase(vectorOfSegments.begin(),vectorOfSegments.begin()+len);

或者,如果您想在一次传递中将一个段替换为两个新段,则可以使用此处的迭代器:

for (auto it=vectorOfSegments.begin(); it != vectorOfSegments.end(); ++it)
{
    std::pair<int,int>& segment = *it;
    int firstPoint = segment.first;
    int secondPoint = segment.second;
    int newMidPoint = (firstPoint + secondPoint) / 2;
    it = vectorOfSegments.erase(it);
    it = vectorOfSegments.insert(it, std::make_pair(firstPoint, newMidPoint));
    it = vectorOfSegments.insert(it+1, std::make_pair(newMidPoint, secondPoint));
}

正如Orbit的Lightning Racis指出的那样,你应该在这些方法之前做reserve。在第一种情况下,执行reserve(vectorOfSegmets.size()*3),后者reserve(vectorOfSegmets.size()*2+1)

答案 1 :(得分:2)

  

似乎最好的方法可能是先复制此向量,然后使用副本作为参考,使用clear()原始向量,然后将新元素推送回最近清空的向量。

几乎。你不需要复制和清除; 移动而不是!

// Move data from `vectorOfSegments` into new vector `original`.
// This is an O(1) operation that more than likely just swaps
// two pointers.
std::vector<std::pair<int, int>> original{std::move(vectorOfSegments)};

// Original vector is now in "a valid but unspecified state".
// Let's run `clear()` to get it into a specified state, BUT
// all its elements have already been moved! So this should be
// extremely cheap if not a no-op.
vectorOfSegments.clear();

// We expect twice as many elements to be added to `vectorOfSegments`
// as it had before. Let's reserve some space for them to get
// optimal behaviour.
vectorOfSegments.reserve(original.size() * 2);

// Now iterate over `original`, adding to `vectorOfSegments`...

答案 2 :(得分:0)

使用像这样的显式索引变量可以最简单地解决这个问题:

for(size_t i = 0; i < segments.size(); i++) {
    ...    //other code
    if(/*condition when to split segments*/) {
        Point midpoint = ...;
        segments[i] = Segment(..., midpoint);    //replace the segment by the first subsegment
        segments.emplace_back(Segment(midpoint, ...));    //add the second subsegment to the end of the vector
        i--;    //reconsider the first subsegment
    }
}

注意:

  • segments.size()在循环的每次迭代中被调用,因此我们真的重新考虑所有附加的段。

  • 显式索引意味着std::vector<>可以在emplace_back()调用中自由重新分配,没有迭代器/指针/引用可以变为无效。

  • 我假设你不关心矢量的顺序,因为你将新的段添加到矢量的末尾。如果您关心,您可能希望使用链接列表来避免算法的二次复杂性,因为std::vector<>的插入/删除具有线性复杂性。在我的代码中,我通过替换旧段来避免插入/删除。

    保留订单的另一种方法是首先忽略订单,然后通过排序重新建立订单。假设一个好的排序算法,即O(n * log(n)),它仍然优于天真的O(n ^ 2),但比链表方法的O(n)更差。

  • 如果您不想重新考虑新细分,只需使用常量并忽略计数器减量:

    size_t count = segments.size();
    for(size_t i = 0; i < count; i++) {
        ...    //other code
        if(/*condition when to split segments*/) {
            Point midpoint = ...;
            segments[i] = Segment(..., midpoint);    //replace the segment by the first subsegment
            segments.emplace_back(Segment(midpoint, ...));    //add the second subsegment to the end of the vector
        }
    }