使用索引向量重新排序向量

我想对向量中的项目重新排序,使用另一个向量来指定顺序:

char   A[]     = { 'a','b','c' };
size_t ORDER[] = { 1,2 };

vector<char>   vA(A,A + sizeof(A) / sizeof(*A));
vector<size_t> vOrder(ORDER,ORDER + sizeof(ORDER) / sizeof(*ORDER));

reorder_naive(vA,vOrder);
// A is now { 'b','a','c' }

以下是需要复制向量的低效实现:

void reorder_naive(vector<char>& vA,const vector<size_t>& vOrder)  
{   
    assert(vA.size() == vOrder.size());  
    vector vCopy = vA; // Can we avoid this?  
    for(int i = 0; i < vOrder.size(); ++i)  
        vA[i] = vCopy[ vOrder[i] ];  
}  

有没有更有效的方法,例如,使用 swap()?

abcd8031 回答:使用索引向量重新排序向量

该算法基于 chmike 的,但重新排序索引的向量是 const.这个功能都同意他的11![0..10] 的排列.复杂度为 O(N^2),取 N 作为输入的大小,或者更准确地说,最大的大小 轨道.

有关修改输入的优化 O(N) 解决方案,请参见下文.

template< class T >
void reorder(vector<T> &v, vector<size_t> const &order )  {   
    for ( int s = 1, d; s < order.size(); ++ s ) {
        for ( d = order[s]; d < s; d = order[d] ) ;
        if ( d == s ) while ( d = order[d], d != s ) swap( v[s], v[d] );
    }
}

这是一个 STL 风格的版本,我投入了更多的精力.它快了大约 47%(也就是说,几乎是 [0..10] 的两倍!)因为它尽可能早地完成所有交换然后返回.重新排序向量由许多轨道组成,每个轨道在到达其第一个成员时重新排序.当最后几个元素不包含轨道时,速度会更快.

template< typename order_iterator, typename value_iterator >
void reorder( order_iterator order_begin, order_iterator order_end, value_iterator v )  {   
    typedef typename std::iterator_traits< value_iterator >::value_type value_t;
    typedef typename std::iterator_traits< order_iterator >::value_type index_t;
    typedef typename std::iterator_traits< order_iterator >::difference_type diff_t;
    
    diff_t remaining = order_end - 1 - order_begin;
    for ( index_t s = index_t(), d; remaining > 0; ++ s ) {
        for ( d = order_begin[s]; d > s; d = order_begin[d] ) ;
        if ( d == s ) {
            -- remaining;
            value_t temp = v[s];
            while ( d = order_begin[d], d != s ) {
                swap( temp, v[d] );
                -- remaining;
            }
            v[s] = temp;
        }
    }
}


最后,为了一劳永逸地回答这个问题,一个会破坏重新排序向量的变体(用 -1 填充它).对于 [0..10] 的排列,它比之前的版本快了大约 16%.因为覆盖输入可以实现动态规划,所以它是 O(N),对于某些序列较长的情况,渐近更快.


And finally, just to answer the question once and for all, a variant which does destroy the reorder vector (filling it with -1's). For permutations of [0..10], It's about 16% faster than the preceding version. Because overwriting the input enables dynamic programming, it is O(N), asymptotically faster for some cases with longer sequences.

template< typename order_iterator, typename value_iterator >
void reorder_destructive( order_iterator order_begin, order_iterator order_end, value_iterator v )  {
    typedef typename std::iterator_traits< value_iterator >::value_type value_t;
    typedef typename std::iterator_traits< order_iterator >::value_type index_t;
    typedef typename std::iterator_traits< order_iterator >::difference_type diff_t;
    
    diff_t remaining = order_end - 1 - order_begin;
    for ( index_t s = index_t(); remaining > 0; ++ s ) {
        index_t d = order_begin[s];
        if ( d == (diff_t) -1 ) continue;
        -- remaining;
        value_t temp = v[s];
        for ( index_t d2; d != s; d = d2 ) {
            swap( temp, v[d] );
            swap( order_begin[d], d2 = (diff_t) -1 );
            -- remaining;
        }
        v[s] = temp;
    }
}

这篇关于使用索引向量重新排序向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持前端之家!

本文链接:https://www.f2er.com/3186433.html

大家都在问