有一个
vector
数组,里面包含10000000个整数,如何高效的函数其偶数位置的元素?
这道题,如果我们用迭代器删除会遇到一些问题。
比较好的方案是:
1 创建一个新 vector 来存储奇数位置的元素。
2 使用 std::copy_if 算法将奇数位置的元素复制到新 vector 中。
3 用新 vector 替换原始 vector。
#include
#include
#include
#include
int main() {
std::vector numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector result;
// 使用 std::copy_if 复制奇数位置的元素
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(result),
[&numbers](const int &value) {
return &value % 2 == 1;
});
// 用新 vector 替换原始 vector
numbers.swap(result);
// 输出处理后的容器
for (int n : numbers) {
std::cout << n << ' ';
}
// 输出结果:1 3 5 7 9
}
这个方法的时间复杂度为 O(n),因为它只需要遍历一次原始 vector。同时,空间复杂度也为 O(n),因为需要创建一个新 vector 来存储奇数位置的元素。虽然它需要额外的空间,但在大多数情况下,这种方法在性能上可以接受。
有没有更好的方法呢?时间复杂度没法优化了,空间复杂度呢?
当然有。
我们可以使用一种更好的方法来实现,这种方法在原地修改 vector,无需创建新的 vector。我们可以使用两个迭代器,一个用于遍历原始 vector,另一个用于在原地移动奇数位置的元素。
#include
#include
int main() {
std::vector numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// 创建一个用于移动元素的迭代器
auto write_iter = numbers.begin();
// 遍历 vector,移动奇数位置的元素
for (auto read_iter = numbers.begin(); read_iter != numbers.end(); ++read_iter) {
if ((read_iter - numbers.begin()) % 2 == 1) {
*write_iter++ = *read_iter;
}
}
// 使用 erase 删除多余的元素
numbers.erase(write_iter, numbers.end());
// 输出处理后的容器
for (int n : numbers) {
std::cout << n << ' ';
}
// 输出结果:1 3 5 7 9
}
这种方法的时间复杂度仍为 O(n),但空间复杂度降低为 O(1),因为它在原地修改 vector,无需创建新的 vector。这是一种更好的方法,因为它不仅高效,而且避免了额外的空间开销。
试试remove-erase idiom。
C++ 中的 remove-erase idiom 是一种用于删除容器(如 vector、list、deque 等)中满足特定条件的元素的方法。它结合了标准库中的 remove 或 remove_if 算法与容器的 erase 成员函数,实现了高效且简洁的元素删除。
以下是 remove-erase idiom 的基本步骤:
使用 std::remove 或 std::remove_if 算法将要删除的元素移至容器的末尾。这两个算法返回一个迭代器,指向第一个被移动的元素。
std::remove 删除所有与给定值相等的元素。
std::remove_if 删除所有满足特定条件(谓词)的元素。
使用容器的 erase 成员函数删除从 std::remove 或 std::remove_if 返回的迭代器到容器末尾的元素范围。
#include
#include
#include
int main() {
std::vector numbers = {1, 42, 3, 42, 5, 42};
// 使用 remove-erase idiom 删除等于 42 的元素
numbers.erase(std::remove(numbers.begin(), numbers.end(), 42), numbers.end());
// 输出处理后的容器
for (int n : numbers) {
std::cout << n << ' ';
}
// 输出结果:1 3 5
}
同样,可以使用 std::remove_if 删除满足特定条件的元素。例如,以下代码删除所有偶数元素:
numbers.erase(std::remove_if(numbers.begin(), numbers.end(), [](int n) { return n % 2 == 0; }), numbers.end());
remove-erase idiom 提供了一种简洁、高效的方式来删除容器中的元素,同时避免了在遍历过程中进行删除操作可能导致的问题。
C++ 中的 remove-erase idiom 解决了在删除容器(如 vector、list、deque 等)中满足特定条件的元素时可能遇到的以下问题:
遍历与删除的问题:当我们在遍历容器的过程中删除元素时,可能会导致迭代器失效,从而引发未定义行为。remove-erase idiom 通过将要删除的元素移至容器末尾,并在遍历结束后统一删除,避免了这个问题。
效率问题:直接使用容器的 erase 函数逐个删除元素可能导致低效的操作。对于具有连续存储的容器(如 vector 和 deque),每次删除元素时,后续的所有元素都需要移动,这可能导致 O(n^2) 的时间复杂度。而 remove-erase idiom 仅需线性时间复杂度,因为它只需一次遍历和一次删除操作。
代码简洁性:remove-erase idiom 提供了一种简洁的方式来删除容器中的元素。它将标准库中的 remove 或 remove_if 算法与容器的 erase 成员函数结合,使代码更易于理解和维护。
通用性:remove-erase idiom 可以用于各种容器类型,包括 vector、list、deque 等。这意味着你可以使用相同的方法处理不同类型的容器,提高代码的通用性和可重用性。
总之,C++ 中的 remove-erase idiom 解决了在删除容器中满足特定条件的元素时可能遇到的遍历与删除、效率、代码简洁性和通用性等问题。