Interclasare: “Unirea a Două Trenuri Sortate”

Bun, hai să vorbim despre un algoritm elegant și extrem de util: INTERCLASAREA. Nu e doar despre unire (cuvânt simplu) și combinare. E despre cum îmbinăm două liste deja sortate într-una singură, păstrând ordinea. E un algoritm atât de important încât e piatra de temelie a unuia dintre cei mai eficienți algoritmi de sortare: Merge Sort.

1. Ce este Interclasarea? “Lipirea a Două Listări Telefonice”

Gândește-te la interclasare ca la unirea a două agende telefonice deja sortate alfabetic:

  • Ai agenda A: Ana, Cristi, Maria
  • Ai agenda B: Bogdan, Dan, Elena
  • Rezultat: Ana, Bogdan, Cristi, Dan, Elena, Maria

Regula de bază: Interclasarea funcționează DOAR pe vectori SORTAȚI!

2. Cum Funcționează Algoritmul? “Compară și Alege”

Ideea principală: Compară primul element din fiecare vector, ia-l pe cel mai mic, avansează în vectorul respectiv, repetă.

Analogie cu două cozi la casă:

Ai două cozi sortate după vârstă (cea mai tânără în față):

  • Coada A: 10 ani, 15 ani, 20 ani
  • Coada B: 12 ani, 18 ani, 22 ani

Interclasarea: ia pe rând cei mai tineri din fiecare coadă:
10 (din A), 12 (din B), 15 (din A), 18 (din B), 20 (din A), 22 (din B)

3. Algoritmul de Interclasare de Bază

#include <iostream>
using namespace std;

int main() {
    // Două vectori SORTAȚI
    int A[] = {1, 3, 5, 7};
    int B[] = {2, 4, 6, 8};

    int nA = 4;  // Dimensiunea lui A
    int nB = 4;  // Dimensiunea lui B

    // Vectorul rezultat (are dimensiunea nA + nB)
    int C[8];

    int i = 0;  // Index pentru A
    int j = 0;  // Index pentru B  
    int k = 0;  // Index pentru C

    // Interclasare: compară și alege
    while(i < nA && j < nB) {
        if(A[i] < B[j]) {
            C[k] = A[i];
            i++;
        } else {
            C[k] = B[j];
            j++;
        }
        k++;
    }

    // Dacă au rămas elemente în A, le copiem
    while(i < nA) {
        C[k] = A[i];
        i++;
        k++;
    }

    // Dacă au rămas elemente în B, le copiem
    while(j < nB) {
        C[k] = B[j];
        j++;
        k++;
    }

    // Afișare rezultat
    cout << "Vectorul A: ";
    for(int i = 0; i < nA; i++) cout << A[i] << " ";

    cout << "\nVectorul B: ";
    for(int i = 0; i < nB; i++) cout << B[i] << " ";

    cout << "\nVectorul interclasat C: ";
    for(int i = 0; i < nA + nB; i++) cout << C[i] << " ";

    return 0;
}

Ce se întâmplă pas cu pas:

A: [1, 3, 5, 7]     B: [2, 4, 6, 8]     C: []

Pas 1: Compar 1 și 2 → ia 1 (A), C=[1]
Pas 2: Compar 3 și 2 → ia 2 (B), C=[1,2]  
Pas 3: Compar 3 și 4 → ia 3 (A), C=[1,2,3]
Pas 4: Compar 5 și 4 → ia 4 (B), C=[1,2,3,4]
Pas 5: Compar 5 și 6 → ia 5 (A), C=[1,2,3,4,5]
Pas 6: Compar 7 și 6 → ia 6 (B), C=[1,2,3,4,5,6]
Pas 7: Compar 7 și 8 → ia 7 (A), C=[1,2,3,4,5,6,7]
Pas 8: Rămâne 8 din B → C=[1,2,3,4,5,6,7,8]

4. Variante ale Algoritmului

4.1 Interclasare cu elemente duplicate

#include <iostream>
using namespace std;

int main() {
    int A[] = {1, 3, 3, 5};
    int B[] = {2, 3, 4, 6};
    int nA = 4, nB = 4;

    int C[8];
    int i = 0, j = 0, k = 0;

    while(i < nA && j < nB) {
        if(A[i] <= B[j]) {  // Folosim <= pentru duplicate
            C[k] = A[i];
            i++;
        } else {
            C[k] = B[j];
            j++;
        }
        k++;
    }

    // Copiere resturi...

    cout << "Cu duplicate: ";
    for(int i = 0; i < 8; i++) cout << C[i] << " ";

    return 0;
}

4.2 Interclasare descrescătoare

#include <iostream>
using namespace std;

int main() {
    int A[] = {7, 5, 3, 1};  // SORTAT descrescător
    int B[] = {8, 6, 4, 2};  // SORTAT descrescător
    int nA = 4, nB = 4;

    int C[8];
    int i = 0, j = 0, k = 0;

    // Schimbăm < cu > pentru descrescător
    while(i < nA && j < nB) {
        if(A[i] > B[j]) {  // > în loc de <
            C[k] = A[i];
            i++;
        } else {
            C[k] = B[j];
            j++;
        }
        k++;
    }

    // Copiere resturi...

    cout << "Descrescator: ";
    for(int i = 0; i < 8; i++) cout << C[i] << " ";

    return 0;
}

5. Interclasare Fără Duplicate

#include <iostream>
using namespace std;

int main() {
    int A[] = {1, 3, 5, 7};
    int B[] = {2, 3, 4, 6};
    int nA = 4, nB = 4;

    int C[8];
    int i = 0, j = 0, k = 0;

    while(i < nA && j < nB) {
        if(A[i] < B[j]) {
            C[k] = A[i];
            i++;
            k++;
        } 
        else if(A[i] > B[j]) {
            C[k] = B[j];
            j++;
            k++;
        }
        else {  // Elemente egale (duplicate)
            C[k] = A[i];  // Pune o singură dată
            i++;
            j++;
            k++;
        }
    }

    // Copiere resturi (dar acum dimensiunea lui C poate fi mai mică)
    while(i < nA) {
        C[k] = A[i];
        i++;
        k++;
    }

    while(j < nB) {
        C[k] = B[j];
        j++;
        k++;
    }

    cout << "Fara duplicate: ";
    for(int i = 0; i < k; i++) cout << C[i] << " ";

    return 0;
}

6. Aplicații Practice

Aplicația 1: Unirea a doi vectori de numere pare și impare

#include <iostream>
using namespace std;

int main() {
    int pare[] = {2, 4, 6, 8};      // SORTAT
    int impare[] = {1, 3, 5, 7};    // SORTAT
    int nPare = 4, nImpare = 4;

    int rezultat[8];
    int i = 0, j = 0, k = 0;

    while(i < nPare && j < nImpare) {
        if(pare[i] < impare[j]) {
            rezultat[k] = pare[i];
            i++;
        } else {
            rezultat[k] = impare[j];
            j++;
        }
        k++;
    }

    // Copiere resturi...

    cout << "Pare + impare: ";
    for(int i = 0; i < 8; i++) cout << rezultat[i] << " ";

    return 0;
}

Aplicația 2: Interclasare cu citire de la tastatură

#include <iostream>
using namespace std;

int main() {
    int A[5], B[5];

    cout << "Introdu 5 numere SORTATE pentru A:\n";
    for(int i = 0; i < 5; i++) {
        cout << "A[" << i << "] = ";
        cin >> A[i];
    }

    cout << "\nIntrodu 5 numere SORTATE pentru B:\n";
    for(int i = 0; i < 5; i++) {
        cout << "B[" << i << "] = ";
        cin >> B[i];
    }

    int C[10];
    int i = 0, j = 0, k = 0;

    while(i < 5 && j < 5) {
        if(A[i] < B[j]) {
            C[k] = A[i];
            i++;
        } else {
            C[k] = B[j];
            j++;
        }
        k++;
    }

    while(i < 5) {
        C[k] = A[i];
        i++;
        k++;
    }

    while(j < 5) {
        C[k] = B[j];
        j++;
        k++;
    }

    cout << "\nRezultat interclasat: ";
    for(int i = 0; i < 10; i++) cout << C[i] << " ";

    return 0;
}

7. Interclasarea ca Funcție

#include <iostream>
using namespace std;

void interclasare(int A[], int nA, int B[], int nB, int C[]) {
    int i = 0, j = 0, k = 0;

    while(i < nA && j < nB) {
        if(A[i] < B[j]) {
            C[k] = A[i];
            i++;
        } else {
            C[k] = B[j];
            j++;
        }
        k++;
    }

    while(i < nA) {
        C[k] = A[i];
        i++;
        k++;
    }

    while(j < nB) {
        C[k] = B[j];
        j++;
        k++;
    }
}

int main() {
    int A[] = {1, 4, 7, 9};
    int B[] = {2, 3, 5, 8};
    int C[8];

    interclasare(A, 4, B, 4, C);

    cout << "Rezultat: ";
    for(int i = 0; i < 8; i++) cout << C[i] << " ";

    return 0;
}

8. Complexitatea Algoritmului

#include <iostream>
using namespace std;

int main() {
    cout << "=== COMPLEXITATE INTERCLASARE ===\n\n";

    cout << "Dacă A are n elemente și B are m elemente:\n";
    cout << "• Parcurgem fiecare element o singură dată\n";
    cout << "• Facem n + m comparații\n";
    cout << "• Complexitate: O(n + m)\n\n";

    cout << "EXEMPLU:\n";
    cout << "• A are 1000 elemente\n";
    cout << "• B are 2000 elemente\n";
    cout << "• Interclasarea va face cel mult 3000 de pași\n";
    cout << "• Mult mai eficient decât sortarea de la zero!\n";

    return 0;
}

9. Merge Sort – Folosind Interclasarea

Merge Sort = împarte vectorul în jumătăți, sortează fiecare jumătate, apoi le interclasează.

#include <iostream>
using namespace std;

void interclasare(int v[], int stanga, int mijloc, int dreapta) {
    int n1 = mijloc - stanga + 1;
    int n2 = dreapta - mijloc;

    int L[n1], R[n2];

    // Copiere în vectori temporari
    for(int i = 0; i < n1; i++) L[i] = v[stanga + i];
    for(int j = 0; j < n2; j++) R[j] = v[mijloc + 1 + j];

    // Interclasare
    int i = 0, j = 0, k = stanga;

    while(i < n1 && j < n2) {
        if(L[i] <= R[j]) {
            v[k] = L[i];
            i++;
        } else {
            v[k] = R[j];
            j++;
        }
        k++;
    }

    while(i < n1) {
        v[k] = L[i];
        i++;
        k++;
    }

    while(j < n2) {
        v[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(int v[], int stanga, int dreapta) {
    if(stanga < dreapta) {
        int mijloc = stanga + (dreapta - stanga) / 2;

        // Sortează cele două jumătăți
        mergeSort(v, stanga, mijloc);
        mergeSort(v, mijloc + 1, dreapta);

        // Interclasează jumătățile sortate
        interclasare(v, stanga, mijloc, dreapta);
    }
}

int main() {
    int v[] = {5, 2, 8, 1, 3, 7, 4, 6};
    int n = 8;

    mergeSort(v, 0, n-1);

    cout << "Sortat cu Merge Sort: ";
    for(int i = 0; i < n; i++) cout << v[i] << " ";

    return 0;
}

10. Exerciții Practice

Exercițiul 1: Completează algoritmul

int A[] = {1, 4, 6};
int B[] = {2, 3, 5};
int C[6];
int i = 0, j = 0, k = 0;

while(i < 3 && j < 3) {
    if(A[i] < B[j]) {
        C[k] = A[i];
        /* completează */
    } else {
        C[k] = B[j];
        /* completează */
    }
    k++;
}

Soluție:

C[k] = A[i];
i++;

C[k] = B[j];
j++;

Exercițiul 2: Ce face acest cod?

int i = 0, j = 0, k = 0;
while(i < nA && j < nB) {
    if(A[i] <= B[j]) {
        C[k++] = A[i++];
    } else {
        C[k++] = B[j++];
    }
}

Răspuns: Este algoritmul de interclasare (versiune compactă).

Exercițiul 3: Interclasare cu verificare dacă vectorii sunt sortați

#include <iostream>
using namespace std;

bool esteSortat(int v[], int n) {
    for(int i = 0; i < n-1; i++) {
        if(v[i] > v[i+1]) return false;
    }
    return true;
}

int main() {
    int A[] = {5, 3, 1};  // NU e sortat!
    int B[] = {2, 4, 6};

    if(!esteSortat(A, 3) || !esteSortat(B, 3)) {
        cout << "EROARE: Vectorii trebuie sa fie sortati!" << endl;
        return 1;
    }

    // Continuă cu interclasarea...

    return 0;
}

Schema Universală de Interclasare:

1. i = 0 (index pentru A), j = 0 (index pentru B), k = 0 (index pentru C)
2. Cât timp i < nA și j < nB:
   - Dacă A[i] < B[j]: C[k] = A[i], i++
   - Altfel: C[k] = B[j], j++
   - k++
3. Copiază restul din A (dacă există)
4. Copiază restul din B (dacă există)

În concluzie:

  • Interclasarea funcționează DOAR pe vectori SORTAȚI
  • Complexitatea e O(n + m) – extrem de eficientă
  • Merge Sort se bazează pe interclasare
  • Regula de aur: Compară, alege, avansează în vectorul ales

Când folosești interclasarea?

  1. Când ai două liste sortate și vrei să le unesti păstrând ordinea
  2. În Merge Sort pentru a combina jumătățile sortate
  3. În baze de date pentru a uni rezultate sortate
  4. În sisteme de fișiere pentru a interclasa fișiere sortate

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *