Școala Româno-Britanică
Modulul 1 Clasa a X-a | C++

Ora 6: Eliminarea dublurilor dintr-un șir

Ce vei învăța astăzi: Cum eliminăm dublurile (valorile care apar de mai multe ori) dintr-un șir și cum numărăm elementele distincte cu vectorul de frecvență (vector care numără de câte ori apare fiecare valoare)

Rezultat practic: Elimini dublurile dintr-un șir și numeri elementele distincte folosind vectorul de frecvență (Competența 4.1).

Competența 4.1 Săptămâna 3

Înainte de a începe

De ce contează? Datele reale conțin aproape întotdeauna dubluri, iar numărarea valorilor distincte este una dintre cele mai frecvente cerințe din subiectele de BAC.

Ce știi deja?

Din ora 5 știi tehnica steagului ok pentru verificări. În această lecție folosim aceeași idee într-un alt fel: marcarea valorilor văzute, ca să recunoaștem dacă o valoare este nouă.

Ideea-cheie: Numărăm o valoare doar la prima ei apariție: dacă frec[x] == 0, o marcăm cu 1 și creștem numărătorul — astfel fiecare valoare distinctă contribuie exact o dată.
SINTEZĂ TEORETICĂ ȘI EXEMPLE GHIDATE

Ce vei învăța astăzi?

După această lecție vei învăța cum eliminăm dublurile dintr-un șir și cum numărăm elementele distincte folosind vectorul de frecvență.

Problema dublurilor dintr-un șir
Marcarea valorilor deja văzute
Numărarea elementelor distincte
Implementarea eficientă în C++

Problema dublurilor

Un șir poate conține valori repetate. Când vrem să lucrăm cu mulțimea valorilor, trebuie să ținem cont de fiecare valoare o singură dată.

De exemplu, în șirul 3 5 3 3 5 2, valorile distincte sunt {2, 3, 5}.

Două abordări

  • Comparare directă: pentru fiecare element verificăm dacă a mai apărut înainte (lejer, dar lent)
  • Vector de frecvență: marcăm valoarea la prima apariție și numărăm marcați (eficient)

Tehnica marcare-numărare

Ideea: când citim valoarea x, dacă frec[x] == 0, înseamnă că o vedem pentru prima dată. O marcăm (frec[x] = 1) și numărăm (nr++). Dacă frec[x] == 1, este un dublu și îl ignorăm.

Parcurgerea șirului 3 5 3 3 5 2
3 frec[3] == 0, marcăm și numărăm: nr = 1
5 frec[5] == 0, marcăm și numărăm: nr = 2
3 frec[3] == 1, dublu, ignorăm
3 dublu, ignorăm
5 dublu, ignorăm
2 frec[2] == 0, marcăm și numărăm: nr = 3

Rezultat: numărul de elemente distincte este 3.

Numărarea elementelor distincte în C++

citeste n frec[1..100] = 0 nr = 0 pentru i = 1, n executa citeste x daca frec[x] == 0 atunci frec[x] = 1 nr = nr + 1 scrie nr
C++ - Numărarea elementelor distincte
#include <iostream>
using namespace std;

int main() {
    int n, x, i, frec[101] = {0}, nr = 0;
    cin >> n;
    for (i = 1; i <= n; i++) {
        cin >> x;
        if (frec[x] == 0) {
            frec[x] = 1;
            nr++;
        }
    }
    cout << nr;
    return 0;
}

Observă: numărăm doar valorile întâlnite pentru prima dată. Astfel, fiecare valoare distinctă contribuie exact o dată la rezultat.

Exercițiu de completare

Completează enunțul despre eliminarea dublurilor:

În șirul 4 1 4 2 1, valorile distincte sunt ______: ______, 1 și 2. O valoare apare o singură dată în ______.

Bancă de cuvinte:
3 4 mulțime

Exercițiu de completare

Completează fraza despre marcare:

Când întâlnim valoarea x, dacă frec[x] == ______, o vedem pentru prima dată: o marcăm cu frec[x] = 1 și incrementăm ______. Dacă frec[x] este deja 1, înseamnă că este un ______.

Bancă de cuvinte:
0 nr dublu

Test de înțelegere

Test rapid - eliminarea dublurilor:

1. Câte valori distincte are șirul 2 2 3 2 5 3?

2. Când numărăm elementele distincte, dublul unei valori:

3. Condiția care indică prima apariție a valorii x este:

Ai grijă la: condiția de „prima apariție" este frec[x] == 0, nu frec[x] == 1; altfel vei număra valorile greșit.

Aprofundare

Tehnica marcare-numărare este o aplicație directă a vectorului de frecvență și reduce complexitatea eliminării dublurilor de la O(n) (comparare directă) la O(n).

  • Condiția cheie: numărăm o valoare doar dacă o vedem pentru prima dată: if (frec[x] == 0) { frec[x] = 1; nr++; }.
  • Valori mari: dacă valorile depășesc domeniul unui vector rezonabil, se poate sorta șirul și număra apoi valorile consecutive diferite, în O(n log n).
  • Frecvență completă: dacă problema cere de câte ori apare fiecare valoare, folosim frec[x]++ în loc de frec[x] = 1; numărul de distincte devine numărul de poziții cu frec[i] > 0.
  • Legătura cu mulțimea: vectorul de prezență rezultat după marcare este exact reprezentarea mulțimii valorilor din șir, iar nr este cardinalul (numărul de elemente) ei.
  • Pentru BAC: această tehnică apare în problemele "câte valori distincte", "de câte ori apare un element", "elemente care apar o singură dată".
APLICARE ȘI REZOLVARE DE PROBLEME

PbInfo - Numărarea elementelor distincte

Enunț:

Cerință: Se citesc n și n numere naturale. Să se afișeze câte valori distincte conține șirul.

Date de intrare:

Programul citește de la tastatură numărul n, apoi n numere naturale, separate prin spații.

Date de ieșire:

Programul va afișa pe ecran numărul de valori distincte.

Restricții și precizări:

1 ≤ n ≤ 100; valorile citite sunt numere naturale între 1 și 100.

Exemplu:

Intrare:

8
4 1 4 2 1 4 2 3

Ieșire:

4
Cum gândim soluția:
  1. Marcăm fiecare valoare la prima apariție
  2. Numărăm valorile marcate
  3. Afișăm numărul obținut
Scrie soluția în C++:
Soluție corectă:
#include <iostream>
using namespace std;

int main() {
    int n, x, i, frec[101] = {0}, nr = 0;
    cin >> n;
    for (i = 1; i <= n; i++) {
        cin >> x;
        if (frec[x] == 0) {
            frec[x] = 1;
            nr++;
        }
    }
    cout << nr;
    return 0;
}

Ce învățăm: metoda marcare-numărare elimină dublurile la citire, fără a mai fi nevoie să păstrăm întregul șir.

PbInfo - Afișarea fără dubluri

Enunț:

Cerință: Se citesc n și n numere naturale. Să se afișeze valorile distincte, în ordine crescătoare, fără dubluri.

Date de intrare:

Programul citește de la tastatură numărul n, apoi n numere naturale, separate prin spații.

Date de ieșire:

Programul va afișa valorile distincte, în ordine crescătoare, separate prin spații.

Restricții și precizări:

1 ≤ n ≤ 100; valorile citite sunt numere naturale între 1 și 100.

Exemplu:

Intrare:

8
4 1 4 2 1 4 2 3

Ieșire:

1 2 3 4
Cum gândim soluția:
  1. Marcăm prezența fiecărei valori
  2. Parcurgem vectorul de prezență crescător
  3. Afișăm valorile marcate
Scrie soluția în C++:
Soluție corectă:
#include <iostream>
using namespace std;

int main() {
    int n, x, i, frec[101] = {0};
    cin >> n;
    for (i = 1; i <= n; i++) {
        cin >> x;
        frec[x] = 1;
    }
    for (i = 1; i <= 100; i++)
        if (frec[i] == 1)
            cout << i << " ";
    return 0;
}

Ce învățăm: vectorul de prezență elimină dublurile, iar parcurgerea crescătoare produce lista sortată fără repetiții.

EXERCIȚII PRACTICE

Exercițiul Practic: Eliminăm dublurile

Sarcini de lucru

Completează următoarele sarcini pentru a elimina dublurile dintr-un șir

Sarcina 1: Numără distinctele

Cerință: Pentru șirul 7 1 7 2 1 7 3 3, scrie câte valori distincte există și enumeră-le.

Soluție pentru Sarcina 1:
Număr de valori distincte: 4
Valorile: 1, 2, 3, 7
Sarcina 2: Scrie programul

Cerință: Scrie un program C++ care citește n și n numere naturale, apoi afișează numărul de valori distincte folosind metoda marcare-numărare.

Soluție pentru Sarcina 2:
#include <iostream>
using namespace std;

int main() {
    int n, x, i, frec[101] = {0}, nr = 0;
    cin >> n;
    for (i = 1; i <= n; i++) {
        cin >> x;
        if (frec[x] == 0) {
            frec[x] = 1;
            nr++;
        }
    }
    cout << nr;
    return 0;
}

Scrie pe caiet - De reținut

Titlul lecției

Ora 6: Eliminarea dublurilor dintr-un șir

Definiția lecției

Eliminarea dublurilor dintr-un șir înseamnă păstrarea fiecărei valori o singură dată, în ordinea primei apariții. Folosim vectorul de frecvență ca vector de prezență: la fiecare valoare citită x verificăm dacă a mai fost văzută, testând frec[x] == 0. Dacă nu a mai fost văzută, o marcăm cu frec[x] = 1 și o numărăm sau o afișăm; altfel o ignorăm. Un exemplu real: numărarea produselor distincte vândute într-o zi la un magazin, pornind de la lista completă a vânzărilor, care conține multe repetări. Algoritmul are complexitate liniară O(n), fiind mult mai rapid decât compararea fiecărui element cu toate cele dinaintea lui, care are complexitate pătratică O(n²).

Definiții esențiale

Algoritmul cheie - pseudocod

citeste n frec[1..100] = 0 nr = 0 pentru i = 1, n executa citeste x daca frec[x] == 0 atunci frec[x] = 1 nr = nr + 1 scrie nr

Algoritmul cheie - cod C++

C++ - Numărarea elementelor distincte
#include <iostream>
using namespace std;

int main() {
    int n, x, i, frec[101] = {0}, nr = 0;
    cin >> n;
    for (i = 1; i <= n; i++) {
        cin >> x;
        if (frec[x] == 0) {
            frec[x] = 1;
            nr++;
        }
    }
    cout << nr;
    return 0;
}

Greșeli comune de evitat

Sfaturi practice și trucuri

Exemplu practic

Profesorul are lista notelor acordate la un test, cu repetări, și vrea să afle câte note distincte există:

C++ - Numărul de note distincte
#include <iostream>
using namespace std;

int main() {
    int n, x, i, frec[101] = {0}, nr = 0;
    cin >> n;
    for (i = 1; i <= n; i++) {
        cin >> x;
        if (frec[x] == 0) {
            frec[x] = 1;
            nr++;
        }
    }
    cout << nr;
    return 0;
}