我正在研究一种数据结构的擦除方法,该数据结构具有硬编码的最大元素数N,它依赖std::array
来避免堆内存。虽然std::array
只包含N个元素,但是它们中的M是“相关”元素,其中M小于或等于N.例如,如果N是10并且数组看起来像这样:
std::array<int, N> elements = { 0, 1, 2, -1, 4, -1, 6, -1, -1, 9 };
...如果M为7,则只有前7个元素是“相关的”而其他元素被认为是垃圾(结尾{ -1, -1, -9 }
是垃圾)。我在这里使用int
作为SO示例,但真实程序存储实现operator==
的对象。以下是删除所有-1
并更新M:
#include <algorithm>
#include <array>
#include <iostream>
constexpr unsigned N = 10;
unsigned M = 7;
std::array<int, N> elements = { 0, 1, 2, -1, 4, -1, 6, -1, -1, 9 };
int main() {
for (unsigned i = 0; i < M; ++i)
std::cout << elements[i] << ' ';
std::cout << '\n';
auto newEnd = std::remove_if(
std::begin(elements), std::begin(elements) + M,
[](const auto& element) {
return -1 == element;
}
);
unsigned numDeleted = M - std::distance(std::begin(elements), newEnd);
M -= numDeleted;
std::cout << "Num deleted: " << numDeleted << '\n';
for (unsigned i = 0; i < M; ++i)
std::cout << elements[i] << ' ';
std::cout << '\n';
return 0;
}
我的问题是std::remove_if
的渐近复杂性是什么?我想象在std::remove_if
和std::distance
之间它是整体O(2M)或O(M),其中std::remove_if
是一个更昂贵的操作。但是我不确定std::remove_if
是否为O(N * M),因为每次删除都会移动元素
编辑:为了清楚起见,我理解这应该应用谓词M次,但我想知道每次谓词为真时是否应用了N个移位
答案 0 :(得分:4)
复杂性: 谓词的
std::distance(first, last)
个应用程序。
删除的元素没有移位操作,因为在调用std::remove_if
后,它们可能具有未指定的值
答案 1 :(得分:2)
修改强>
回想起来,这个答案解决了一个比问题更复杂的问题 - 如何在线性时间内实现“推回到结束”功能。关于提出的具体问题 - 与remove_if
有关 - @ millenimumbug的答案更好地解决了这个问题。
我明白为什么你会认为复杂性是Θ(mn),因为每个 m 删除的项目可能需要移位 Θ(n)距离。
实际上可以在Θ(n)和其他 O(1)空间(只是几个额外的迭代器)中执行此操作。
考虑下图,显示算法可能实现的迭代。
红色项目是一组连续的已识别项目,此时要删除(此时您只需要两个点来记录)。绿色项目是正在考虑的项目(另一个指针)。
如果要删除绿色项目,红色组只需包含它就会变大。这将在下一个图表中显示,其中红色组展开。在下一次迭代中,绿色项目将是右侧的项目。
如果没有,则需要将所有红色组移过它。一些想法可以说服你,这可以在红色组中的线性时间内完成(即使迭代器保证只是前向迭代器)。
为什么复杂性是线性的?因为你可以想象这相当于相对于左组向左移动的绿色元素。理由类似于摊销分析。
下图显示了第二种情况。在下一次迭代中,绿色元素(被考虑)将再次位于红色组的右侧。