我有一个行段列表(一个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
新元素到最近倒空的矢量。
有更好的方法吗?
答案 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
}
}