Algoritmi
Algoritmi djeluju na spremnike kroz iteratore.
-
Algoritmi nikada ne izvršavaju operacije koje nude spremnici i ne mijenjaju veličinu spremnika.
-
Svi su algoritmi definirani u zaglavlju
<algorithm>osim nekoliko numeričkih koji su definirani u zaglavlju<numeric>te algoritama u<memory>zaglavlju. -
Algoritam se može pojaviti u više verzija i tada dobiva sufiks:
-
Sufiks
_if. Ova verzija uzima predikat dok osnovna verzija uzima vrijednost. Na primjer,find()ifind_if(). -
Sufiks
_copy. Ova verzija kopira ulazni niz, dok osnovna radi na njemu. Na primjer,reverse()ireverse_copy(). -
Sufiks
_n. Ova verzija umjestoenditeratora uzima broj elemenata s kojima treba raditi.
-
-
Algoritmi koji imaju ulazan i izlazan niz pretpostavljaju da izlazan niz ima isti broj elemenata kao i ulazan i stoga je izlazni niz zadan samo svojim početnim iteratorom.
Popis algoritama (1)
Algoritmi su podijeljeni u nekoliko grupa:
-
Nemodificirajući algoritmi
-
Modificirajući algoritmi
-
Operacije particioniranja
-
Operacije sortiranja
-
Operacije na sortiranim kolekcijama
-
Skupovne operacije
-
Opearcije na hrpi
-
Minimum/maksimum operacije
-
Opearacije uspoređivanja
-
Permutacije
-
Numeričke operacije
-
Operacije na neinicijaliziranoj memoriji
Osnovna literatura za algoritme je https://en.cppreference.com
Popis algoritama (2)
Nemodificirajući algoritmi
Definirani su u zaglavlju <algorithm>.
-
all_of,any_of,none_ofprovjera predikata -
for_each,for_each_n(C++17) primjena funkcije na elemente -
count,count_ifbroj elemenata koji zadovoljavaju kriterij -
mismatchprvo mjesto razlikovanja -
find,find_if,find_if_notnalaženje elementa -
find_endnalaženje posljednjeg niza elemenata -
find_first_ofnalaženje bilo koje elementa iz skupa -
adjacent_findnalaženje prva dva jednaka elementa -
search,search_npotraga za nizom elemenata
Popis algoritama (3)
Modificirajući algoritmi
Definirani su u zaglavlju <algorithm>.
-
copy,copy_if,copy_n,copy_backwardkopiranje elemenata -
move,move_backwardmicanje elemenata -
fill,fill_nkopiranje zadane vrijednosti -
transformprimjeni funkciju na kolekciju -
generate,generate_nsukcesivni funkcijski pozivi -
remove,remove_if,remove_copy,remove_copy_ifuklanjanje elemenata -
replace,replace_if,replace_copy,replace_copy_ifzamjena elemenata -
swap,swap_ranges,iter_swapmeđusobna zamjena elemenata -
reverse,reverse_copyinverzija kolekcije -
rotate,rotate_copyrotacija elemenata kolekcije -
shift_left,shift_right(C++20) pomak elemenata -
shuffleslučajno preuređivanje -
sample(C++17) slučajni izbor elemenata -
unique,unique_copyukloni konsekutivne duplikate
Popis algoritama (4)
Definirani su u zaglavlju <algorithm>.
Operacije particioniranja
-
is_partitionedispitivanja particioniranosti -
partition,partition_copy,stable_partitionparticioniranje -
partition_pointlociranje razdijelne točke
Operacije sortiranja
-
is_sortedprovjera sortiranosti -
is_sorted_untilnajveći sortirani raspon -
sort,stable_sortsortiranje -
partial_sort,partial_sort_copy,nth_elementparcijalno sortiranje
Operacije na sortiranim kolekcijama
-
lower_boundprvi element koji nije manji od zadanog -
upper_boundprvi element veći od zadanog -
binary_searchbinarno pretraživanje -
equal_rangeraspon ekvivalentnih elemenata -
merge,inplace_mergespajanje sortiranih kolekcija
Popis algoritama (5)
Definirani su u zaglavlju <algorithm>.
Skupovne operacije
-
includesinkluzija -
set_differenceskupovna razlika -
set_intersectionpresjek -
set_symmetric_differencesimetrična razliak -
set_unionunija
Opearcije na hrpi
-
is_heapje li max hrpa -
is_heap_untilnajveći raspon koji je max hrpa -
make_heapučini max hrpom -
push_heapdodaj element u max hrpu -
pop_heapukloni element iz max hrpe -
sort_heappretvorimax hrpu u rastući niz
Minimum/maksimum operacije
-
max,max_elementmaksimum -
min,min_elementminimum -
minmax,minmax_elementminimum i maksimum -
clamp(C++17) odsjecanje između dva praga
Opearacije uspoređivanja
-
equaljednakost -
lexicographical_compareleksikografsko uspoređivanje -
compare_3way,lexicographical_compare_3way(C++20) trostruko uspoređivanje
Permutacije
-
is_permutationje li permutacija nečega -
next_permutationsljedeća permutacija -
prev_permutationprethodna permutacija
Popis algoritama (6)
Numeričke operacije
Definirani su u zaglavlju <numeric>.
-
iotaispuni suksesivnim inkrementima -
accumulate,reduce(C++17) sumiraj -
inner_productskalarni produkt -
adjacent_differencediferencije elemenata -
partial_sum,exclusive_scan(C++17),inclusive_scan(C++17) parcijalne sume -
transform_reduce(C++17) primjena funkcije i zatim redukcija -
transform_exclusive_scan(C++17) primjena funkcije iexclusive_scan -
transform_inclusive_scan(C++17) primjena funkcije iinclusive_scan
Operacije na neinicijaliziranoj memoriji
Definirani su u zaglavlju <memory>.
-
uninitialized_copy,uninitialized_copy_nkopiranje -
uninitialized_fill,uninitialized_fill_nispunjavanje -
uninitialized_move(C++17),uninitialized_move_n(C++17) premještanje -
uninitialized_default_construct(C++17),uninitialized_default_construct_n(C++17) defaultna konstrukcija -
uninitialized_value_construct(C++17),uninitialized_value_construct_n(C++17) vrijednosna inicijalizacija -
destroy_at(C++17),destroy(C++17),destroy_n(C++17) destrukcija
Kopiranje
Ovi algoritmi kopiraju ulazni niz u izlazni niz. Ulazni niz je dan kao raspon elemenata, a izlazni niz je dan samo iteratorom koji pokazuje na prvi element niza.
it = copy(srcBeg, srcEnd, destBeg)
it = copy_if(srcBeg, srcEnd, destBeg, op)
it = copy_n (srcBeg, num, destBeg)
it = copy_backward (srcBeg, srcEnd, destEnd)
-
Algoritmi
copyicopy_ifuzimaju raspon elemenata koje kopiraju[srcBeg,srcEnd)i kopiraju ih u spremnik čiji je početni iteratordestBeg. -
Algoritam
copy_nuzima iterator na prvi element ulaznog niza i broj elemenata koje treba kopirati. -
Algoritmi
copy,copy_ificopy_niteriraju u pozitivnom smjeru pri kopiranju (odsrcBegpremasrcEnd) dok algoritamcopy_backwarditerira u negativnom smjeru (odsrcEndpremasrcBeg). Stogacopy_backwarduzima end-iterator izlaznog niza. -
Algoritam
copy_ifkopira element samo ako predikatopvratitrue. -
Svi algoritmi vraćaju iterator na mjesto iza zadnjeg kopiranog elementa u izlaznom nizu.
-
Algoritmi pretpostavljaju da je izlazni niz dovoljno velik da primi sve elemente.
-
Složenost: linearna
list<int> lst{1,2,3,4,5};
vector<int> vec(5);
copy(lst.begin(), lst.end(), vec.begin()); // Kopiraj listu u vektor
fill(vec.begin(), vec.end(), 0);
// Kopiraj samo neparne brojeve iz liste u vektor
copy_if(lst.begin(), lst.end(), vec.begin(),
[](int x){ return x % 2; });
// Pisanje preko istog spremnika.
vector<int> vec1{1,2,3,4,5,6,7,8,9};
copy_n(next(vec1.begin(),4),4,vec1.begin()); // 5,6,7,8,5,6,7,8,9,
vector<char> vec2(10,'.');
vec2.push_back('a');
vec2.push_back('b');
vec2.push_back('c');
print(vec2);
// Insertiraj točkice s ulaza na kraj spremnika
vec2.insert(vec2.end(), 10, '.'); // ..........abc..........
// Za kopiranje na kraj istog spremnika koristimo copy_backward
copy_backward(vec2.begin()+10, vec2.begin()+13, vec2.end());
// ..........abc.......abc
for_each i transform
for_each
Algoritam for_each djeluje na kolekciji zadanoj sa dva iteratora i na svakom elementu kolekcije
poziva funkciju koja je treći argument algoritma. Signatura algoritma je sljedeća:
UnaryProc for_each (InputIterator beg, InputIterator end, UnaryProc op)
gdje je UnaryProc tip funkcije (funkcijskog objekta, lambda izraza) koji se poziva na svakom elementu kolekcije.
Povratna vrijednost je kopija objekta op. Povratna vrijednost od op se ignorira.
Algoritam for_each može mijenjati elemente spremnika ako op uzima argument poreferenci. Ako op
ima unutarnje stanje, to stanje će biti dohvatljivo kroz povratnu vrijednost algoritma.
void f(int& n) { n = n*n; }
// ...
std::vector<int> vek{0,1,2,3,4,5,6,7,8,9};
std::for_each(vek.begin(), vek.end(), f);
// sada je vek = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
int noParni = 0;
std::for_each(vek.begin(), vek.end(), [&noParni](int x){
if(!(x % 2))
noParni++;
});
std::cout << noParni << std::endl; // 5
transform
Algoritam transform prolazi kroz ulaznu kolekciju, na svakom elementu kolekcije poziva funkciju op i rezultat
ispisuje u izlaznu kolekciju. Vraća iterator koji pokazuje iza zadnjeg transformiranog elementa u izlaznoj kolekciji.
Signatura algoritma je sljedeća:
OutputIterator transform(InputIterator srcBeg, InputIterator srcEnd, OutputIterator destBeg, UnaryFunc op)
Na primjer,
int f(int n) { return n*n; }
// ...
std::vector<int> vek{0,1,2,3,4,5,6,7,8,9};
std::transform(vek.begin(), vek.end(), vek.begin(), f);
// vek = 0 1 4 9 16 25 36 49 64 81
Algoritam ima verziju koja uzima dva ulazna niza i funkciju sa dva argumenta koja ulazne nizove transformira u izlazni. Na primjer,
int f2(int n, int m) { return n-m; }
// ...
std::random_device rd; // zaglavlje <random>
std::mt19937 g(rd());
std::vector<int> vek1 = vek, vek2(vek.size());
std::shuffle(vek1.begin(), vek1.end(), g); // slučajna permutacija
std::transform(vek.begin(), vek.end(), vek1.begin(), vek2.begin(), f2);
// vek2 = vek - vek1
Nalaženje elementa
count
Algoritam count() vraća broj pojavljivanja vrijednosti u nizu: count_if vraća broj elemenata na kojima je
predikat istinit.
n = count(beg, end, value)
n = count_if(beg, end, op)
Na primjer,
int A[] = { 2, 0, 4, 6, 0, 3, 1, -7 };
const int N = sizeof(A) / sizeof(int);
std::cout << "Broj nula: "
<< std::count(A, A + N, 0) << std::endl;
std::cout << "Broj brojeva > 2: "
<< std::count_if(A, A + N, [](int x){ return x>2; }) << std::endl;
find
Ovi algoritmi vraćaju iterator na prvi nađeni element ili end iterator. Osnovna verzija traži vrijednost,
a druge dvije traže prvi element na kojem je predikat istinit (find_if) ili neistinit (find_if_not).
it = find (beg, end, value)
it = find_if(beg, end, op)
it = find_if_not(beg, end, op)
Na primjer,
std::vector<int> vec{1,4,1,9,4,5,5,7,4};
auto it = find(vec.begin(), vec.end(), 4);
assert(*it == 4);
assert(std::distance(vec.begin(), it) == 1);
it = find_if_not(vec.begin(), vec.end(), [](int x){ return x<7;});
assert(*it == 9);
assert(std::distance(vec.begin(), it) == 3);
search
Algoritam search traži niz elemenata u kolekciji. Kolekcija koja se pretražuje je dana s prvim parom
iteratora (beg i end), a niz elemenata koji tražimo je zadan s drugim parom iteratora (beg1 i end1).
Za uspoređivanje elemenata se u prvoj verziji algoritma koristi operator ==, a u drugoj binarni predikat
op: op(elem,searchElem) vraća "istinu" ako su elementi isti, a u suprotnom laž.
it = search(beg, end, beg1, end1)
it = search(beg, end, beg1, end1, op)
std::list<int> li{1,2,3,4,5,1,2,3,4,5};
std::array<int,3> niz{3,4,5};
auto it1 = std::search(li.begin(), li.end(), niz.begin(), niz.end());
if(it1 != li.end()){
it1++; // Nađi drugu grupu "niz"
auto it2 = std::search(it1, li.end(), niz.begin(), niz.end());
assert(*it2 == 3);
assert(std::distance(li.begin(),it2) == 7);
}
Algoritam search traži prvi podniz u nizu i vraća iterator na prvu poziciju nađenog podniza ili end iterator.
Algoritam koji traži zadnji podniz u nizu naziva se find_end (umjesto search_end, što bi bilo konzistentno).
Generiranje vrijednosti
fill
Ovaj algoritam inicijalizira svaki element kolekcije sa zadanom vrijednošću. Ne vraća ništa.
#include <cmath>
std::vector<double> vec(10);
std::fill(vec.begin(), vec.end(), M_PI);
generate
Ovaj algoritam inicijalizira svaki element kolekcije pozivom funkcije op() koja ne uzima argumente.
#include <cstdlib> // za rand
std::vector<int> vec(5);
std::generate(vec.begin(), vec.end(), rand); // 1804289383,846930886,1681692777,1714636915,1957747793
iota
Algoritam iota generira niz sikcesivnih vrijednosti (svaka sljedeća je veća za 1)
polazeći od dane vrijednosti.
#include <numeric> // za iota
std::vector<float> vec(5);
std::iota(vec.begin(), vec.end(), 15);// 15,16,17,18,19
Zamjena vrijednosti
Algoritam replace svako pojavljivanje stare vrijednsti zamijenjuje s novom.
Verzija replace_if zamijenjuje svaki element na kojem unarni predikat op vrati istinu s
novom vrijednošću. Verzija _copy ovog algoritma kombinira copy i replace i smješta transformirane
elemente u izlaznu kolekciju.
replace(beg, end, oldValue, newValue)
replace_if(beg, end, op, newValue)
replace_copy(beg, end, destBeg, oldValue, newValue)
replace_copy_if(beg, end, destBeg, op, newValue)
Na primjer,
std::vector<char> vec{'a','b','c','d','e','f'};
std::replace(vec.begin(), vec.end(), 'a', 'x'); // x,b,c,d,e,f
std::vector<char> vec1;
std::replace_copy(vec.begin(), vec.end(), std::back_inserter(vec1), 'e', 'x');
// x,b,c,d,x,f
Brisanje elemenata
Algoritmi ne mogu brisati elemente spremnika te stoga samo
prebacuje elemente određene za brisanje na kraj spremnika i
vraća novi end iterator spremnika (iterator koji pokazuje na prvi eliminirani element).
Pri tome algoritam ne garantira da će elementi određeni za brisanje biti prebačeni na
kraj spremnika. U većini implementacija oni će biti jednostavno prebrisani.
remove
Algoritam remove briše sve sve elemente jednake zadanom iz kolekcije. Na primjer,
Nakon prolaza remove algoritma potrebno je zvati funkciju spremnika erase kako bi se
izvršilo stvarno brisanje elemenata.
std::vector<int> ivecc{1,2,3,4,5,6,7,8,9,0};
auto newend = std::remove(ivecc.begin(), ivecc.end(), 1); // samo premještanje elemenata
ivecc.erase(newend, ivecc.end()); // stvarno brisanje elemenata
Verzija remove_if briše one elemente na kojima predikat vrati istinu.
Verzija remove_copy kombinira copy i remove. Ulazni niz ostaje isti, a na izlazni se kopiraju
samo oni elementi koji nisu izbrisani. Na primjer,
std::vector<char> vec{'a','b','c','a','d','e','f'};
std::remove_copy(vec.begin(), vec.end(), std::ostream_iterator<char>(std::cout, " "), 'a'); // b,c,d,e,f
-
removealgoritam se ne koristi sa listom koja ima (efikasniju)removemetodu. S listom se ne koristi nitisortalgoritam jer ona imasortmetodu koja je efikasnija. -
removealgoritam se ne koristi sa asocijativnim spremnicima koji imaju verzijuerasemetode koja radi istu operaciju. Asocijativni spremnicimap,set,multimapimultisetse ne sortiraju jer su u njima po konstrukciji elementi sortirani.
unique
Algoritam
unique
eliminira sve kosekutivne duplikate u spremiku (otkrivene operatorom
==).
Metoda std::sort služi
sortiranju spremika (pomoću operatora <) i pozivamo ju prije
std::unique() kako bismo osigurali da su duplikati konsekutivni:
// Izbacivanje duplikata
std::vector<std::string> lista_imena;
// Ubacimo neka imena
lista_imena.push_back("Ante");
lista_imena.push_back("Lovre");
lista_imena.push_back("Karmela");
lista_imena.push_back("Ante");
lista_imena.push_back("Lovre");
// Sortirajmo listu kako bi se ista imena našla jedna do drugih
std::sort(lista_imena.begin(), lista_imena.end());
// "brisanje" duplikata
auto it_unique = std::unique(lista_imena.begin(), lista_imena.end());
// stvarno brisanje
lista_imena.erase(it_unique, lista_imena.end()); // Ante Karmela Lovre
Kao i kod remove algoritma, postoje i unique_if i unique_copy algoritmi.
Algoritmi na sortiranim rasponima
Na spremnicima čiji su elementi sortirani za pretraživanja možemo koristiti algoritme:
Asocijativni spremnici map, multimap, set i multiset imaju
implementirane metode lower_bound, upper_bound i equal_range.
Nesortirani asocijativni spremnici unordered_map, itd. imaju
implementiranu metodu equal_range.
-
lower_bound za dani element vraća iterator na prvu poziciju koja je veća ili jednaka od danog elementa. Kada tražene pozicije nema vraća se end iterator.
-
upper_bound za dani element vraća iterator na prvu poziciju koja je strogo veća od danog elementa. Kada tražene pozicije nema vraća se end iterator.
-
equal_range za dani element vraća par iteratora. Prvi je onaj koji vraća
lower_bound, a drugi je onaj koji vraćaupper_bound. Ako su vraćeni iteratori jednaki onda element nije nađen. Ako je vraćeni raspon neprazan, onda on sadrži sve elemente ekvivalentne traženom elementu. -
binary_search ispituje dali je zadani element u rasponu metodom binarnog pretraživanja (i stoga raspon mora biti sortiran).
Primjer:
std::default_random_engine r_engine;
r_engine.seed( std::time(nullptr) );
std::uniform_int_distribution<unsigned int> dist(0, 7);
std::vector<unsigned int> vec(20);
std::generate(vec.begin(), vec.end(), [&dist, &r_engine]() { return dist(r_engine); } );
// npr. 3,7,3,4,7,4,1,6,2,6,6,7,4,0,2,5,1,6,2,2
std::sort(vec.begin(), vec.end());
// 0,1,1,2,2,2,2,3,3,4,4,4,5,6,6,6,6,7,7,7
auto its = std::equal_range(vec.begin(), vec.end(), 4);
for(auto it = its.first; it != its.second; ++it)
std::cout << "vec[" << std::distance(vec.begin(), it) << "]=" << *it << ",";
std::cout << std::endl; // vec[9]=4,vec[10]=4,vec[11]=4