交换向量的值和索引

Swap values and indexes of a vector

本文关键字:索引 向量 交换      更新时间:2023-10-16

我有一个std::vector<int>,它具有从0到N的连续洗牌值,并且希望尽可能有效地交换每个值与其在向量中的位置。

的例子:

v[6] = 3;

v[3] = 6;

这是一个简单的问题,但我不知道如何处理它,以使它变得微不足道,最重要的是,非常快。非常感谢您的建议。

给定N在编译时,并且给定数组包含[0,N)中的每个索引正好一次,它是相对直接的(只要它不需要在适当的位置,就像上面的评论中提到的):

v'[n] = find_index(v, n)构造一个新的数组并赋值给旧的数组。

在这里,我使用可变模板和std::index_sequence将其滚动到一个单一的赋值:

template<typename T, std::size_t N>
std::size_t find_index(const std::array<T,N>& arr, std::size_t index) {
    return static_cast<std::size_t>(std::distance(arr.begin(), std::find(arr.begin(), arr.end(), index)));
}
template<typename T, std::size_t N, std::size_t... Index>
void swap_index_value(std::array<T,N>& arr, std::index_sequence<Index...> seq){
    arr = { find_index(arr, Index)... };
}
template<typename Integer, std::size_t N>
void swap_index_value(std::array<Integer,N>& arr) {
    swap_index_value(arr, std::make_index_sequence<N>{});
}

这看起来并不复杂。对[0,N)中的每个n调用find_index(arr, n)总共需要N * (N+1)/2次比较(std::sort只需要N * log(N))。

然而,由于我们知道数组中存在每个索引,我们可以直接填充一个索引数组当我们遍历原始数组时,假设T是整型,我们也可以跳过一些std::size_t <-> T转换:

template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
    std::array<T, N> indices;
    for (T i = 0; i < N; ++i)
        indices[arr[i]] = i;
    arr = indices;
}

我们仍然使用两倍的空间并对数组进行随机顺序的写操作,但实际上我们只需要2*N个赋值,而且代码比以前更简单了。

或者,我们也可以std::sort,如果我们保留一个副本做查找:

template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
    std::sort(arr.begin(), arr.end(), [copy = arr](const T& lhs, const T& rhs) {
        return copy[lhs] < copy[rhs];
    });
}

这里是第一个版本,第二个版本在这里,

测试哪一个更快,留给读者作为练习;)