简体   繁体   English

对向量进行排序而不改变原始向量的最佳方法是什么?

[英]What is the best way to sort a vector leaving the original one unaltered?

As the title says, I'm looking for a way to sort a vector without modifying the original one.正如标题所说,我正在寻找一种在不修改原始向量的情况下对向量进行排序的方法。 My first idea is of course to create a copy of the vector before the sort, eg:我的第一个想法当然是在排序之前创建向量的副本,例如:

std::vector<int> not_in_place_sort(const std::vector<int>& original)
{
   auto copy = original;
   std::sort(copy.begin(), copy.end());
   return copy;
}

However, maybe there is a more efficient way to perform the sort using C++ standard algorithm (maybe a combination of sort and transform ?)但是,也许有一种更有效的方法来使用 C++ 标准算法(也许是sorttransform的组合?)

Use partial_sort_copy.使用 partial_sort_copy。 Here is an example:下面是一个例子:

vector<int> v{9,8,6,7,4,5,2,0,3,1};
vector<int> v_sorted(v.size());
partial_sort_copy(begin(v), end(v), begin(v_sorted), end(v_sorted));

Now, v remains untouched but v_sorted contains {0,1,2,3,4,5,6,7,8,9}.现在, v 保持不变,但 v_sorted 包含 {0,1,2,3,4,5,6,7,8,9}。

Here is my favorite.这里是我的最爱。 Sort an index and not the original array/vector itself.排序索引而不是原始数组/向量本身。

#include <algorithm>

int main() {

    int intarray[4] = { 2, 7, 3, 4 };//Array of values
    //or you can have vector of values as below
    //std::vector<int> intvec = { 2, 7, 3, 4 };//Vector of values
    int indexofarray[4] = { 0, 1, 2, 3 };//Array indices

    std::sort(indexofarray, indexofarray + 4, [intarray](int index_left, int index_right) { return intarray[index_left] < intarray[index_right]; });//Ascending order.
    //have intvec in place of intarray for vector.


}

After this, indexofarray[] elements would be 0, 2, 3, 1 , while intarray[] is unchanged.在此之后, indexofarray[]元素将为0, 2, 3, 1 ,而intarray[]不变。

As suggested in the comments pass the function argument by value std::vector<int> original :正如评论中所建议的那样,通过std::vector<int> original值传递函数参数:

#include <iostream>
#include <vector>
#include <algorithm>

std::vector<int> not_in_place_sort(std::vector<int> original) {
    std::sort(original.begin(), original.end());
    return original;
}

int main() {
    std::vector<int> v = { 8, 6, 7, 2, 3, 4, 1, 5, 9 };
    std::vector<int> v2 = not_in_place_sort(v); // pass the vector by value
    std::cout << "v1: " << '\n';
    for (auto el : v) {
        std::cout << el << ' ';
    }
    std::cout << "\nv2: " << '\n';
    for (auto el : v2) {
        std::cout << el << ' ';
    }
}

That will sort a copy of your original vector leaving the original intact.这将对原始向量的副本进行排序,而原始向量则保持不变。 As pointed out below this might restrict some optimizations such as RVO but will call vector's move constructor in the return statement instead.正如下面所指出的,这可能会限制一些优化,例如RVO,但会在return语句中调用vector 的移动构造函数

For the case where you are interested in proxy sorting (sorting an index list), you may want to implement a more flexible algorithm that allows you to deal with containers which do not support random access (such as std::list ).对于您对代理排序(对索引列表进行排序)感兴趣的情况,您可能希望实现一种更灵活的算法,该算法允许您处理不支持随机访问的容器(例如std::list )。 For example:例如:

#include <algorithm>
#include <iostream>
#include <list>
#include <numeric>
#include <vector>

template <typename Container>
auto sorted_indices(const Container& c) {
  std::vector<typename Container::size_type> indices(c.size());
  std::iota(indices.begin(), indices.end(), 0);
  std::sort(indices.begin(), indices.end(), [&c](auto lhs, auto rhs) {
    return (*(std::next(c.begin(), lhs)) < *(std::next(c.begin(), rhs)));
  });
  return indices;
}

template <typename Container, typename Indices>
auto display_sorted(const Container& c, const Indices& indices) {
  std::cout << "sorted: ";
  for (auto&& index : indices) {
    std::cout << *(std::next(c.begin(), index)) << " ";
  }
  std::cout << std::endl;
}

template <typename Container>
auto display_sorted(const Container& c) {
  return display_sorted(c, sorted_indices(c));
}

template <typename Container>
auto display(const Container& c) {
  std::cout << "as provided: ";
  for (auto&& ci : c) std::cout << ci << " ";
  std::cout << std::endl;
}

int main() {
  // random access
  const std::vector<int> a{9, 5, 2, 3, 1, 6, 4};
  display(a);
  display_sorted(a);
  display(a);

  std::cout << "---\n";

  // no random access
  const std::list<int> b{9, 5, 2, 3, 1, 6, 4};
  display(b);
  display_sorted(b);
  display(b);
}

Sample run:示例运行:

$ clang++ example.cpp -std=c++17 -Wall -Wextra
$ ./a.out
as provided: 9 5 2 3 1 6 4 
sorted: 1 2 3 4 5 6 9 
as provided: 9 5 2 3 1 6 4 
---
as provided: 9 5 2 3 1 6 4 
sorted: 1 2 3 4 5 6 9 
as provided: 9 5 2 3 1 6 4 

As you would expect, relying on proxy sorting could have important performance implications.正如您所料,依赖代理排序可能会对性能产生重要影响。 For example: every time you want to traverse in order, you will possibly incur cache misses.例如:每次要按顺序遍历时,可能会导致缓存未命中。 In addition, the traversal will have the same complexity as the underlying container for random access: In the case of std::vector , std::next(v.begin(), n) is O(1) , but in the case of std::list , std::next(l.begin(), n) is O(n) .此外,遍历将具有与随机访问的底层容器相同的复杂性:在std::vector的情况下, std::next(v.begin(), n)O(1) ,但在这种情况下std::list std::next(l.begin(), n)O(n)

For int's it doesn't make much difference if you're sorting an index or making a copy & sorting the copy;对于 int 来说,如果您正在对索引进行排序或制作副本并对副本进行排序,则没有太大区别; the data still needs to be initialized, and in the case of the indexes, this will involve a loop assigning values rather than faster memcpy routines;数据仍然需要初始化,在索引的情况下,这将涉及分配值的循环,而不是更快的 memcpy 例程; so may end up slower;所以最终可能会变慢; in addition you're going to be jumping around memory lots more;此外,您将更多地在内存中跳跃; so now the cache can't do its job nicely.所以现在缓存不能很好地完成它的工作。

For larger objects I'd not sort the index, but use a vector of pointers.对于较大的对象,我不会对索引进行排序,而是使用指针向量。 The copy of the pointers is cheap compared to copying the objects themselves;与复制对象本身相比,指针的复制成本较低; the containers are still obvious because they're containing pointers of your object;容器仍然很明显,因为它们包含对象的指针; and the sort isn't attempting to reference another vector.并且排序不会尝试引用另一个向量。

You can create another vector to store the indices.您可以创建另一个向量来存储索引。 Here is the code:这是代码:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> numbers = {50,30,20,10,40};
    vector<int> indexOfNumbers;
    
    for(int i = 0; i < numbers.size(); i++)
    {
        indexOfNumbers.push_back(i); 
    }
    // Now, indexOfNumbers = [0,1,2,3,4]

    std::sort(
        indexOfNumbers.begin(), indexOfNumbers.end(), 
        [numbers](int leftIndex, int rightIndex) 
        { 
            return numbers[leftIndex] < numbers[rightIndex]; // sort in ascending order
        }
    );
    // After sorting, indexOfNumbers = [3, 2, 1, 4, 0]

    // Access the sorted elements
    cout << "Accessing the sorted elements : ";
    for(int i = 0; i < numbers.size(); i++)
    {
        cout << numbers[indexOfNumbers[i]] << " ";
    }
    // prints numbers in sorted order i.e. [10,20,30,40,50]
   return 0;
}

Source: Made slight modification according to Tyrer's answer ( https://stackoverflow.com/a/47537314 )来源:根据 Tyrer 的回答稍作修改( https://stackoverflow.com/a/47537314

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM