代码之家  ›  专栏  ›  技术社区  ›  John Carter

如何根据不同std::vector的值对std::vectors进行排序?[副本]

  •  61
  • John Carter  · 技术社区  · 16 年前

    我有几个 std::vector ,长度都一样。我想对其中一个向量进行排序,并对所有其他向量应用相同的变换。有什么巧妙的方法吗?(最好使用STL或Boost)?一些向量成立 int s和其中一些 std::string s

    伪代码:

    std::vector<int> Index = { 3, 1, 2 };
    std::vector<std::string> Values = { "Third", "First", "Second" };
    
    Transformation = sort(Index);
    Index is now { 1, 2, 3};
    
    ... magic happens as Transformation is applied to Values ...
    Values are now { "First", "Second", "Third" };
    
    13 回复  |  直到 7 年前
        1
  •  30
  •   nicegom Konrad Rudolph    13 年前

    弗里奥的方法和你的方法结合起来很好。首先,构建一个由数字1组成的向量 n ,以及向量中指示排序顺序的元素:

    typedef vector<int>::const_iterator myiter;
    
    vector<pair<size_t, myiter> > order(Index.size());
    
    size_t n = 0;
    for (myiter it = Index.begin(); it != Index.end(); ++it, ++n)
        order[n] = make_pair(n, it);
    

    现在,您可以使用自定义排序器对这个数组进行排序:

    struct ordering {
        bool operator ()(pair<size_t, myiter> const& a, pair<size_t, myiter> const& b) {
            return *(a.second) < *(b.second);
        }
    };
    
    sort(order.begin(), order.end(), ordering());
    

    现在你已经掌握了内部重新排列的顺序 order (更确切地说,在项目的第一个组成部分中)。现在,您可以使用此顺序对其他向量进行排序。可能有一个非常聪明的就地变体同时运行,但在其他人提出之前,这里有一个变体还没有到位。使用 顺序 作为每个元素的新索引的查找表。

    template <typename T>
    vector<T> sort_from_ref(
        vector<T> const& in,
        vector<pair<size_t, myiter> > const& reference
    ) {
        vector<T> ret(in.size());
    
        size_t const size = in.size();
        for (size_t i = 0; i < size; ++i)
            ret[i] = in[reference[i].first];
    
        return ret;
    }
    
        2
  •  10
  •   Calimo    11 年前
    typedef std::vector<int> int_vec_t;
    typedef std::vector<std::string> str_vec_t;
    typedef std::vector<size_t> index_vec_t;
    
    class SequenceGen {
      public:
        SequenceGen (int start = 0) : current(start) { }
        int operator() () { return current++; }
      private:
        int current;
    };
    
    class Comp{
        int_vec_t& _v;
      public:
        Comp(int_vec_t& v) : _v(v) {}
        bool operator()(size_t i, size_t j){
             return _v[i] < _v[j];
       }
    };
    
    index_vec_t indices(3);
    std::generate(indices.begin(), indices.end(), SequenceGen(0));
    //indices are {0, 1, 2}
    
    int_vec_t Index = { 3, 1, 2 };
    str_vec_t Values = { "Third", "First", "Second" };
    
    std::sort(indices.begin(), indices.end(), Comp(Index));
    //now indices are {1,2,0}
    

    现在,您可以使用“索引”向量对“值”向量进行索引。

        3
  •  8
  •   Dave Hillier    16 年前

    把你的价值观放在 Boost Multi-Index container 然后迭代以按所需顺序读取值。如果你愿意,你甚至可以将它们复制到另一个向量上。

        4
  •  4
  •   friol    16 年前

    我只想到一个粗略的解决方案:创建一个向量,它是所有其他向量的总和(一个结构向量,如{3,Third,…},{1,First,…}),然后按第一个字段对这个向量进行排序,然后再次拆分这些结构。

    Boost内部或使用标准库可能有更好的解决方案。

        5
  •  3
  •   xtofl Adam Rosenfield    16 年前

    您可能可以定义一个自定义的“facade”迭代器,在这里执行您需要的操作。它会将迭代器存储到所有向量中,或者从第一个向量的偏移量中导出除第一个向量之外的所有向量的迭代器。棘手的部分是迭代器解引用的内容:想想boost::tuple之类的东西,巧妙地使用boost::tie。(如果你想扩展这个想法,你可以使用模板递归构建这些迭代器类型,但你可能永远不想写下它的类型——所以你要么需要c++0x auto,要么需要一个接受范围的排序包装函数)

        6
  •  3
  •   ltjax    14 年前

    我认为你 真正地 need(但如果我错了,请纠正我)是一种以某种顺序访问容器元素的方法。

    与其重新排列我的原始收藏,我会借用数据库设计中的一个概念:按照一定的标准排列索引。该索引是一种额外的间接方式,提供了极大的灵活性。

    这样,就可以根据类的不同成员生成多个索引。

    using namespace std;
    
    template< typename Iterator, typename Comparator >
    struct Index {
        vector<Iterator> v;
    
        Index( Iterator from, Iterator end, Comparator& c ){
            v.reserve( std::distance(from,end) );
            for( ; from != end; ++from ){
                v.push_back(from); // no deref!
            }
            sort( v.begin(), v.end(), c );
        }
    
    };
    
    template< typename Iterator, typename Comparator >
    Index<Iterator,Comparator> index ( Iterator from, Iterator end, Comparator& c ){
        return Index<Iterator,Comparator>(from,end,c);
    }
    
    struct mytype {
        string name;
        double number;
    };
    
    template< typename Iter >
    struct NameLess : public binary_function<Iter, Iter, bool> {
        bool operator()( const Iter& t1, const Iter& t2 ) const { return t1->name < t2->name; }
    };
    
    template< typename Iter >
    struct NumLess : public binary_function<Iter, Iter, bool> {
        bool operator()( const Iter& t1, const Iter& t2 ) const { return t1->number < t2->number; }
    };
    
    void indices() {
    
        mytype v[] =    { { "me"    ,  0.0 }
                        , { "you"   ,  1.0 }
                        , { "them"  , -1.0 }
                        };
        mytype* vend = v + _countof(v);
    
        Index<mytype*, NameLess<mytype*> > byname( v, vend, NameLess<mytype*>() );
        Index<mytype*, NumLess <mytype*> > bynum ( v, vend, NumLess <mytype*>() );
    
        assert( byname.v[0] == v+0 );
        assert( byname.v[1] == v+2 );
        assert( byname.v[2] == v+1 );
    
        assert( bynum.v[0] == v+2 );
        assert( bynum.v[1] == v+0 );
        assert( bynum.v[2] == v+1 );
    
    }
    
        7
  •  1
  •   aph    13 年前

    如果你只是想基于单个向量迭代所有向量,xtofl的答案有一个稍微紧凑的变体 keys 矢量。创建一个置换向量,并使用它来索引其他向量。

    #include <boost/iterator/counting_iterator.hpp>
    #include <vector>
    #include <algorithm>
    
    std::vector<double> keys = ...
    std::vector<double> values = ...
    
    std::vector<size_t> indices(boost::counting_iterator<size_t>(0u), boost::counting_iterator<size_t>(keys.size()));
    std::sort(begin(indices), end(indices), [&](size_t lhs, size_t rhs) {
        return keys[lhs] < keys[rhs];
    });
    
    // Now to iterate through the values array.
    for (size_t i: indices)
    {
        std::cout << values[i] << std::endl;
    }
    
        8
  •  1
  •   Tim MB    10 年前

    ltjax的答案是一个很好的方法,它实际上是在boost的zip_iterator中实现的 http://www.boost.org/doc/libs/1_43_0/libs/iterator/doc/zip_iterator.html

    它将你提供的任何迭代器打包成一个元组。

    然后,您可以根据元组中迭代器值的任何组合为排序创建自己的比较函数。对于这个问题,它只是元组中的第一个迭代器。

    这种方法的一个优点是,它允许您保持每个单独向量的内存连续(如果您正在使用向量,这就是您想要的)。您也不需要存储单独的int索引向量。

        9
  •  1
  •   user1596722    10 年前

    这将是Konrad答案的补充,因为它是一种将排序顺序应用于向量的就地变体的方法。不管怎样,既然编辑不成功,我就把它放在这里

    这是一个时间复杂度稍高的就地变体,这是由于检查布尔值的原始操作造成的。额外的空间复杂性是一个向量,它可以是一个空间高效的编译器依赖实现。如果给定的顺序本身可以修改,则可以消除向量的复杂性。

    这是一个时间复杂度稍高的就地变体,这是由于检查布尔值的原始操作造成的。额外的空间复杂性是一个向量,它可以是一个空间高效的编译器依赖实现。如果给定的顺序本身可以修改,则可以消除向量的复杂性。这是算法正在做的一个例子。 如果顺序是3 0 4 1 2,则由位置索引指示的元素的移动将是3-->;0;0---->1、 1--->3、 2-->;4、 4--->2.

    template<typename T>
    struct applyOrderinPlace
    {
    void operator()(const vector<size_t>& order, vector<T>& vectoOrder)
    {
    vector<bool> indicator(order.size(),0);
    size_t start = 0, cur = 0, next = order[cur];
    size_t indx = 0;
    T tmp; 
    
    while(indx < order.size())
    {
    //find unprocessed index
    if(indicator[indx])
    {   
    ++indx;
    continue;
    }
    
    start = indx;
    cur = start;
    next = order[cur];
    tmp = vectoOrder[start];
    
    while(next != start)
    {
    vectoOrder[cur] = vectoOrder[next];
    indicator[cur] = true; 
    cur = next;
    next = order[next];
    }
    vectoOrder[cur] = tmp;
    indicator[cur] = true;
    }
    }
    };
    
        10
  •  0
  •   Ziezi sethi    9 年前

    下面是一个相对简单的实现,使用 索引映射 有序与无序之间 names 这将用于匹配 ages 对于被命令者 名称 :

    void ordered_pairs()
    {
        std::vector<std::string> names;
        std::vector<int> ages;
    
        // read input and populate the vectors
        populate(names, ages);
    
        // print input
        print(names, ages);
    
        // sort pairs
        std::vector<std::string> sortedNames(names);
        std::sort(sortedNames.begin(), sortedNames.end());
    
        std::vector<int> indexMap;
        for(unsigned int i = 0; i < sortedNames.size(); ++i)
        {
            for (unsigned int j = 0; j < names.size(); ++j)
            {
                if (sortedNames[i] == names[j]) 
                {
                    indexMap.push_back(j);
                    break;
                }
            }
        }
        // use the index mapping to match the ages to the names
        std::vector<int> sortedAges;
        for(size_t i = 0; i < indexMap.size(); ++i)
        {
            sortedAges.push_back(ages[indexMap[i]]);
        }
    
        std::cout << "Ordered pairs:\n";
        print(sortedNames, sortedAges); 
    }
    

    为了完整起见,以下是函数 populate() print() :

    void populate(std::vector<std::string>& n, std::vector<int>& a)
    {
        std::string prompt("Type name and age, separated by white space; 'q' to exit.\n>>");
        std::string sentinel = "q";
    
        while (true)
        {
            // read input
            std::cout << prompt;
            std::string input;
            getline(std::cin, input);
    
            // exit input loop
            if (input == sentinel)
            {
                break;
            }
    
            std::stringstream ss(input);
    
            // extract input
            std::string name;
            int age;
            if (ss >> name >> age)
            {
                n.push_back(name);
                a.push_back(age);
            }
            else
            {
                std::cout <<"Wrong input format!\n";
            }
        }
    }
    

    以及:

    void print(const std::vector<std::string>& n, const std::vector<int>& a)
    {
        if (n.size() != a.size())
        {
            std::cerr <<"Different number of names and ages!\n";
            return;
        }
    
        for (unsigned int i = 0; i < n.size(); ++i)
        {
             std::cout <<'(' << n[i] << ", " << a[i] << ')' << "\n";
        }
    }
    

    最后, main() 变为:

    #include <iostream>
    #include <sstream>
    #include <string>
    #include <vector>
    #include <algorithm>
    
    void ordered_pairs();
    void populate(std::vector<std::string>&, std::vector<int>&);
    void print(const std::vector<std::string>&, const std::vector<int>&);
    
    //=======================================================================
    int main()
    {
        std::cout << "\t\tSimple name - age sorting.\n";
        ordered_pairs();
    }
    //=======================================================================
    // Function Definitions...
    
        11
  •  0
  •   umair butt    8 年前
    **// C++ program to demonstrate sorting in vector
    // of pair according to 2nd element of pair
    #include <iostream>
    #include<string>
    #include<vector>
    #include <algorithm>
    
    using namespace std;
    
    // Driver function to sort the vector elements
    // by second element of pairs
    bool sortbysec(const pair<char,char> &a,
                  const pair<int,int> &b)
    {
        return (a.second < b.second);
    }
    
    int main()
    {
        // declaring vector of pairs
        vector< pair <char, int> > vect;
    
        // Initialising 1st and 2nd element of pairs
        // with array values
        //int arr[] = {10, 20, 5, 40 };
        //int arr1[] = {30, 60, 20, 50};
        char arr[] = { ' a', 'b', 'c' };
        int arr1[] = { 4, 7, 1 };
    
        int n = sizeof(arr)/sizeof(arr[0]);
    
        // Entering values in vector of pairs
        for (int i=0; i<n; i++)
            vect.push_back( make_pair(arr[i],arr1[i]) );
    
        // Printing the original vector(before sort())
        cout << "The vector before sort operation is:\n" ;
        for (int i=0; i<n; i++)
        {
            // "first" and "second" are used to access
            // 1st and 2nd element of pair respectively
            cout << vect[i].first << " "
                 << vect[i].second << endl;
    
        }
    
        // Using sort() function to sort by 2nd element
        // of pair
        sort(vect.begin(), vect.end(), sortbysec);
    
        // Printing the sorted vector(after using sort())
        cout << "The vector after sort operation is:\n" ;
        for (int i=0; i<n; i++)
        {
            // "first" and "second" are used to access
            // 1st and 2nd element of pair respectively
            cout << vect[i].first << " "
                 << vect[i].second << endl;
        }
        getchar();
        return 0;`enter code here`
    }**
    
        12
  •  0
  •   kingusiu    6 年前

    基于Konrad Rudolph和Gabriele D'Antonia的答案,使用C++11 lambdas和STL算法:

    template< typename T, typename U >
    std::vector<T> sortVecAByVecB( std::vector<T> & a, std::vector<U> & b ){
    
        // zip the two vectors (A,B)
        std::vector<std::pair<T,U>> zipped(a.size());
        for( size_t i = 0; i < a.size(); i++ ) zipped[i] = std::make_pair( a[i], b[i] );
    
        // sort according to B
        std::sort(zipped.begin(), zipped.end(), []( auto & lop, auto & rop ) { return lop.second < rop.second; }); 
    
        // extract sorted A
        std::vector<T> sorted;
        std::transform(zipped.begin(), zipped.end(), std::back_inserter(sorted), []( auto & pair ){ return pair.first; }); 
    
        return sorted;
    }
    
        13
  •  -2
  •   cDc    7 年前

    很多人问了这个问题,但没有人给出令人满意的答案。这里有一个std::sort助手,它能够同时对两个向量进行排序,只考虑一个向量的值。该解决方案基于自定义RadomIt(随机迭代器),直接对原始向量数据进行操作,无需临时复制、结构重排或额外索引:

    C++, Sort One Vector Based On Another One