std::向量中的值传播

value propagation in a std::vector

本文关键字:值传 传播 向量 std      更新时间:2023-10-16

如果我有一个像这样初始化的std::vector

0 1 2 3 4 5

如何最好地将4传播到第一个位置?也就是说,我想要处于这种状态的std::vector

4 0 1 2 3 5

取出4并重新插入可能会很昂贵,因为我相信前面的插入是O(N)。我想交换连续位置的值(比如在冒泡排序中),但这也是O(N)。使用另一个容器(如std::list)是唯一的方法吗?

编辑:在看到一些混乱之后,让我澄清一下,我的目标是在std::vector中另一个已知位置的值之前,在std::vector中的已知任意位置预加一个值。

即使有一个可接受的答案,正常的C++方法也是使用提供的算法。在这种情况下,应该是std::rotate

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator> // for std::advance

int main(int argc, char** argv) {
    std::vector<int> v = { 0, 1, 2, 3, 4, 5 };
    std::cout << "Before: ";
    for (auto element : v)
        std::cout << element << " ";
    std::cout << std::endl;
    // edit starts here
    auto first=v.begin();
    auto nfirst=first;
    std::advance(nfirst, 4); // Iterator of first element to move to front
    auto last=nfirst;
    std::advance(last, 1); // 1 is element count for moving to front
    std::rotate(first, nfirst, last);
    // edit ends here
    std::cout << "After: ";
    for (auto element : v)
        std::cout << element << " ";
    std::cout << std::endl;
    return 0;
}

编辑:在与Luc Touraille讨论后,我看到了改进的空间。现在,该解决方案使用std::advance进行迭代器操作。因此,它应该与std::rotate所需的前向迭代器一起工作。

如果O(N)是一个问题,则更改容器(例如std::deque)是唯一的选项。

然而,一定要确保O(N)真的是一个问题!

说真的,我非常怀疑O(n)在实际代码中是否真的有问题。使用std::vector而不是std::list有更好的理由,比如比使用big-O更好的内存局部性和更少的内存开销。但是,您仍然可以优化标准方法(尽管要求dstsrc之前)

std::vector<int>::iterator src = ..., dst = ...;
...
auto tmp = std::move(*src);
vec.erase(src);
vec.insert(dst, std::move(tmp));

通过将两个O(n)遍历(在erase中向左移动一次,在insert中向右移动一次)变成一个(可能更小)遍历:

auto tmp = std::move(*src);
for(auto iter=src; iter!=dst; --iter)
    *iter = std::move(*std::prev(iter));
*dst = std::move(tmp);

编辑:不过请注意,我上面的代码片段只不过是不必要地复制了Jan在他的回答中提出的std::rotate

除了@JanHerrmann提供的出色答案外,这里还有一个用于在一个范围内移动元素的通用函数:

#include <algorithm>
#include <iostream>
#include <iterator>
// Moves the element src before dst, maintaining the order of other values
template <typename RandomAccessIt>
void moveElement(RandomAccessIt src, RandomAccessIt dst)
{ 
    if (dst == src)
    {
        return;
    }
    if (dst < src)
    { 
        std::rotate(dst, src, src + 1);
    }
    else
    { 
        std::rotate(src, src + 1, dst);
    }
}
void printVector(const std::vector<int> &v)
{
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << 'n';
}
int main() {
    std::vector<int> v = { 0, 1, 2, 3, 4, 5 };
    printVector(v); // 0 1 2 3 4 5
    moveElement(v.begin() + 4, v.begin());
    printVector(v); // 4 0 1 2 3 5
    moveElement(v.begin() + 2, v.begin() + 2);
    printVector(v); // 4 0 1 2 3 5
    moveElement(v.begin() + 2, v.begin() + 3);
    printVector(v); // 4 0 1 2 3 5
    moveElement(v.begin() + 2, v.end());
    printVector(v); // 4 0 2 3 5 1
}