根据索引向量对向量重新排序 - 更新

Reorder vector according to a vector of indices - update

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

这是对之前关于就地重新排序的问题的更新 关于索引向量有一些混淆。调用要重新排序的向量 vA,以及索引 vI 的向量,则 vA 应按此顺序重新排序,vA[vI[0]]、vA[vI[1]]、vA[vI[2]、...一个示例用法是将 vI 初始化为一组 vA.size() 索引,从 0 到 vA.size()-1,然后根据 vA 对 vI 进行排序(使用 vA[vI[...]] 进行比较)。然后可以使用重新排序函数根据 vI 对 vA 进行排序。

将初始 vA 视为已排序 vA 的排列。根据 vA 对 vI 进行排序后,然后根据 vI 对 vA 进行重新排序"取消排列"vA,返回到排序的排列。使用下面显示的示例函数,对 vA 进行重新排序也会将 vI 重新排序回其初始化状态(0 到 vA.size()-1)。

下面的示例显示了一个名为reorderfail()的非工作版本,后跟一个名为reorder()的工作版本。这两个函数都将 vI 返回到其原始状态 0 到 vA.size()-1,但 reorderfail() 无法正确对 vA 进行重新排序,因为它缺少"取消排列"vA 所需的间接级别。

#include <algorithm>
#include <vector>
using namespace std;
template <class T>
void reorderfail(vector<T>& vA, vector<size_t>& vI)  
{   
size_t i, j;
    for(i = 0; i < vA.size(); i++ ){ 
        while(i != (j = vI[i])){
            swap(vA[i], vA[j]);
            swap(vI[i], vI[j]);
        }
    }
}
template <class T>
void reorder(vector<T>& vA, vector<size_t>& vI)  
{
size_t i, j, k;
    for(i = 0; i < vA.size(); i++){ 
        while(i != (j = vI[i])){
            k = vI[j];
            swap(vA[j], vA[k]);
            swap(vI[i], vI[j]);
        }
    }
}
int main( )
{
char A[]   = { 'b', 'c', 'a' };
size_t I[] = {  2 ,  0 ,  1  };  // order should be A[2] A[0] A[1]
size_t size = sizeof(A) / sizeof(*A);
    vector<char>   vA(size);
    vector<size_t> vI(size);
    vA.assign(A, A + size);
    vI.assign(I, I + size);
    reorderfail(vA, vI);    // returns vA = {'c', 'a', 'b'};
    vA.assign(A, A + size);
    vI.assign(I, I + size);
    reorder(vA, vI);        // returns vA = {'a', 'b', 'c'};
    return(0);
}

移动数据而不是交换数据的工作重新排序版本。这个例子可能更容易解释。每个排列都是一组"循环"。通过撤消循环来重新排序。假设有 8 个元素,并且 vI 包含所需的顺序。我将索引 i 放在 vI 上方:

i :     {  0 ,  1 ,  2 ,  3 ,  4 ,  5 ,  6 ,  7  }
vI[i] = {  5 ,  7 ,  0 ,  3 ,  6 ,  2 ,  4 ,  1  }
周期 1: vI[0] == 5, vI[5] == 2

, vI[2] == 0。

周期 2: vi[1] == 7

, vi[7] == 1。

周期 3: vI[3] == 3.

周期 4: vI[4] == 6

, vi[6] == 4.

撤消循环 1, t = vA[0], vA[0] = vA[5], vA[5] = vA[2]

, vA[2] = t。

撤消循环 2, t = vA[1], vA[1] = vA[7]

, vA[7] = t.

撤消周期 3,无需更正

撤消周期 4, t = vA[4], vA[4] = vA[6]

, vA[6] = t.

每次在 vA[k] 中存储值时,vI[k] 都设置为 k 以指示 vA[k] 已排序。

template <class T>
void reorder(vector<T>& vA, vector<size_t>& vI)  
{
size_t i, j, k;
T t;
    for(i = 0; i < vA.size(); i++){
        if(i != vI[i]){
            t = vA[i];
            k = i;
            while(i != (j = vI[k])){
            // every move places a value in it's final location
                vA[k] = vA[j];
                vI[k] = k;
                k = j;
            }
            vA[k] = t;
            vI[k] = k;
        }
    }
}