Sortiranje u C++: funkcija sort i poređenje

Lekcija 4 od 5 · Sortiranje

Na takmičenjima se sortiranje skoro nikada ne piše ručno – koristi se funkcija sort iz biblioteke <algorithm>. Ona radi O(n log n), a uz sopstvenu funkciju poređenja sortira po bilo kom kriterijumu: opadajuće, po više kriterijuma, strukture, parove i stringove.

Pre nego što počneš: potrebno predznanje – jednostavni algoritmi sortiranja, vector u C++, strukture.
Kako da učiš: svaki primer prekucaj i pokreni, a zadatke na kraju prvo reši sam/a; uputstvo i rešenje otvori tek posle toga. Svi programi su u C++-u.

Osnovna upotreba

Funkcija sort(početak, kraj) sortira rastuće deo od početak do kraj (bez kraj):

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int a[6] = {5, 2, 9, 1, 7, 3};
    sort(a, a + 6);                        // običan niz: od a do a + n
    for (int x : a) cout << x << " ";
    cout << endl;

    vector<int> v = {5, 2, 9, 1, 7, 3};
    sort(v.begin(), v.end());              // vektor: od begin() do end()
    for (int x : v) cout << x << " ";
    cout << endl;

    sort(v.begin(), v.end(), greater<int>());  // opadajuće
    for (int x : v) cout << x << " ";
    cout << endl;

    sort(v.begin() + 1, v.begin() + 4);    // samo elementi na pozicijama 1, 2 i 3
    for (int x : v) cout << x << " ";
    cout << endl;
    return 0;
}

Opadajući poredak može i bez greater: sortiraj rastuće pa obrni niz sa reverse(v.begin(), v.end()).

Sopstveni kriterijum: funkcija poređenja

Trećim argumentom zadajemo funkciju koja za dva elementa x i y vraća true ako x treba da bude ispred y. Na primer, sortiranje po zbiru cifara, a kod jednakog zbira po veličini broja:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int zbirCifara(int x) {
    int s = 0;
    while (x > 0) {
        s += x % 10;
        x /= 10;
    }
    return s;
}

// true ako x treba da bude ispred y
bool ispred(int x, int y) {
    if (zbirCifara(x) != zbirCifara(y)) return zbirCifara(x) < zbirCifara(y);
    return x < y;                           // isti zbir cifara: manji broj ide prvi
}

int main() {
    int n;
    cin >> n;
    vector<int> v(n);
    for (int &x : v) cin >> x;
    sort(v.begin(), v.end(), ispred);
    for (int x : v) cout << x << " ";
    cout << endl;
    return 0;
}
Važno: funkcija poređenja za jednake elemente mora da vrati false. Ako napišeš <= umesto <, program može da radi pogrešno ili da se sruši.

Umesto posebne funkcije može da se napiše lambda – funkcija napisana na mestu poziva:

sort(v.begin(), v.end(), [](int x, int y) {
    return x % 10 < y % 10;    // po poslednjoj cifri
});

Sortiranje struktura i parova

Najčešći takmičarski slučaj: rang lista. Takmičare sortiramo po bodovima opadajuće, a one sa istim brojem bodova po imenu abecedno.

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

struct Takmicar {
    string ime;
    int bodovi;
};

bool bolji(const Takmicar &x, const Takmicar &y) {
    if (x.bodovi != y.bodovi) return x.bodovi > y.bodovi;  // više bodova ide prvo
    return x.ime < y.ime;                                  // pa abecedno
}

int main() {
    int n;
    cin >> n;
    vector<Takmicar> t(n);
    for (auto &x : t) cin >> x.ime >> x.bodovi;
    sort(t.begin(), t.end(), bolji);
    for (int i = 0; i < n; i++)
        cout << i + 1 << ". " << t[i].ime << " " << t[i].bodovi << endl;
    return 0;
}

Parovi (pair) se bez dodatne funkcije porede prvo po first, pa po second. Trik: ako sačuvamo par (-bodovi, ime), običan sort daje istu rang listu. Parovi su zgodni i kada treba zapamtiti originalnu poziciju elementa:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<pair<int, int>> p(n);          // (vrednost, originalna pozicija)
    for (int i = 0; i < n; i++) {
        cin >> p[i].first;
        p[i].second = i + 1;
    }
    sort(p.begin(), p.end());
    // redni brojevi elemenata od najmanjeg do najvećeg
    for (auto &x : p) cout << x.second << " ";
    cout << endl;
    return 0;
}

Stringovi, stable_sort i uklanjanje duplikata

Stringovi se porede leksikografski (kao u rečniku, po ASCII kodovima: velika slova su ispred malih, a "ab" < "abc"), pa sort nad vektorom stringova daje abecedni redosled.

stable_sort radi isto što i sort, ali čuva redosled jednakih elemenata. Kombinacija sort + unique + erase uklanja duplikate:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> v = {4, 1, 4, 2, 1, 4, 3};
    sort(v.begin(), v.end());                          // 1 1 2 3 4 4 4
    v.erase(unique(v.begin(), v.end()), v.end());      // unique pomera različite na početak
    for (int x : v) cout << x << " ";
    cout << endl;
    cout << "Različitih: " << v.size() << endl;
    return 0;
}
FunkcijaŠta radiSloženost
sort(b, e)sortira rastućeO(n log n)
sort(b, e, greater<int>())sortira opadajućeO(n log n)
stable_sort(b, e, f)sortira i čuva redosled jednakihO(n log n)
reverse(b, e)obrće redosledO(n)
unique(b, e)uklanja uzastopne duplikate (pre toga sort)O(n)
nth_element(b, b + k, e)na mesto k stavlja element koji bi tu bio u sortiranom nizuO(n) prosečno
is_sorted(b, e)proverava da li je sortiranoO(n)

Zadaci za vežbu

Za svaki zadatak postoji primer, uputstvo i rešenje. Rešenja su provereno tačna na primerima iz teksta.

ZADATAK 01Težina: Srednje

Rang lista sa deljenim mestima

Dati su takmičari (ime i bodovi). Ispiši rang listu sortiranu po bodovima opadajuće, a kod istih bodova po imenu. Takmičari sa istim brojem bodova dele isto mesto, a sledeće mesto se preskače (na primer 1, 2, 2, 4).

Primer

Ulaz

5
Marko 80
Ana 95
Jovan 80
Iva 70
Luka 95

Izlaz

1. Ana 95
1. Luka 95
3. Jovan 80
3. Marko 80
5. Iva 70
ZADATAK 02Težina: Lako

Prvo parni, pa neparni

Sortiraj n prirodnih brojeva tako da prvo budu svi parni brojevi rastuće, a zatim svi neparni opadajuće.

Primer

Ulaz

7
5 8 3 2 9 4 1

Izlaz

2 4 8 9 5 3 1
ZADATAK 03Težina: Teže

Spajanje intervala

Dato je n intervala [a, b] (1 ≤ n ≤ 105). Intervali koji se preklapaju ili dodiruju spajaju se u jedan. Ispiši dobijene intervale sleva nadesno.

Primer 1

Ulaz

4
1 3
8 10
2 6
15 18

Izlaz

1 6
8 10
15 18

Primer 2

Ulaz

2
1 4
4 5

Izlaz

1 5
Česte greške i saveti
  • Za vektor se piše sort(v.begin(), v.end()), a za niz sort(a, a + n) – ne a + n - 1.
  • Funkcija poređenja koristi strogo < ili >, nikada <=.
  • Strukture prosleđuj funkciji poređenja po referenci (const Takmicar &) – inače se pri svakom poređenju kopiraju.
  • unique uklanja samo uzastopne duplikate – niz prvo mora da se sortira.

Proveri znanje

Otvori kviz

Funkcija sort u C++

1. Kako se sortira ceo vektor v rastuće?




2. Šta vraća funkcija poređenja ispred(x, y)?




3. Kako se porede dva para (a, b) i (c, d)?




4. Šta treba uraditi pre unique da bi se uklonili svi duplikati?