排序后如何获取索引排列

Posted

技术标签:

【中文标题】排序后如何获取索引排列【英文标题】:How to obtain the index permutation after the sorting 【发布时间】:2013-07-09 17:11:39 【问题描述】:

给定一个数组arr = 5, 16, 4, 7,我们可以通过sort(arr, arr+sizeof(arr)/sizeof(arr[0]))对其进行排序。 所以现在数组arr = 4, 5, 7, 16 和排序数组的排列索引是2, 0, 3, 1。 换句话说,原始数组中的arr[2]现在是排序数组中位置0的最小元素。

有没有一种有效的方法可以让我们得到排列索引?

【问题讨论】:

我知道有人会问这个问题,这不是作业。 当 OP 的代表高于 500 时,很少有人问“这是作业吗”的问题 :-) 【参考方案1】:

创建一个索引数组,用数字 0..N-1 填充它,然后使用自定义比较器对其进行排序。比较器应该比较原始数组中索引lhsrhs 处的项目。以这种方式对索引数组进行排序会将它们重新排序为排列:

vector<int> data = 5, 16, 4, 7;   
vector<int> index(data.size(), 0);
for (int i = 0 ; i != index.size() ; i++) 
    index[i] = i;

sort(index.begin(), index.end(),
    [&](const int& a, const int& b) 
        return (data[a] < data[b]);
    
);
for (int i = 0 ; i != index.size() ; i++) 
    cout << index[i] << endl;

这打印2, 0, 3, 1

这是demo on ideone。

注意:您可以使用index 按排序顺序检索data

for (int i = 0 ; i != index.size() ; i++) 
    cout << data[index[i]] << endl;

【讨论】:

如果您不需要排列索引,这种技术甚至会很有用。例如。当你有不可移动的物体时。 @MSalters:如果容器按值保存大型对象,这是否也适用?排序会导致大的移动操作;但可以避免排序排列。 您的解决方案对我不起作用。我在 Mac 上使用“g++ -std=c++14 -O3 your_solution.cpp”编译,得到“非法指令:4”。知道如何解决它吗? @utobi 我在我的 Mac 上试过这个,-std=c++14-std=c++11,程序两次都运行正确。我跑了g++ -v,我得到的输出和你的唯一区别是LLVM的版本:我有LLVM version 7.3.0 (clang-703.0.31),而你有7.0.0 (clang-700.0.72)。您应该可以通过升级到 Xcode 的下一个版本来解决此问题。 要填写index[i] = i;,请考虑std::iota(index, index + n, 0);【参考方案2】:

为什么不放一些卫星数据?无需对数字进行排序,只需对成对的数字及其索引进行排序。由于首先对对的第一个元素进行排序,因此这不会破坏稳定的排序算法。

对于不稳定的算法,这会将其更改为稳定的。

但请注意,如果您尝试以这种方式排序,它会在排序时生成索引,而不是在排序之后。

另外,由于知道排列索引会导致 O(n) 排序算法,所以你不能比 O(nlogn) 更快。

【讨论】:

我应该提到一个稳定的排序。 你好,这个可能和问题无关,但是我在找卫星数据的定义。有什么地方可以找到吗?【参考方案3】:

在 C++ 中,我们可以使用 pair 数据类型轻松地做到这一点。示例代码如下;

arr = 5, 16, 4, 7;
vector<pair<int,int> >V;
for(int i=0;i<4;i++)
    pair<int,int>P=make_pair(arr[i],i);
    V.push_back(P);


sort(V.begin(),V.end());

所以 V[i].first 是第 i 个值,V[i].second 是第 i 个索引。所以要打印排序数组中的索引。

for(int i=0;i<4;i++)cout<<V[i].second<<endl;

请注意,在对包含配对项的数组(或向量)进行排序时,数组首先根据第一个值进行排序。如果两对具有相同的第一个值,则根据它们的第二个值对它们进行排序。

【讨论】:

【参考方案4】:

Multimap 可以来救援

template<typename TIn, typename TOut>
sortindex(const TIn &first, const TIn &last, TOut &out) 
    using ValT = typename std::decay<decltype(*first)>::type;
    std::multimap<ValT, size_t> sindex;

    for(auto p=first; p != last; p++)
        sindex.emplace(*p, std::distance(first, p));

    for(auto &&p: sindex)
        *out++ = p.second;

可以这样使用:

std::vector<size_t> a32, 22, 45, 9, 12, 15;
std::vector<size_t> indexes;

sortindex(a.begin(), a.end(), std::back_inserter(indexes));

如果需要在排序后的值和索引之间进行耦合,则可以直接返回多映射,而不是将索引写入输出迭代器。

template<typename TIn>
auto
sortindex(const TIn &first, const TIn &last)
    --> std::multimap<typename std::decay<decltype(*first)>::type, size_t> 
  // return value can be commented out in C++14
    using ValT = typename std::decay<decltype(*first)>::type;
    std::multimap<ValT, size_t> sindex;

    for(auto p=first; p != last; p++)
        sindex.emplace(*p, std::distance(first, p));

    return sindex;

【讨论】:

【参考方案5】:

对于那些正在研究@Sergey Kalinichenko 解决方案的类似 C++17 界面的人,遵循 sn-p 可能会有用。

template <class ExecutionPolicy, typename RandomIt>
auto sort_permutation(ExecutionPolicy&& policy, RandomIt cbegin, RandomIt cend) 
    auto len = std::distance(cbegin, cend);
    std::vector<size_t> perm(len);
    std::iota(perm.begin(), perm.end(), 0U);
    std::sort(policy, perm.begin(), perm.end(),
          [&](const size_t& a, const size_t& b)
    
        return *(cbegin+a) < *(cbegin+b);
    );
    return perm;

你可以这样使用:

std::vector<int> a 2, 3, 1, 2, 3, 6, 8, 5;
auto r = sort_permutation(std::execution::par, a.cbegin()+1, a.cend()-1);

如果您不想与 tbb 链接,请删除执行政策争论。

【讨论】:

【参考方案6】:

我最近不得不在 php 中解决一个类似的问题。 您创建一个本地比较函数以供 PHP 的 UASORT 排序算法使用。在排序后的数组上调用 array_keys(),这应该会输出你的排列数组。

// 测试数组

$tArray = array('2', '10', '1', '23', '8', '3');

// 排序数组;维护索引关联,使用 uasort;否则使用 usort 等

uasort($tArray, 'compareSize');

// 结果键是您的排列索引(如果在上一步中使用了 uasort)

$Keys = array_keys($tArray);

//局部比较函数

函数 compareSize($a, $b)

if($a == $b) 返回 0; 其他 返回 ($a

================================================ ========================= 结果:

排序 =: 数组 ([2] => 1 [0] => 2 [5] => 3 [4] => 8 [1] => 10 [3] => 23)

键=:数组([0] => 2 [1] => 0 [2] => 5 [3] => 4 [4] => 1 [5] => 3)

【讨论】:

请格式化代码,很难判断正确性【参考方案7】:

是的,有一个 O(NlogN) 时间。由于无论如何排序都需要 O(NlogN) 时间,这不会影响整体时间复杂度。时间复杂度O(NlogN)空间复杂度O(N) 其中 N=输入数组中的元素数

算法:

    复制输入数组 (P),将其称为 Q。 对输入数组 P 进行排序。 对于 Q 中的每个数字,执行二进制搜索以找到 P 中该元素的索引。

【讨论】:

【参考方案8】:

我认为我们可以在不使用矢量的情况下解决这个问题。 我是新手,老实说,我不明白你上面写的什么,你们都使用了我稍后会学习的矢量:)))(我很懒) 这是我的方式:

//首先,将数组a[]复制到数组b[]

   void copyarray(double b[],double a[],int n)
     for(int i=0;i<n;i++)b[i]=a[i];
    

//二、排序数组a[](递减)

/*第三,“sorter”数组a[],意思是:在数组a[]中,如果存在相同的值,就合并为一个! 我将设置数组 o[] 是“排序后的数组 a[]” */

 void sorter(double o[],double a[],int n,int &dem)   
     int i;
     o[0]=a[0];
     for(i=1;i<n;i++)
        if(a[i]!=a[i-1])
          dem++; o[dem]=a[i];
         
      
      dem++;
 

/* 第四,我们的主要目标:获取索引:我将索引放入数组c[] */

void index_sort(double o[],double b[], double c[], int dem, int n)
    int i,j,chiso=0;
    for(j=0;j<dem;j++)
       for(i=0;i<n;i++)
          if(b[i]==o[j])
             c[chiso]=i;
             chiso++;
           
        
     
 

  // DONE!
 int main()
        int n,dem=0;
         double a[100],b[100],c[100],o[100];
         cin>>n;
         input(a,n);
         copyarray(b,a,n);
         copyarray(c,a,n);
         sort(a,n);
         sorter(o,a,n,dem); 
         index_sort(o,b,c,dem,n); 
         for(int i=0;i<n;i++) cout<<c[i]<<" ";
   

【讨论】:

以上是关于排序后如何获取索引排列的主要内容,如果未能解决你的问题,请参考以下文章

重新排序单元格后如何更改获取结果控制器数组中对象的顺序

如何获取向量的排序索引?

如何获取ListView中Item的行数

如何将 NSArray 拆分为按字母顺序排列的部分以与索引表视图一起使用

在R中的排序向量中获取索引的最有效方法?

使用 LINQ 排序后获取集合中项目的新索引