it-swarm.com.de

Wie sortiere ich einen std :: vector nach den Werten eines anderen std :: vector?

Ich habe mehrere std::vector, alle die gleiche Länge. Ich möchte einen dieser Vektoren sortieren und dieselbe Transformation auf alle anderen Vektoren anwenden. Gibt es einen ordentlichen Weg, dies zu tun? (vorzugsweise mit STL oder Boost)? Einige der Vektoren enthalten ints und einige von ihnen std::strings.

Pseudo-Code:

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" };
49
John Carter

friols Ansatz ist gut, wenn er mit Ihrem verbunden ist. Erstellen Sie zunächst einen Vektor, der aus den Zahlen 1… n besteht, zusammen mit den Elementen aus dem Vektor, der die Sortierreihenfolge bestimmt:

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);

Jetzt können Sie dieses Array mit einem benutzerdefinierten Sortierer sortieren:

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());

Jetzt haben Sie die Reihenfolge der Umlagerung in order (genauer in der ersten Komponente der Elemente) erfasst. Sie können diese Reihenfolge jetzt verwenden, um Ihre anderen Vektoren zu sortieren. Es gibt wahrscheinlich eine sehr clevere In-Place-Variante, die zur gleichen Zeit ausgeführt wird, aber bis jemand anderes darüber spricht, gibt es eine Variante, die nicht in-Place ist. Es verwendet order als Nachschlagetabelle für den neuen Index jedes Elements.

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;
}
29
Konrad Rudolph

Fügen Sie Ihre Werte in einen Boost Multi-Index-Container ein, und wiederholen Sie dann das Lesen der Werte in der gewünschten Reihenfolge. Sie können sie sogar in einen anderen Vektor kopieren, wenn Sie möchten.

8
Dave Hillier
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}

Jetzt können Sie den Vektor "Indizes" verwenden, um den Vektor "Werte" zu indexieren.

8
lalitm

Mir fällt nur eine grobe Lösung ein: Erstellen Sie einen Vektor, der die Summe aller anderen Vektoren ist (ein Vektor mit Strukturen wie {3, Third, ...}, {1, First, ...}) und sortieren Sie diese anschließend Vektor durch das erste Feld und teilen Sie dann die Strukturen erneut auf .

Wahrscheinlich gibt es in Boost oder mit der Standardbibliothek eine bessere Lösung.

4

Sie können wahrscheinlich einen benutzerdefinierten "Fassaden" -Iterator definieren, der das tut, was Sie hier brauchen. Es würde Iteratoren für alle Ihre Vektoren speichern oder alternativ die Iteratoren für alle außer dem ersten Vektor vom Versatz des ersten Vektors ableiten. Der heikle Teil ist das, was der Iterator dereferenziert: Denken Sie an etwas wie boost :: Tuple und nutzen Sie clever :: boost :: tie. (Wenn Sie diese Idee erweitern möchten, können Sie diese Iteratortypen mithilfe von Vorlagen rekursiv erstellen. Sie möchten jedoch wahrscheinlich niemals den Typ davon aufschreiben. Daher benötigen Sie entweder C++ 0x auto oder eine Wrapperfunktion für die Sortierung, die Bereiche verwendet.

3
ltjax

Ich denke, was Sie wirklich brauchen (aber korrigieren Sie mich, wenn ich falsch liege) ist eine Möglichkeit, auf Elemente eines Containers in einer bestimmten Reihenfolge zuzugreifen.

Anstatt meine ursprüngliche Sammlung neu zu ordnen, lehnte ich mir ein Konzept aus dem Datenbankdesign: Behalten Sie einen Index, der nach einem bestimmten Kriterium geordnet ist. Dieser Index ist eine zusätzliche Indirektion, die große Flexibilität bietet.

Auf diese Weise ist es möglich, mehrere Indizes nach verschiedenen Mitgliedern einer Klasse zu generieren.

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 );

}
2
xtofl

die Antwort von ltjax ist ein großartiger Ansatz - der eigentlich in boost's Zip_iterator http://www.boost.org/doc/libs/1_43_0/libs/iterator/doc/Zip_iterator.html

Es wird zusammen in einem Tupel verpackt, unabhängig von den Iteratoren, die Sie bereitstellen. 

Sie können dann eine eigene Vergleichsfunktion für eine Sortierung erstellen, die auf einer beliebigen Kombination von Iteratorwerten in Ihrem Tuple basiert. Für diese Frage wäre es nur der erste Iterator in Ihrem Tuple.

Eine schöne Funktion dieses Ansatzes besteht darin, dass Sie den Speicher jedes einzelnen Vektors zusammenhängend halten können (wenn Sie Vektoren verwenden und das ist es, was Sie möchten). Sie müssen auch keinen separaten Indexvektor von Ints speichern.

1
aph

Eine etwas kompaktere Variante der Antwort von xtofl für den Fall, dass Sie alle Ihre Vektoren basierend auf dem einzigen keys-Vektor durchlaufen möchten. Erstellen Sie einen Permutationsvektor und verwenden Sie diesen, um Ihre anderen Vektoren zu indexieren.

#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;
}
1
Tim MB

Dies wäre ein Nachtrag zu Konrads Antwort gewesen, da es eine Annäherung an eine In-Place-Variante wäre, die Sortierreihenfolge auf einen Vektor anzuwenden. Jedenfalls, da die Bearbeitung nicht durchlaufen wird, werde ich sie hier einfügen

Hier ist eine In-Place-Variante mit einer etwas höheren zeitlichen Komplexität, die auf einen primitiven Vorgang der Überprüfung eines Booleschen Objekts zurückzuführen ist. Die zusätzliche Platzkomplexität ist von einem Vektor, der eine platzsparende compilerabhängige Implementierung sein kann. Die Komplexität eines Vektors kann eliminiert werden, wenn die gegebene Reihenfolge selbst modifiziert werden kann.

Hier ist eine In-Place-Variante mit einer etwas höheren zeitlichen Komplexität, die auf einen primitiven Vorgang der Überprüfung eines Booleschen Objekts zurückzuführen ist. Die zusätzliche Platzkomplexität ist von einem Vektor, der eine platzsparende compilerabhängige Implementierung sein kann. Die Komplexität eines Vektors kann eliminiert werden, wenn die gegebene Reihenfolge selbst modifiziert werden kann. Dies ist ein Beispiel dafür, was der Algorithmus tut. Wenn die Reihenfolge 3 0 4 1 2 ist, würde die Bewegung der Elemente, wie durch die Positionsindizes angegeben, 3 ---> 0 sein; 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;
}
}
};
1
user1596722
**// 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`
}**
0
umair butt

mit C++ 11 Lambdas und den STL-Algorithmen basierend auf Antworten von Konrad Rudolph und Gabriele D'Antona:

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;
}
0
kingusiu

Hier ist eine relativ einfache Implementierung mit index mapping zwischen der geordneten und ungeordneten names, die verwendet wird, um die ages der geordneten names zuzuordnen:

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); 
}

Der Vollständigkeit halber sind hier die Funktionen populate() und 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";
        }
    }
}

und:

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";
    }
}

Und schließlich wird 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...
0
Ziezi