it-swarm.com.de

Wie kann ich zwei Vektoren auf die gleiche Weise sortieren, wobei nur einer der Vektoren verwendet wird?

Wie kann ich zwei Vektoren auf die gleiche Weise sortieren, wobei nur einer der Vektoren verwendet wird?

Angenommen, ich habe zwei Vektoren derselben Größe:

vector<MyObject> vectorA;
vector<int> vectorB;

Ich sortiere dann vectorA mit einer Vergleichsfunktion. Diese Sortierung hat vectorA neu angeordnet. Wie kann ich die gleiche Neuordnung auf vectorB anwenden?


Eine Möglichkeit besteht darin, eine Struktur zu erstellen:

struct ExampleStruct {
    MyObject mo;
    int i;
};

und sortiere dann einen Vektor, der den Inhalt von vectorA und vectorB enthält, gezippt in einen einzelnen Vektor:

// vectorC[i] is vectorA[i] and vectorB[i] combined
vector<ExampleStruct> vectorC;

Dies scheint keine ideale Lösung zu sein. Gibt es andere Optionen, insbesondere in C++ 11?

72
user2381422

Eine Sortierpermutation finden

Mit einem std::vector<T> Und einem Vergleich für T möchten wir in der Lage sein, die Permutation zu finden, die Sie verwenden würden, wenn Sie den Vektor anhand dieses Vergleichs sortieren würden.

template <typename T, typename Compare>
std::vector<std::size_t> sort_permutation(
    const std::vector<T>& vec,
    Compare& compare)
{
    std::vector<std::size_t> p(vec.size());
    std::iota(p.begin(), p.end(), 0);
    std::sort(p.begin(), p.end(),
        [&](std::size_t i, std::size_t j){ return compare(vec[i], vec[j]); });
    return p;
}

Anwenden einer Sortierpermutation

Mit einem std::vector<T> Und einer Permutation möchten wir in der Lage sein, einen neuen std::vector<T> Zu erstellen, der entsprechend der Permutation neu angeordnet wird.

template <typename T>
std::vector<T> apply_permutation(
    const std::vector<T>& vec,
    const std::vector<std::size_t>& p)
{
    std::vector<T> sorted_vec(vec.size());
    std::transform(p.begin(), p.end(), sorted_vec.begin(),
        [&](std::size_t i){ return vec[i]; });
    return sorted_vec;
}

Sie können natürlich apply_permutation Ändern, um den angegebenen Vektor zu mutieren, anstatt eine neu sortierte Kopie zurückzugeben. Dieser Ansatz ist immer noch linear und verwendet ein Bit pro Element in Ihrem Vektor. Theoretisch ist es immer noch eine lineare Raumkomplexität. In der Praxis kann die Reduzierung der Speichernutzung jedoch dramatisch sein, wenn sizeof(T) groß ist. ( Siehe Details )

template <typename T>
void apply_permutation_in_place(
    std::vector<T>& vec,
    const std::vector<std::size_t>& p)
{
    std::vector<bool> done(vec.size());
    for (std::size_t i = 0; i < vec.size(); ++i)
    {
        if (done[i])
        {
            continue;
        }
        done[i] = true;
        std::size_t prev_j = i;
        std::size_t j = p[i];
        while (i != j)
        {
            std::swap(vec[prev_j], vec[j]);
            done[j] = true;
            prev_j = j;
            j = p[j];
        }
    }
}

Beispiel

vector<MyObject> vectorA;
vector<int> vectorB;

auto p = sort_permutation(vectorA,
    [](T const& a, T const& b){ /*some comparison*/ });

vectorA = apply_permutation(vectorA, p);
vectorB = apply_permutation(vectorB, p);

Ressourcen

100
Timothy Shields

Ich möchte mit einer Erweiterung beitragen, die ich mir ausgedacht habe. Ziel ist es, mit einer einfachen Syntax mehrere Vektoren gleichzeitig sortieren zu können.

sortVectorsAscending(criteriaVec, vec1, vec2, ...)

Der Algorithmus ist derselbe wie der von Timothy, jedoch unter Verwendung von variadic templates , sodass wir mehrere Vektoren beliebigen Typs gleichzeitig sortieren können.

Hier ist das Code-Snippet:

template <typename T, typename Compare>
void getSortPermutation(
    std::vector<unsigned>& out,
    const std::vector<T>& v,
    Compare compare = std::less<T>())
{
    out.resize(v.size());
    std::iota(out.begin(), out.end(), 0);

    std::sort(out.begin(), out.end(),
        [&](unsigned i, unsigned j){ return compare(v[i], v[j]); });
}

template <typename T>
void applyPermutation(
    const std::vector<unsigned>& order,
    std::vector<T>& t)
{
    assert(order.size() == t.size());
    std::vector<T> st(t.size());
    for(unsigned i=0; i<t.size(); i++)
    {
        st[i] = t[order[i]];
    }
    t = st;
}

template <typename T, typename... S>
void applyPermutation(
    const std::vector<unsigned>& order,
    std::vector<T>& t,
    std::vector<S>&... s)
{
    applyPermutation(order, t);
    applyPermutation(order, s...);
}

template<typename T, typename Compare, typename... SS>
void sortVectors(
    const std::vector<T>& t,
    Compare comp,
    std::vector<SS>&... ss)
{
    std::vector<unsigned> order;
    getSortPermutation(order, t, comp);
    applyPermutation(order, ss...);
}

// make less verbose for the usual ascending order
template<typename T, typename... SS>
void sortVectorsAscending(
    const std::vector<T>& t,
    std::vector<SS>&... ss)
{
    sortVectors(t, std::less<T>(), ss...);
}

Testen Sie es in Ideone .

Ich erkläre das ein bisschen besser in dieser Blog-Beitrag .

4
tuket

In-Place-Sortierung mit Permutation

Ich würde eine Permutation wie Timothy verwenden, obwohl, wenn Ihre Daten zu groß sind und Sie nicht mehr Speicher für den sortierten Vektor reservieren möchten, Sie dies tun sollten an Ort und Stelle. Hier ist ein Beispiel für eine O(n) (lineare Komplexität) direkte Sortierung mithilfe von Permutation:

Der Trick besteht darin, die Permutation und die umgekehrte Permutation zu ermitteln, wo die beim letzten Sortierschritt überschriebenen Daten abgelegt werden sollen.

template <class K, class T> 
void sortByKey(K * keys, T * data, size_t size){
    std::vector<size_t> p(size,0);
    std::vector<size_t> rp(size);
    std::vector<bool> sorted(size, false);
    size_t i = 0;

    // Sort
    std::iota(p.begin(), p.end(), 0);
    std::sort(p.begin(), p.end(),
                    [&](size_t i, size_t j){ return keys[i] < keys[j]; });

    // ----------- Apply permutation in-place ---------- //

    // Get reverse permutation item>position
    for (i = 0; i < size; ++i){
        rp[p[i]] = i;
    }

    i = 0;
    K savedKey;
    T savedData;
    while ( i < size){
        size_t pos = i;
        // Save This element;
        if ( ! sorted[pos] ){
            savedKey = keys[p[pos]];
            savedData = data[p[pos]];
        }
        while ( ! sorted[pos] ){
            // Hold item to be replaced
            K heldKey  = keys[pos];
            T heldData = data[pos];
            // Save where it should go
            size_t heldPos = rp[pos];

            // Replace 
            keys[pos] = savedKey;
            data[pos] = savedData;

            // Get last item to be the pivot
            savedKey = heldKey;
            savedData = heldData;

            // Mark this item as sorted
            sorted[pos] = true;

            // Go to the held item proper location
            pos = heldPos;
        }
        ++i;
    }
}
3
MtCS

Mit range-v ist es einfach, eine Zip-Ansicht zu sortieren:

std::vector<MyObject> vectorA = /*..*/;
std::vector<int> vectorB = /*..*/;

ranges::v3::sort(ranges::view::Zip(vectorA, vectorB));

oder explizit Projektion verwenden:

ranges::v3::sort(ranges::view::Zip(vectorA, vectorB),
                 std::less<>{},
                 [](const auto& t) -> decltype(auto) { return std::get<0>(t); });

Demo

2
Jarod42
  1. Machen Sie aus Ihren einzelnen Vektoren einen Vektor aus Paaren.
    Vektor von Paaren initialisieren
    Addition zu einem Vektor eines Paares

  2. Erstellen Sie einen benutzerdefinierten Sortiervergleich:
    Sortieren eines Vektors benutzerdefinierter Objekte
    http://rosettacode.org/wiki/Sort_using_a_custom_comparator#C.2B.2B

  3. Sortieren Sie Ihren Vektor von Paaren.

  4. Teilen Sie Ihren Vektor von Paaren in einzelne Vektoren.

  5. Setzen Sie alle diese in eine Funktion.

Code:

std::vector<MyObject> vectorA;
std::vector<int> vectorB;

struct less_than_int
{
    inline bool operator() (const std::pair<MyObject,int>& a, const std::pair<MyObject,int>& b)
    {
        return (a.second < b.second);
    }
};

sortVecPair(vectorA, vectorB, less_than_int());

// make sure vectorA and vectorB are of the same size, before calling function
template <typename T, typename R, typename Compare>
sortVecPair(std::vector<T>& vecA, std::vector<R>& vecB, Compare cmp)
{

    std::vector<pair<T,R>> vecC;
    vecC.reserve(vecA.size());
    for(int i=0; i<vecA.size(); i++)
     {
        vecC.Push_back(std::make_pair(vecA[i],vecB[i]);   
     }

    std::sort(vecC.begin(), vecC.end(), cmp);

    vecA.clear();
    vecB.clear();
    vecA.reserve(vecC.size());
    vecB.reserve(vecC.size());
    for(int i=0; i<vecC.size(); i++)
     {
        vecA.Push_back(vecC[i].first);
        vecB.Push_back(vecC[i].second);
     }
}
1
ruben2020

Ich bin nicht sicher, ob das funktioniert, aber ich würde so etwas verwenden. Um beispielsweise zwei Vektoren zu sortieren, würde ich eine absteigende Blasensortiermethode und Vektorpaare verwenden.

Für absteigende Blasensortierung würde ich eine Funktion erstellen, die ein Vektorpaar erfordert.

void bubbleSort(vector< pair<MyObject,int> >& a)
{
    bool swapp = true;
    while (swapp) {
        int key;
        MyObject temp_obj;
        swapp = false;
        for (size_t i = 0; i < a.size() - 1; i++) {
            if (a[i].first < a[i + 1].first) {
                temp_obj = a[i].first;
                key = a[i].second;

                a[i].first = a[i + 1].first;
                a[i + 1].first = temp_obj;

                a[i].second = a[i + 1].second;
                a[i + 1].second = key;

                swapp = true;
            }
        }
    }
}

Danach würde ich Ihre 2 Vektorwerte in ein Vektorpaar setzen. Wenn Sie gleichzeitig Werte hinzufügen können, verwenden Sie diesen und rufen Sie dann die Blasensortierfunktion auf.

vector< pair<MyObject,int> > my_vector;

my_vector.Push_back( pair<MyObject,int> (object_value,int_value));

bubbleSort(my_vector);

Wenn Sie Werte verwenden möchten, nachdem Sie zu Ihren 2 Vektoren hinzugefügt haben, können Sie diesen verwenden und dann die Blasensortierfunktion aufrufen.

vector< pair<MyObject,int> > temp_vector;

for (size_t i = 0; i < vectorA.size(); i++) {
            temp_vector.Push_back(pair<MyObject,int> (vectorA[i],vectorB[i]));
        }

bubbleSort(temp_vector);

Ich hoffe das hilft. Grüße, Caner

0
Caner SAYGIN

Ich gehe davon aus, dass VektorA und VektorB gleich lang sind. Sie könnten einen anderen Vektor erstellen, nennen wir ihn pos, wobei:

pos[i] = the position of vectorA[i] after sorting phase

und dann können Sie vectorB mit pos sortieren, d. h. vectorBsorted erstellen, wobei:

vectorBsorted[pos[i]] = vectorB[i]

und dann wird vectorBsorted nach der gleichen Permutation von Indizes sortiert wie vectorA.

0
pkacprzak