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 umjestoend
iteratora 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_of
provjera predikata -
for_each
,for_each_n
(C++17) primjena funkcije na elemente -
count
,count_if
broj elemenata koji zadovoljavaju kriterij -
mismatch
prvo mjesto razlikovanja -
find
,find_if
,find_if_not
nalaženje elementa -
find_end
nalaženje posljednjeg niza elemenata -
find_first_of
nalaženje bilo koje elementa iz skupa -
adjacent_find
nalaženje prva dva jednaka elementa -
search
,search_n
potraga za nizom elemenata
Popis algoritama (3)
Modificirajući algoritmi
Definirani su u zaglavlju <algorithm>
.
-
copy
,copy_if
,copy_n
,copy_backward
kopiranje elemenata -
move
,move_backward
micanje elemenata -
fill
,fill_n
kopiranje zadane vrijednosti -
transform
primjeni funkciju na kolekciju -
generate
,generate_n
sukcesivni funkcijski pozivi -
remove
,remove_if
,remove_copy
,remove_copy_if
uklanjanje elemenata -
replace
,replace_if
,replace_copy
,replace_copy_if
zamjena elemenata -
swap
,swap_ranges
,iter_swap
međusobna zamjena elemenata -
reverse
,reverse_copy
inverzija kolekcije -
rotate
,rotate_copy
rotacija elemenata kolekcije -
shift_left
,shift_right
(C++20) pomak elemenata -
shuffle
slučajno preuređivanje -
sample
(C++17) slučajni izbor elemenata -
unique
,unique_copy
ukloni konsekutivne duplikate
Popis algoritama (4)
Definirani su u zaglavlju <algorithm>
.
Operacije particioniranja
-
is_partitioned
ispitivanja particioniranosti -
partition
,partition_copy
,stable_partition
particioniranje -
partition_point
lociranje razdijelne točke
Operacije sortiranja
-
is_sorted
provjera sortiranosti -
is_sorted_until
najveći sortirani raspon -
sort
,stable_sort
sortiranje -
partial_sort
,partial_sort_copy
,nth_element
parcijalno sortiranje
Operacije na sortiranim kolekcijama
-
lower_bound
prvi element koji nije manji od zadanog -
upper_bound
prvi element veći od zadanog -
binary_search
binarno pretraživanje -
equal_range
raspon ekvivalentnih elemenata -
merge
,inplace_merge
spajanje sortiranih kolekcija
Popis algoritama (5)
Definirani su u zaglavlju <algorithm>
.
Skupovne operacije
-
includes
inkluzija -
set_difference
skupovna razlika -
set_intersection
presjek -
set_symmetric_difference
simetrična razliak -
set_union
unija
Opearcije na hrpi
-
is_heap
je li max hrpa -
is_heap_until
najveći raspon koji je max hrpa -
make_heap
učini max hrpom -
push_heap
dodaj element u max hrpu -
pop_heap
ukloni element iz max hrpe -
sort_heap
pretvorimax hrpu u rastući niz
Minimum/maksimum operacije
-
max
,max_element
maksimum -
min
,min_element
minimum -
minmax
,minmax_element
minimum i maksimum -
clamp
(C++17) odsjecanje između dva praga
Opearacije uspoređivanja
-
equal
jednakost -
lexicographical_compare
leksikografsko uspoređivanje -
compare_3way
,lexicographical_compare_3way
(C++20) trostruko uspoređivanje
Permutacije
-
is_permutation
je li permutacija nečega -
next_permutation
sljedeća permutacija -
prev_permutation
prethodna permutacija
Popis algoritama (6)
Numeričke operacije
Definirani su u zaglavlju <numeric>
.
-
iota
ispuni suksesivnim inkrementima -
accumulate
,reduce
(C++17) sumiraj -
inner_product
skalarni produkt -
adjacent_difference
diferencije 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_n
kopiranje -
uninitialized_fill
,uninitialized_fill_n
ispunjavanje -
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
copy
icopy_if
uzimaju raspon elemenata koje kopiraju[srcBeg,srcEnd)
i kopiraju ih u spremnik čiji je početni iteratordestBeg
. -
Algoritam
copy_n
uzima iterator na prvi element ulaznog niza i broj elemenata koje treba kopirati. -
Algoritmi
copy
,copy_if
icopy_n
iteriraju u pozitivnom smjeru pri kopiranju (odsrcBeg
premasrcEnd
) dok algoritamcopy_backward
iterira u negativnom smjeru (odsrcEnd
premasrcBeg
). Stogacopy_backward
uzima end-iterator izlaznog niza. -
Algoritam
copy_if
kopira element samo ako predikatop
vratitrue
. -
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
-
remove
algoritam se ne koristi sa listom koja ima (efikasniju)remove
metodu. S listom se ne koristi nitisort
algoritam jer ona imasort
metodu koja je efikasnija. -
remove
algoritam se ne koristi sa asocijativnim spremnicima koji imaju verzijuerase
metode koja radi istu operaciju. Asocijativni spremnicimap
,set
,multimap
imultiset
se 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