Professional Documents
Culture Documents
Programski Jezik C Sa Zbirkom Urađenih Zadataka
Programski Jezik C Sa Zbirkom Urađenih Zadataka
III
1.2.7.6 Naredba goto ............................................................................... 37
1.2.8 Pokazivači .......................................................................................... 38
1.2.9 Nizovi ................................................................................................. 40
1.2.9.1 Jednodimenzioni nizovi................................................................. 40
1.2.9.2 Višedimenzioni nizovi................................................................... 42
1.2.9.3 Inicijalizacija nizova ..................................................................... 44
1.2.9.4 Kombinovane deklaracije nizova i pokazivača ............................. 45
1.2.10 Rad sa stringovima ............................................................................. 46
1.2.10.1 Neke korisne funkcije za rad sa stringovima............................... 48
1.2.10.2 Rad sa većim brojem stringova ................................................... 49
1.2.11 Funkcije .............................................................................................. 51
1.2.11.1 Prototipovi funkcija ..................................................................... 55
1.2.11.2 Funkcije i nizovi .......................................................................... 55
1.2.11.3 Funkcije i matrice ........................................................................ 57
1.2.11.4 Kombinovane deklaracije nizova, pokazivača i funkcija. ........... 59
1.2.11.5 Pokazivači na funkcije ................................................................ 59
1.2.11.6 Funkcija main .............................................................................. 61
1.2.11.7 Rekurzivne funkcije .................................................................... 62
1.2.12 Pretraživanje i sortiranje nizova ......................................................... 64
1.2.12.1 Algoritmi za pretraživanje nizova ............................................... 64
1.2.12.2 Algoritmi za sortiranje nizova ..................................................... 66
1.2.13 Složenost algoritama .......................................................................... 68
1.2.13.1 Analiza najgoreg i prosječnog slučaja ......................................... 70
1.2.13.2 Rast složenosti. Veliko O ............................................................ 70
1.2.13.3 Studija slučaja: Broj podnizova sa parnom sumom .................... 72
1.2.14 Ključne riječi typedef i const ............................................................. 75
1.2.15 Pretprocesor........................................................................................ 76
1.2.16 Važnije programske biblioteke i odgovarajuće funkcije .................... 78
1.2.16.1 stdio.h ..................................................................................... 78
1.2.16.2 string.h ................................................................................... 81
1.2.16.3 ctype.h ..................................................................................... 81
1.2.16.4 math.h........................................................................................ 81
1.2.16.5 limits.h ................................................................................... 82
1.2.16.6 stdlib.h ................................................................................... 82
1.2.17 Karakteristike promjenljivih .............................................................. 82
1.2.17.1 Lokalne i globalne promjenljive.................................................. 82
1.2.17.2 Dinamičke i statičke promjenljive ............................................... 83
1.2.17.3 Eksterne promjenljive ................................................................. 84
1.2.17.4 Ključne riječi register, auto i volatile ............................. 85
1.2.18 Dinamička alokacija i dealokacija ...................................................... 85
1.2.19 Enumeracija........................................................................................ 87
1.2.20 Strukture ............................................................................................. 87
1.2.20.1 Deklaracija strukturnih promjenljivih. Operator . ....................... 88
IV
1.2.20.2 Pokazivači na strukture. Operator ‐> ...........................................90
1.2.20.3 Strukture i funkcije.......................................................................92
1.2.21 Unije ....................................................................................................93
1.2.22 Polja bitova .........................................................................................94
1.2.23 Rad sa fajlovima..................................................................................95
1.2.23.1 Otvaranje fajla ..............................................................................95
1.2.23.2 Upis i čitanje iz fajla. Kretanje kroz fajl. Zatvaranje fajla ...........97
1.2.23.3 Brisanje i promjena imena fajla ...................................................99
1.2.23.4 Redirekcija ...................................................................................99
1.2.24 Još malo o standardima C99 i C11 ....................................................100
1.2.24.1 Tipovi podataka bool i complex .............................................101
1.2.24.2 Cjelobrojni tipovi fiksne veličine ...............................................102
1.2.24.3 inline funkcije ........................................................................102
1.2.24.4 Složeni literali ............................................................................103
1.2.24.5 Funkcija zna svoje ime i još ponešto..........................................104
1.2.24.6 Standard C11 i njegove novine ..................................................105
1.3 Linkovani tipovi podataka .......................................................................... 107
1.3.1 Liste...................................................................................................107
1.3.1.1 Kreiranje jednostruko povezane liste ...........................................108
1.3.1.2 Umetanje čvora u unutrašnjost liste .............................................109
1.3.1.3 Brisanje repa liste .........................................................................110
1.3.1.4 Brisanje čvora iz unutrašnjosti liste .............................................110
1.3.1.5 Dvostruko povezane liste .............................................................111
1.3.1.6 Umetanje čvora u unutrašnjost dvostruko povezane liste ............111
1.3.1.7 Brisanje čvora iz unutrašnjosti dvostruko povezane liste ............112
1.3.1.8 Specijalni tipovi listi ....................................................................112
1.3.1.9 Primjer programa za rad sa listom ...............................................113
1.3.2 Grafovi ..............................................................................................117
1.3.2.1 Predstavljanje grafova ..................................................................118
1.3.2.2 Problem najkraćeg puta ................................................................119
1.3.3 Stabla .................................................................................................121
2. ZBIRKA ZADATAKA .................................................................................... 127
2.1 Kontrola toka programa .............................................................................. 128
2.2 Nizovi .......................................................................................................... 141
2.3 Matrice ........................................................................................................ 146
2.4 Stringovi ...................................................................................................... 158
2.5 Funkcije....................................................................................................... 172
2.6 Rekurzivne funkcije .................................................................................... 191
2.7 Sortiranje ..................................................................................................... 197
2.8 Dinamička alokacija i dealokacija .............................................................. 207
2.9 Fajlovi ......................................................................................................... 214
V
2.10 Strukture i liste ............................................................................................231
2.11 Grafovi ........................................................................................................257
2.12 Binarno stablo .............................................................................................265
2.13 Igre ..............................................................................................................275
Literatura ....................................................................................................................299
VI
Predgovor drugom izdanju
I
Čitaocima želimo uspješno savladavanje programskog jezika C i nadamo se da će
im ovaj udžbenik biti od koristi. Konačno, ne zaboravite tajnu učenja programiranja –
programiranje se najbolje uči programirajući!
II
12 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
Uradimo, za početak, jedan jednostavan program. Tekst ovog programa, koga još
nazivamo i izvorni kôd (eng. source code) je dat u nastavku.
#include<stdio.h>
int main()
{
int a,b;
a=11;
b=7;
printf("Zbir brojeva %d i %d je %d",a,b,a+b);
return 0;
}
Tekst programa se unosi u editoru teksta, što može biti Notepad, Wordpad ili bilo
koje drugo okruženje za programski razvoj koje uključuje editor teksta. Fajlovi koji
sadrže C izvorni kôd imaju ekstenziju .c.
Nakon unošenja programskog teksta, pristupa se njegovom pretprocesiranju, koje
podrazumijeva niz operacija koje se izvršavaju nad samim izvornim kôdom, kao što su
zamjene dijelova teksta drugim tekstom, uklanjanje bjelina itd.
Nakon pretprocesiranja, slijedi prevođenje programa iz izvornog u mašinski kôd,
tj. kompajliranje. Dobijeni mašinski kôd je sa relativnim adresama programskih
instrukcija i naziva se objektni kôd.
U sljedećem koraku se vrši povezivanje (linkovanje) dobijenog objektnog kôda sa
mašinskim kôdom funkcija koje koristimo u našem programu, a koje se nalaze u raznim
bibliotekama. Rezultat povezivanja je jedna programska cjelina koja sadrži sve
procesorske instrukcije potrebne za pravilno izvršenje programa.
Na osnovu integralnog objektnog kôda programa, kreira se izvršni program u
apsolutnim adresama i smješta se u RAM memoriju. Ovaj dio procedure obavlja loader.
Konačno, program se izvršava tako što procesor uzima instrukciju po instrukciju
izvršnog kôda iz RAM memorije i izvršava ih.
Prethodno opisani koraci kreiranja i izvršenja jednog C programa su prikazani na
slici 1. Posmatrajući sve ove korake, može se steći utisak da je čitav proces vrlo
komplikovan. Međutim, zahvaljujući postojanju integrisanih razvojnih okruženja (eng.
Integrated Development Environment - IDE), unos, editovanje i prevođenje programa
1.2 P ROGRAMSKI JEZIK C 13
Sa standardom C99, uvodi se tip cjelobrojne promjenljive long long int, koji
zauzima 8 bajtova i čiji je opseg vrijednosti od od -263 do 263-1. Pri deklaraciji long
long int promjenljive se može izostaviti riječ int. Takođe, i kod ovog tipa ključna
riječ unsigned mijenja opseg vrijednosti, gdje nisu dozvoljene negativne vrijednosti.
U tabeli 4 su dati cjelobrojni tipovi podataka, broj bajtova koji zauzimaju i odgovarajući
opsezi vrijednosti.
Pored dekadnih brojeva, postoji mogućnost zapisivanja cjelobrojnih oktalnih
brojeva, koji uvijek počinju sa nulom (npr. 074) i heksadecimalnih brojeva, koji počinju
sa 0x (npr. 0x3c).
Na kraju, recimo par riječi o binarnoj reprezentaciji int tipova. Za memorijsko
predstavljanje označenih int tipova, koristi se zapis cijelih brojeva sa dvojnim
komplementom. Tako, binarne kombinacije short int tipa, koji zauzima 2 bajta, su
date u nastavku (baza brojnog sistema je data u donjem indeksu broja):
0000 0000 0000 00002 = 010
0000 0000 0000 00012 = 110
0000 0000 0000 00102 = 210
⁞
0111 1111 1111 11112 = 3276710
1000 0000 0000 00002 = -3276810
1000 0000 0000 00012 = -3276710
⁞
1111 1111 1111 11112 = -110
Kod neoznačenih int tipova, najmanja vrijednost je 0, a najveća 2M-1, gdje je M
broj bita koji zauzima predmetni tip. U ovom slučaju, binarne kombinacije unsigned
short int tipa su:
0000 0000 0000 00002 = 010
0000 0000 0000 00012 = 110
0000 0000 0000 00102 = 210
⁞
1111 1111 1111 11102 = 6553410
1111 1111 1111 11112 = 6553510
Kod ostalih int tipova se samo mijenja broj bita, a princip predstavljanja je isti.
Na osnovu binarne reprezentacije, protumačite šta će se dogoditi ako povećate za 1
vrijednost int promjenljive koja sadrži najveći cijeli broj koji se može upisati u nju.
Specifikator Tip
Cio broj. %d i %i su sinonimi pri štampi, ali se razlikuju prilikom
učitavanja funkcijom scanf (%i će interpretirati broj kao oktalni ako
%d, %i
počinje sa 0, odnosno kao heksadecimalni ako počinje sa 0x, dok %d to
neće uraditi).
%ld, %li long int
%lld, %lli long long int
Neoznačen cijeli broj (unsigned int). Može se kombinovati sa dugim
%u
formatima, npr. %lu, %llu.
%o Neoznačen cijeli broj kao oktalni broj.
Neoznačen cijeli broj kao heksadecimalni broj. %x koristi mala slova, dok
%x, %X
%X koristi velika.
float ili double u standardnom zapisu ([‐]ddd.ddd).
%f, %F Podrazumijevani broj decimalnih mjesta je 6. Formati %f i %F imaju vrlo
male razlike i to u načinu štampe specijalnih vrijednosti beskonačno i NaN.
double vrijednosti. Kod printf se ne mora navoditi l, tj. dovoljno je
%lf, %lF %f, prilikom štampanja double vrijednosti. Kod scanf se mora koristiti
%lf prilikom učitavanja double vrijednosti.
float ili double u eksponencijalnoj formi ([‐]d.ddd e[+/‐]ddd).
%e, %E Razlike %e i %E su samo u veličini slova e prilikom štampe, tj. %e
prikazuje e, dok %E prikazuje E.
float ili double u standardnoj ili eksponencijalnoj formi, zavisno koja
je pogodnija. %g koristi mala slova, dok %G koristi velika. Ovaj tip se
%g, %G razlikuje od %f i %F po tome što završne nule u decimalnom dijelu broja
nisu uključene, i po tome da se decimalna tačka ne prikazuje kod cijelih
brojeva.
%s String
%c Karakter
%p Pokazivač, u formatu zavisnom od implementacije.
Tabela 5. Specifikatori formata u programskom jeziku C.
dodatnih spejsova, čak i kada je b manje od broja cifara broja. Lijevo poravnanje broja
u okviru b polja se može dobiti na sljedeći način %‐bd. Na primjer, naredba
printf("%10d\n%‐10d\n%2d",12345,12345,12345);
će odštampati sljedeći tekst:
12345
12345
12345
U slučaju realnih brojeva, format %bf, gdje je b cijeli broj, podrazumjeva prikaz
decimalnog broja sa ukupno b mjesta (uključujući decimalnu tačku i predznak). Ukoliko
je b manje od ukupnog broja polja potrebnih za zapis broja, broj će se podrazumijevano
prikazati, tj. na način %f, uz podrazumijevanih šest decimalnih mjesta. Format %a.bf,
gdje su a i b cijeli brojevi, podrazumjeva prikaz decimalnog broja sa ukupno a mjesta
(uključujući decimalnu tačku i predznak) i b decimala. Po prirodi stvari, očekivano je
da je a veće od b. Ovdje takođe možemo mijenjati poravnanje broja u okviru a polja na
način %‐a.bf. Na primjer, naredbe:
double a=123.4567;
printf("%9f\n%‐13.4f\n%12.2f\n%.8f",‐a,a,a,a);
će odštampati sljedeći tekst:
‐123.456700
123.4567
123.46
123.45670000
Unutar drugog dijela naredbe mogu se nalaziti i matematički izrazi. Na primjer,
printf("%d\n", j++);
će prikazati vrijednost j i zatim je uvećati za 1. Takođe, mogu biti i pozivi funkcija koje
vraćaju vrijednost. Dobra praksa, ipak, preporučuje izbjegavanje ovih opcija jer mogu
dovesti do nečitkosti programa. Postoji još jedna opasnost kod ovakvog korišćenja
naredbe printf, a to je redosljed predaje argumenata, koji je sa desna na lijevo. To
znači da će naredbe:
j=1;
printf("%d %d\n",j,j++);
prikazati na ekranu broj 2, pa 1 i preći u novi red. Naime, prvi predati argumenat je
desni, koji nakon predaje biva uvećan za jedan, pa tek lijevi, koji je tada 2.
Funkcija scanf služi za unos, tj. učitavanje podataka. Njen oblik je sličan obliku
funkcije printf, s tim što se umjesto vrijednosti argumenata koje se štampaju navode
adrese promjenljivih u koje upisujemo učitane podatke. Oblik je sljedeći:
scanf("specifikatori_formata", adrese_promjenljivih);
pri čemu važe specifikatori formata dati u tabeli 5. Naredba
1.2 P ROGRAMSKI JEZIK C 35
for(i=1;i<7;i++)
{
if(i==4)
continue;
printf("%d ",i);
}
će štampati 1 2 3 5 6, dok će petlja
for(i=1;i<7;i++)
{
if(i==4)
break;
printf("%d ",i);
}
štampati 1 2 3.
1.2.8 Pokazivači
int x=67,*p1,*p2;
p1=&x;
p2=p1+4;
printf("%d",p2‐p1);
će se odštampati broj 4 na ekranu, što znači da se između p1 i p2 nalaze 4 memorijske
lokacije veličine jednog int podatka.
Pokazivače ne možemo međusobno sabirati, dijeliti i množiti!
Pokazivači su podržani u malom broju programskih jezika, npr. COBOL, BASIC,
Fortran, C, C++, D, dok noviji jezici uglavnom ne podržavaju rad sa pokazivačima.
Sličan konceptu pokazivača je koncept reference, koji se koristi u objektno-
orijentisanom programiranju. Fleksibilnost rada sa pokazivačima i često korišćene
operacije će biti razmatrane u narednom poglavlju.
1.2.9 Nizovi
zapisu se mogu izvršavati operacije kao i sa bilo kojim drugim promjenljivima istog
tipa, tj. operacije tipa:
a[1]+=3;
b=a[2]*a[3];
c=(a[0]>2)?3:1‐a[1];
Indeks niza ne mora biti cjelobrojna konstanta, već to može biti proizvoljan cjelobrojni
izraz koji rezultuje cijelim brojem iz intervala [0, N-1], gdje je N dužina niza. Na
primjer, sasvim je regularno koristiti zapis tipa:
a[b‐c]+=2;
gdje izraz b‐c daje cijeli broj iz pomenutog intervala.
Važno je znati da programski jezik C ne vrši provjeru vrijednosti indeksa što se tiče
pripadnosti datim granicama, već da se može pristupiti i elementu van tog opsega, npr.
a[N+2] ili čak a[‐4]. Pristupanje "elementu niza" van granica niza implicira i
mogućnost promjene vrijednosti date memorijske lokacije, u kojoj može biti smještena
neka druga promjenljiva iz našeg programa, kôd programa ili nešto treće. U nadležnosti
je programera da uzme u obzir ovu osobinu prilikom pisanja programa koji rade sa
nizovima, kako ne bi mijenjao sadržaj onih memorijskih lokacija koje ne želi da mijenja.
Adrese pojedinih elemenata niza se dobijaju primjenom operatora & na te elemente,
tj. &a[0], &a[1] itd. što se može koristiti prilikom učitavanja niza naredbom scanf.
U programskom jeziku C postoji alternativna konvencija za zapis adresa elemenata niza
a. Naime, samo ime niza a ima vrijednost adrese niza, tj. ime niza se može tumačiti kao
pokazivač na niz. Tačnije, a ima vrijednost adrese prvog bajta prvog elementa niza
(strelica na slici 2). Adresa drugog elementa niza se može dobiti sa a+1, trećeg sa a+2
itd. Znači, važi relacija a+i=&a[i]. Ovakav zapis je posljedica činjenice da se elementi
niza ređaju u memoriji jedan za drugim, kao i činjenice da jezik C podržava sabiranje
pokazivača sa cjelobrojnom konstantom. Kako ime niza a ima vrijednost tipa
pokazivača na int, a+1 predstavlja adresu sljedećeg podatka u nizu, koja je za 4 bajta
(pretpostavimo da tip int zauzima 4 bajta) veća od adrese a. Na primjer, ukoliko se
prvi element cijelobrojnog niza a nalazi na lokaciji 1000, onda će drugi element biti na
adresi a+1=1004, treći na adresi a+2=1008 itd. Kada se operator dereferenciranja *
primjeni na vrijednost a+i, dobija se vrijednost i-tog elementa niza, tj. važi relacija
*(a+i)=a[i], ili konkretno *a=a[0], *(a+1)=a[1], *(a+2)=a[2] itd. Uočimo da
smo koristili zagrade za *(a+i), jer bi bez zagrada *a+i zapravo značilo a[0]+i,
zbog većeg prioriteta operacije * u odnosu na +.
Uzimajući u obzir prethodno rečeno, učitavanje elemenata cjelobrojnog niza a,
dužine N, možemo izvršiti na sljedeće načine:
for(i=0;i<N;i++)
scanf("%d",&a[i]);
ili
1.2 P ROGRAMSKI JEZIK C 47
novu tekuću poziciju, ali samo u slučaju da je učitan niz karaktera, odnosno da je size
različito od nule. Na kraju smo dali jednostavnu petlju za prikazivanje sadržaja ovog
niza karaktera, tj. pojedinih stringova unutar njega.
1.2.11 Funkcije
gradivni blok programa, i može se koristiti u razvoju većeg broja programa. Takođe,
srodne funkcije se mogu grupisati u programske biblioteke.
Definicija funkcije ima oblik:
tip_rezultata ime(tip_1 arg_1, ..., tip_n arg_n)
{
tijelo funkcije
}
gdje:
tip_rezultata predstavlja tip podatka koji funkcija vraća kao rezultat
izvršavanja. Ukoliko funkcija ne vraća nikakav podatak, potrebno je deklarisati kao
void ime(tip_1 arg_1, ..., tip_n arg_n);
ime predstavlja ime funkcije, koje mora biti pravilan identifikator (može sadržati
mala i velika slova, cifre, donju crtu, i ne može početi cifrom);
tip_1 arg_1, ... , tip_n arg_n predstavlja listu ulaznih parametara funkcije,
koji se odvajaju zarezima. Funkcija ne mora imati argumente i tada navodimo samo
zagradu, tj. tip_rezultata ime().
Unutar vitičastih zagrada se navodi tijelo funkcije koje se sastoji od deklaracije
promjenljivih i izvršnih naredbi. Dio funkcije prije bloka naredbi, tj.
tip_rezultata ime(tip_1 arg_1, ..., tip_n arg_n)
se naziva zaglavlje funkcije ili potpis funkcije (eng. function signature).
Ilustrujmo način izvršavanja funkcije na skici jednog jednostavnog programa.
int main()
{
/* Naredbe1 */
fun();
/* Naredbe3 */
}
tip_rezultata fun()
{
/* Naredbe2 */
}
Program započinje izvršavanje od funkcije main(). U njoj se izvršavaju
Naredbe1, do poziva funkcije fun(), kada se prelazi na izvršavanje bloka naredbi koji
se nalazi u okviru nje (Naredbe2). Nakon izvršenja funkcije fun, kontrola se vraća
funkciji main(), kada se izvršavaju preostale Naredbe3.
Funkcije mogu da vraćaju vrijednost, koja je onog tipa koji je specificiran tipom
funkcije. Vrijednost koju funkcija vraća prosljeđuje se pozivajućoj funkciji naredbom
return koja ujedno predstavlja tačku izlaska iz funkcije, tj. prekid izvršavanja
1.2 P ROGRAMSKI JEZIK C 53
funkcije. Drugim riječima, ako se u pozvanoj funkciji nalaze naredbe nakon return,
one se neće izvršiti.
Funkcija može imati argumente nad kojima se vrše odgovarajuće operacije. Svaki
od argumenata ima odgovarajući tip, s time što je u C-u funkciji moguće proslijediti
podatak koji ne odgovara tipu argumenta funkcije, kada dolazi do implicitne konverzije
podataka. Ovo bi trebalo izbjegavati i uvoditi cast operator, tj. sprovoditi slaganje
argumenata po tipu, gdje god je to moguće.
Posmatrajmo primjer funkcije koja računa zbir dva cijela broja i taj zbir vraća kao
rezultat:
int zbir1(int x,int y)
{
return x+y;
}
Ova funkcija se poziva (u funkciji main ili nekoj drugoj funkciji) na sljedeći način:
a=zbir1(b,c);
U pozivajućoj funkciji, promjenljive a, b i c se nazivaju stvarni argumenti ili samo
argumenti, dok se u definiciji funkcije zbir1 promjenljive x i y nazivaju formalni
argumenti ili parametri funkcije. Takođe, u pozivajućoj funkciji smo umjesto imena b
i c mogli koristiti x i y, čime dobijamo istoimene stvarne i formalne argumente. Pošto
se pozivajuća i pozvana funkcija nikad ne izvršavaju istovremeno, ne dolazi do konflikta
imena promjenljivih, tako da je ova situacija potpuno regularna. Drugim riječima, ime
x u pozivajućoj i pozvanoj funkciji se ne odnosi na isti entitet u memoriji. Kao
posljedica toga, promjene na formalnim argumentima ne utiču na vrijednost stvarnih
argumenata. Ilustrujmo to na sljedećem programu:
#include<stdio.h>
int main()
{
int a=3,b=4;
int c=fun(a,b);
printf("Zbir (main) je %d, a zbir (fun) je %d",a+b,c);
}
Konačno, važno je zapamtiti da unutar tijela jedne funkcije ne može biti definisana
druga funkcija. Dakle, definicije funkcija čine liniju. Naravno, unutar tijela jedne
funkcije može biti pozvana druga funkcija.
srednjeg takođe manji od traženog elementa. Proceduru nastavljamo na prvoj ili drugoj
polovini niza, koji su pojedinačno manji od čitavog niza (podijeli dio). Kada tekuća
polovina niza ima jedan ili dva elementa, vršimo direktno poređenje tih elemenata sa
traženim (vladaj dio).
Ilustrujmo binarno pretraživanje na primjeru niza
1 3 6 10 15 21 28 36 45
pretpostavljajući da tražimo broj 36. Srednji element ovog niza je 15. Kako je on manji
od traženog broja, pretragu lokalizujemo na podniz ovog niza između elemenata 21 i 45
(druga polovina niza), odnosno zanemarujemo prvu polovinu niza jer je traženi element
sigurno veći od svih elemenata u toj polovini. Srednji element gornje polovine je 28
(može biti i 36, što je stvar unapred dogovorene konvencije). Kako je 28 manje od 36,
pretragu lokalizujemo na podniz ovog niza između elemenata 36 i 45. Pošto ovaj podniz
ima dva elementa, vršimo direktno poređenje tih elemenata sa traženim, i pošto ga
pronalazimo vraćamo indeks 7.
U nastavku je data rekurzivna funkcija binarno koja implementira binarno
pretraživanje niza.
int binarno(int niz[],int start,int end,int x)
{
if(start>end)
return ‐1;
else if(start==end||start==end‐1)
{
if (niz[start]==x||niz[end]==x)
return (niz[start]==x)?start:end;
else
return ‐1;
}
int sred=start+(end‐start)/2;
if (niz[sred]==x)
return sred;
if (niz[sred]>x)
return binarno(niz,start,sred‐1,x);
else
return binarno(niz,sred+1,end,x);
}
Ova funkcija se poziva na sljedeći način:
binarno(niz,0,n‐1,x)
gdje je n dužina niza niz, a x je traženi broj. Čitaocu prepuštamo tumačenje funkcije.
66 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
int main()
{
int n,temp,i,j,a[100];
puts("Unijeti duzinu niza:");
scanf("%d",&n);
puts("Unijeti elemente niza:");
for(i=0;i<n;i++)
scanf("%d",a+i);
for(i=0;i<n‐1;i++)
for(j=i+1;j<n;j++)
if(a[i]>a[j])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
for(i=0;i<n;i++)
printf("%d ",a[i]);
}
Ako posmatramo niz {4 6 1 5 8 2 7}, on će nakon svake zamjene elemenata
izgledati (elementi koji mijenjaju poziciju u narednom koraku su boldovani):
4 6 1 5 8 2 7
1 6 4 5 8 2 7
1 4 6 5 8 2 7
1 2 6 5 8 4 7
1 2 5 6 8 4 7
1 2 4 6 8 5 7
1 2 4 5 8 6 7
1 2 4 5 6 8 7
1 2 4 5 6 7 8
68 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
for(i=0;i<n;i++)
printf("%d ",a[i]);
}
Ukoliko posmatramo niz iz prethodnog primjera, nakon svake zamjene elemenata
u bubble sort algoritmu, niz će izgledati (elementi koji mijenjaju poziciju u narednom
koraku su boldovani):
4 6 1 5 8 2 7
4 1 6 5 8 2 7
4 1 5 6 8 2 7
4 1 5 6 2 8 7
4 1 5 6 2 7 8
1 4 5 6 2 7 8
1 4 5 2 6 7 8
1 4 2 5 6 7 8
1 2 4 5 6 7 8
Odredimo broj operacija poređenja u ova dva algoritma. Za oba algoritma, logika
je sljedeća: u prvom prolasku kroz niz imamo n-1 poređenje, u drugom n-2, ..., u
zadnjem 1 poređenje. Kad se saberu sva ova poređenja, tj. (n-1)+(n-2)+...+2+1, dobija
se ukupno n(n-1)/2 poređenja. Sa stanovišta složenosti, tj. broja operacija poređenja
potrebnih za sortiranje, ova dva algoritma su među najsloženijim. Više riječi o
složenosti algoritama će biti u poglavlju 1.2.13.
Još jednom napominjemo da su ovo dva elementarna algoritma za sortiranje. Pored
njih, postoji vrlo velik broj drugih algoritama, od kojih će neki, kao što su sortiranje sa
umetanjem (eng. insertion sort) i brzo sortiranje (eng. quick sort), biti obrađeni u
pratećoj zbirci.
Nijesu svi algoritmi koji rješavaju neki problem jednako kvalitetni. Jedna od mjera
kvaliteta algoritama, odnosno programa, je složenost. Tipovi složenosti su:
Vremenska složenost Odnosi se na vrijeme potrebno za izvršavanje algoritma.
Proporcionalna je broju operacija (aritmetičkih, logičkih ili nekih drugih) koje
algoritam izvršava. Dakle, procjena vremenske složenosti se svodi na brojanje
operacija u algoritmu.
Prostorna složenost Odnosi se na memorijski prostor koji zauzimaju podaci u
algoritmu i određuje se sabiranjem veličine memorijskih lokacija koje zauzimaju
promjenljive upotrijebljene u algoritmu.
Komunikaciona složenost Odnosi se na potrebnu komunikaciju procesora sa
periferijama, diskovima i memorijom prilikom izvršavanja programa. Ova
složenost je posebno značajna kod paralelnih izvršavanja programa kada razni
procesori međusobno komuniciraju.
1.2 P ROGRAMSKI JEZIK C 69
for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)
{
K operacija
}
}
ima složenost od N2K operacija (broj iteracija unutrašnjeg ciklusa je N2), plus N2+N
inkrementiranja promjenljivih i i j, i N2+2N+1 poređenje.
pN
i 1
i i p1 N1 p2 N 2 ... pK N K ,
p
i 1
i p1 p2 ... pK 1.
1.2.16.1 stdio.h
Tako smo se iz programske biblioteke stdio.h upoznali sa getchar (učitavanje
karaktera sa tastature), gets (učitavanje stringa sa tastature), putchar (štampanje
karaktera na ekranu), puts (štampanje stringa na ekranu), printf (standardni izlaz
podataka), scanf (standardni unos podataka). Upoznali smo se i sa simboličkom
konstantom NULL.
Pre nego krenemo sa opisom drugih funkcija, vratimo se na funkciju scanf i njene
napredne opcije. Funkcija scanf ima zaglavlje:
int scanf(const char *format, ...)
Već smo vidjeli da argument format predstavlja string koji može sadržati karaktere
bez bjelina, bjeline i specifikatore formata ulaznih podataka (%d, %f, %s itd.). Funkcija
vraća cijeli broj koji predstavlja broj uspješno učitanih podataka. U suprotnom, funkcija
vraća negativnu vrijednost ili EOF, u zavisnosti od greške koja se desila. Specifikator
formata ima opšti oblik:
%[*][širina][dužina]tip
Zagrade [] označavaju da je polje opciono (ne mora se navesti). Objašnjenja pojedinih
dijelova su:
* označava da se ulazni podatak učitava, ali se ignoriše, tj. ne dodjeljuje nijednoj
promjenljivoj.
1.2 P ROGRAMSKI JEZIK C 79
scanf("[^\n]",str);
može da sadrži sve karaktere osim karaktera za prelazak u novi red (Enter), što odgovara
ponašanju funkcije gets.
Ilustrujmo sada jednu situaciju koja može izazivati glavobolje. Naime, kada želimo
da učitamo neki podatak (karakter, broj, string), a nakon toga karakter, na sljedeći način:
scanf("%s",str);
scanf("%c",&ch);
nakon unosa prvog podatka (ovdje stringa) i pritiska Enter, u promjenljivu ch će
automatski biti smješten karakter za novi red, tj. korisnik neće imati mogućnost da unese
karakter. Razlog tome je činjenica da funkcija scanf prilikom učitavanja karaktera ne
uklanja bjeline, jer i njih tretira kao karaktere koji se mogu učitati. Ovo je moguće
izbjeći na sljedeći način:
scanf("%[^\n]%*c", str);
scanf("%c", &ch);
Prvi scanf učitava sve do Enter-a, pa zanemaruje Enter (zbog specifikatora %*c), zatim
se karakter ch neometano učitava sa drugim scanf.
Drugi način da se to izbjegne je da se ubaci razmak ispred %c, tj.
scanf(" %c", &ch);
Na ovaj način se ignorišu bjeline ispred karaktera koji se učitava, tj. u našem slučaju se
ignoriše Enter nakon unijetog stringa.
Opišimo sada funkciju sprintf. Ova funkcija štampa tekst u string (prvi argument
funkcije) isto kao što printf štampa tekst na ekranu. Sintaksa je ista kao kod funkcije
printf, sa dodatkom prvog argumenta stringa. Na primjer, naredba:
sprintf(s,"%.2d:%.2d:%.2d",14,7,34);
će formirati string s koji može predstavljati tekuće vrijeme "14:07:34". Funkcija
sprintf omogućava vrlo elegantno formiranje stringa, uz mogućnost formatiranja
teksta kako smo navikli kod funkcije printf. Obratite pažnju na specifikator
konverzije %.2d kojim se cijeli broj ispisuje u dva polja, pri čemu se zbog tačke prazna
polja popunjavaju nulama, što je zgodno kod formiranja tekućeg vremena.
Nasuprot funkcije sprintf imamo funkciju sscanf, koja omogućava učitavanje
podataka iz stringa (prvog argumenta funkcije), a ne preko tastature kako smo navikli
kod scanf. Sintaksa je ista kao kod funkcije scanf, sa dodatkom prvog argumenta
stringa. Na primjer, naredbama:
char s[]="14:07:34";
sscanf(s,"%2d%*c%2d%*c%2d", &sat, &min, &sek)
se iz stringa s, koji predstavlja vrijeme, u cjelobrojne promjenljive sat, min i sek
učitavaju sati, minuti i sekunde, respektivno. Pomoću %*c zanemarujemo dvotačke.
86 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
biblioteka alloc.h, ali pošto to nije standardna C biblioteka (dostupna kod Borland
Turbo C i TIGCC okruženja), treba je izbjegavati radi prenosivosti kôda.
Posmatrajmo slučaj dinamičke alokacije memorije za niz karaktera. Prvo je
potrebno deklarisati pokazivač na char, npr. char *niz. Saznali smo da niz ima 100
karaktera, uključujući terminacioni karakter. Memorija za ovaj niz se može zauzeti sa:
niz=(char *)malloc(100*sizeof(char));
Pojasnimo ovu naredbu. malloc(n) zauzima memorijski blok od n bajta. Kako želimo
zauzeti 100 pozicija za char podatke, iskoristili smo operator sizeof, koji nam vraća
broj bajtova potreban za smještaj jednog podatka tipa char. Funkcija malloc vraća
pokazivač na neodređen podatak (void *), tako da treba izvršiti eksplicitnu konverziju
tih podataka u onaj tip koji će zapravo biti korišćen. U ovom slučaju, to je (char *).
Dakle, rezultat ove operacije je pokazivač na niz od 100 karaktera. U slučaju da malloc
ne zauzme traženu memoriju, vratiće NULL pokazivač kao rezultat. Dobra programerska
praksa podrazumjeva da se poslije svake naredbe za dinamičku alokaciju provjeri da li
je uspješno obavljena poređenjem vraćenog pokazivača sa NULL vrijednošću. U
zavisnosti od značaja datog podatka, treba preduzeti dalje mjere (npr. izaći iz programa).
Nakon uspješne alokacije memorije za podatke, može se pristupati elementima niza
na bilo koji uobičajeni način (niz[4], *(niz+4)) i sa njima vršiti sve dozvoljene
operacije.
Prednost dinamičke alokacije u odnosu na nizove promjenljive dužine je da se
dinamički zauzeta memorija može osloboditi. To se naziva dinamičkom dealokacijom,
i vrši se funkcijom free koja za argument ima pokazivač na dinamički alociranu
memoriju. U našem primjeru, to se radi sa:
free(niz);
Pored funkcije malloc, za dinamičko zauzimanje memorije može poslužiti
funkcija calloc u obliku:
calloc(n,t);
kojom se zauzima n lokacija u memoriji, veličine t, i u te se lokacije upisuje 0. I ova
funkcija vraća podatak tipa void *, koji je potrebno eksplicitino konvertovati u željeni
pokazivač. Takođe, rezultat je NULL u slučaju neuspješne alokacije memorije.
Funkcija realloc, koja se poziva na sljedeći način:
realloc(niz,n);
realocira (smanjuje ili povećava) zauzetu memorija za podatak niz. Argument n je
veličina memorije koja se želi zauzeti. Funkcija vraća rezultat tipa void *.
Stari programski jezici (FORTRAN, COBOL, BASIC) vrše kontrolu memorije
statički i nemaju opisane funkcije.
88 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
podacima fajla, fajl se mora otvoriti. To se vrši funkcijom fopen koja vraća pokazivač
na otvoreni fajl i pridružuje ga deklarisanom pokazivaču, na sljedeći način:
FILE *a;
a=fopen(fajl_koji_se_otvara, način_pristupa);
fajl_koji_se_otvara je naziv fajla sa kojim se želi raditi. Nazivi fajlova se navode
u formi stringa. Na primjer, "Prvi.txt" označava da se želi pristupiti fajlu Prvi.txt u
folderu koji je za C kompajler tekući. Ako se želi pristupiti fajlu koji nije u tekućem
folderu, potrebno je navesti kompletnu putanju do njega: "C:\\Temp\\Drugi.txt".
Uočite \\ unutar stringa. Znate li čemu služe? Način pristupa je takođe string, koji može
sadržati sljedeće karaktere:
r otvaranje fajla za čitanje,
w otvaranje fajla za upis,
a dopisivanje na kraj datoteke,
+ omogućava i upis i čitanje,
b otvaranje datoteke u binarnom modu,
t otvaranje datoteke u tekstualnom modu (podrazumijevano).
U tabeli 7 su date moguće vrijednosti stringa koji predstavlja način pristupa, kao i
objašnjenje šta će se desiti ukoliko fajl već postoji ili ne postoji.
Način
Značenje Ako fajl postoji/ne postoji
pristupa
r Čitanje Ako fajl ne postoji, fopen vraća NULL.
rb Čitanje u binarnom modu Ako fajl ne postoji, fopen vraća NULL.
Ako fajl postoji, njegov sadržaj će biti prepisan.
w Upis
Ako fajl ne postoji, biće kreiran.
Ako fajl postoji, njegov sadržaj će biti prepisan.
wb Upis u binarnom modu
Ako fajl ne postoji, biće kreiran.
a Dopisivanje na kraj fajla Ako fajl ne postoji, biće kreiran.
Dopisivanje na kraj fajla u
ab Ako fajl ne postoji, biće kreiran.
binarnom modu
r+ Čitanje i upis Ako fajl ne postoji, fopen vraća NULL.
Čitanje i upis u binarnom
rb+ Ako fajl ne postoji, fopen vraća NULL.
modu
Ako fajl postoji, njegov sadržaj će biti prepisan.
w+ Čitanje i upis
Ako fajl ne postoji, biće kreiran.
Čitanje i upis u binarnom Ako fajl postoji, njegov sadržaj će biti prepisan.
wb+
modu Ako fajl ne postoji, biće kreiran.
a+ Čitanje i dopisivanje Ako fajl ne postoji, biće kreiran.
Čitanje i dopisivanje u
ab+ Ako fajl ne postoji, biće kreiran.
binarnom modu
Tabela 7. Načini pristupa fajlu i odgovarajuća značenja.
1.2 P ROGRAMSKI JEZIK C 97
različitu od nule (logičku istinu) ako se došlo do kraja fajla i nulu (logičku neistinu) ako
nije.
Kada se završi rad sa nekim fajlom potrebno ga je zatvoriti. To se obavlja funkcijom
fclose kojoj se prosljeđuje pokazivač na fajl, tj. naredbom:
fclose(a);
U nekim implementacijama, možete koristiti funkciju fcloseall() ili
_fcloseall() kojom se zatvaraju svi eventualno otvoreni fajlovi.
1.2.23.4 Redirekcija
Pored opisanih operacija, za rad sa fajlovima mogu da posluže i funkcije
operativnog sistema. Jedna od njih je redirekcija (preusmjeravanje). Redirekcija
omogućava da podatke učitavamo u program iz tekstualnog fajla ili da podatke iz
programa štampamo u tekstualni fajl, ali pritom ne koristeći ranije opisane funkcije za
čitanje i upis u fajl, već automatski, korišćenjem ove funkcionalnosti operativnog
sistema. Objasnimo redirekciju koristeći komandnu liniju Windows operativnog
sistema (ranije MS-DOS operativni sistem). Kreirajmo sljedeći program:
#include<stdio.h>
int main()
{
int a,b,c;
scanf("%d%d",&a,&b);
c=a+b;
100 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
printf("%d = %d + %d",c,a,b);
}
Pod pretpostavkom da je program preveden na mašinski jezik i da je naziv izvršne
verzije test.exe, otkucajmo
test
u komandnoj liniji i unesimo dva cijela broja (recimo, 5 i 9). Dobijamo odgovarajući
rezultat (recimo, 14 = 5 + 9). Na ovaj način smo do sada radili, tj. program pauzira
izvršavanje (funkcija scanf) dok korisnik ne unese potrebne podatke i pritisne Enter.
Kreirajmo sad fajl ulaz.txt pomoću raspoloživog editora, npr. Notepad-a, u koji
unesemo dva cijela broja, razdvojena bjelinom. Zadavanjem naredbe u komandnoj liniji:
test < ulaz.txt
sadržaj fajla ulaz.txt proslijeđujemo programu test umjesto standardnog ulaza,
tako da se ne čeka korisnikov unos, već se nakon ove naredbe odmah štampa
odgovarajući izlaz.
Slično, efekat naredbe:
test > izlaz.txt
je štampanje rezultata programa (sad opet korisnik treba da unese dva cijela broja) u
automatski kreirani fajl izlaz.txt, a ne u komandnoj liniji.
Konačno, ove dvije naredbe se mogu kombinovati na sljedeći način:
test < ulaz.txt > izlaz.txt
čime se ulaz čita iz fajla ulaz.txt, a izlaz štampa u fajl izlaz.txt.
Još jednom napominjemo da je redirekcija funkcija operativnog sistema, a ne
programskog jezika C, ali da se može koristiti na prikladan način u programiranju.
I veoma stari programski jezici kakvi su FORTRAN i BASIC imali su
implementiranu mogućnost rada sa fajlovima (bazama podataka). Posebno je
programski jezik COBOL specijalizovan za ovu namjenu. Programski jezik C i drugi
srodni jezici su nadogradili ove mogućnosti starijih programskih jezika najčešće
sofisticiranijim funkcijama. Postoje posebni programski paketi namjenjeni prevashodno
radu sa fajlovima kao podacima u cilju obrade podataka. Poznati jezik za podršku radu
sa bazama podataka je SQL.
1.3.1 Liste
Linkovana lista (eng. linked list), ili samo lista, je napredan tip podatka koji
predstavlja niz međusobno povezanih objekata. Lista nije standardan niz, gdje su
elementi smješteni jedan za drugim u memoriji, već kod liste svaki element, još se
naziva i čvor (eng. node), predstavlja zaseban objekat u memoriji, nezavisan od pozicije
ostalih elemenata. Povezivanje čvorova liste, radi kretanja kroz nju i vršenja operacija
kao što su ubacivanje novih i brisanje postojećih elemenata, se vrši pomoću pokazivača.
Lista, kao tip podatka, postoji implementirana u nekim objektno-orjentisanim
programskim jezicima, kakvi su Java i VB.NET. U programskom jeziku C, lista ne
postoji kao tip podatka, već se čvorovi liste implementiraju preko struktura, a povezuju
koristeći pokazivače.
Već smo rekli da struktura može da sadrži pokazivač na drugu strukturu istog tipa.
Ovo se može iskoristiti kao osnova za kreiranje listi, u smislu da svaki element liste
može da pamti sljedeći element u listi, što je vrlo korisno u mnogim aplikacijama.
Posmatrajmo jedan jednostavan primjer kao motivaciju za uvođenje listi. Pretpostavimo
da imamo niz od N brojeva, koji zauzima N susjednih pozicija u memoriji. Vršimo
njegovo sortiranje (bilo kojom metodom) i dobijemo sortirani niz brojeva. Šta se dešava
ako dobijemo za obradu novih K brojeva? Da li se mora ponovo implementirati
operacija sortiranja nad svim brojevima? Ako se ovaj niz predstavi preko liste, to se ne
mora dogoditi. U nastavku ćemo objasniti kako.
Objasnimo prvo kako bismo mogli riješiti problem sa sortiranjem niza u slučaju
ubacivanja novog elementa. Pretpostavimo da imamo niz brojeva 5 3 1 6 8 9, i pored
tog niza posmatrajmo još jedan niz, nazovimo ga pozicije, čiji elementi predstavljaju
poziciju sljedećeg člana u sortiranom nizu (posmatraćemo prirodne pozicije 1, 2, 3, ...,
a ne pozicije elemenata u C nizu 0, 1, 2, ...). Ova dva niza sada možemo predstaviti kao:
niz 5 3 1 6 8 9
pozicije 4 1 2 5 6 0
Najmanji element u nizu je 1, nakon njega dolazi element na poziciji 2 (to je 3), nakon
3 dolazi element na poziciji 1 (to je 5) itd. Dakle, počevši od najmanjeg elementa,
krećemo se kroz niz prateći odgovarajuće vrijednosti u nizu pozicije. Pošto nakon
najvećeg elementa više nema elemenata, odgovarajući element niza pozicije je 0. Ako
sada želimo da dodamo broj 4 u niz, dodaćemo ga na kraj niza, tj. na sedmu poziciju. U
sortiranom poretku, on bi trebao da se nađe između elemenata 3 i 5. Tako bi za element
niza 3 sljedeći element bio novododati na poziciji 7, dok odgovarajući element niza
pozicije za novododati element 4 treba da bude 1. Na ovaj način se raskida veza
108 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
a nakon toga se pokazivač postavi na adresu sljedećeg člana u listi. Pošto je ovo jedini
član liste, tj. sljedeći element ne postoji, uradićemo sljedeće:
s‐>sljed=NULL;
Na ovaj način je kreirana lista od jednog člana.
Ako se dodaje novi čvor u listu, prvo treba alocirati memoriju za njega (opet, uz
odgovarajuću provjeru):
p=(struct st *)malloc(sizeof(struct st));
Postavi se vrijednost ovog čvora (npr. p‐>i=1), postavi se da prethodni čvor pokazuje
na ovaj:
s‐>sljed=p;
Na ovaj način se povezuju čvorovi liste. Pošto je novododati čvor istovremeno i zadnji
čvor u listi, potrebno je uraditi sljedeće:
p‐>sljed=NULL;
Posljednji čvor liste pokazuje na NULL.
Procedura se ponavlja za svaki naredni čvor liste i ilustrovana je na slici 7:
Prvi čvor liste se još naziva i glava liste, dok se zadnji naziva rep liste.
se može brisati glava ili bilo koji drugi element liste. Funkcije dodajCvor i
obrisiCvor vraćaju glavu na izmjenjenu listu, a ta glava se može razlikovati do
originalne glave. Posljednja operacija koju vršimo nad listom je brisanje čitave liste
funkcijom obrisiListu. Nakon svake operacije nad listom, vrši se štampanje liste
void funkcijom stampajListu.
#include<stdio.h>
#include<stdlib.h>
struct lista{
int i;
struct lista *sljed;
};
int main()
{
struct lista *glava,*q,*t;
int a[]={1,3,6,10,15},k=0,n=sizeof(a)/sizeof(int),x;
// Kreiramo glavu liste
glava=(struct lista *)malloc(sizeof(struct lista));
if(glava==NULL) exit(1);
glava‐>sljed=NULL;
glava‐>i=a[k++];
// Kreiramo ostatak liste
t=glava;
while(k<n)
{
q=(struct lista *)malloc(sizeof(struct lista));
if(q==NULL) exit(1);
q‐>sljed=NULL;
q‐>i=a[k++];
t‐>sljed=q;
t=q;
}
stampajListu(glava);
{
struct lista *pom=glava‐>sljed;
glava‐>sljed=glava‐>sljed‐>sljed;
free(pom);
}
return novaGlava;
}
Lista je: 1 3 6 10 15
Unijeti broj koji se dodaje u listu: 8
Lista je: 1 3 6 8 10 15
1.3 L INKOVANI TIPOVI PODATAKA 121
cn[i][j]=min(cs[i][j],cs[i][k]+cs[k][j]);
d(i) d(j)
cs[i][j]=cn[i][j];
}
d(i){
d(j)
printf("%d",cn[i][j]);
printf("\n");
}
}
I pored značaja pojma grafa, mi se nećemo duže na njemu zadržavati. Preći ćemo
na razmatranje tipa podataka koji se naziva stablo.
1.3.3 Stabla
Stablo ili drvo (eng. tree) je specijalni tip grafa, koje svojim oblikom i osobinama
donekle podseća na porodično stablo. U stablu svaki čvor ima tačno jedan čvor koji na
njega ukazuje. Svaki čvor može imati potomke, tj. čvorove koji potiču od datog čvora.
Za čvor kažemo da je predak svim čvorovima koji potiču od njega. Izvorišni čvor je
onaj na koji ne ukazuje nijedan drugi čvor i naziva se korijenom (eng. root). Čvorovi
koji nemaju potomke nazivaju se listovima.
Dajmo sada preciznija objašnjenja pojmova vezanih za stablo.
Usmjereni graf u kome nema ciklusa zove se usmjereni aciklični graf. Stablo je
usmjereni aciklični graf koji zadovoljava sljedeća tri svojstva:
Postoji tačno jedan čvor, korijen, koji nije kraj nijedne ivice (tj. korijen nema
pretke).
Svakom čvoru, izuzev korijena, odgovara tačno jedna ivica čiji je kraj taj čvor (tj.
svaki čvor osim korijena ima tačno jedan čvor koji na njega ukazuje).
Postoji put (lako se dokazuje da je jedinstven) od korijena ka svakom čvoru.
Neka je T=(V,E) stablo. Ako (v,w) pripada E, onda se kaže da je v otac čvora w i da
je w sin čvora v. Ako postoji put od v ka w, kaže se da je v predak čvora w i da je w
potomak čvora v. Ako je osim toga vw, onda je v pravi predak čvora w, a w je pravi
potomak čvora v. Čvorovi koji nemaju pravih potomaka nazivaju se listovima. Čvor v
zajedno sa svojim potomcima naziva se podstablo od T, a sam čvor v je korijen tog
podstabla.
Visina čvora v u stablu jeste dužina najdužeg puta od v ka nekom listu. Visina stabla
jeste visina korijena.
Binarno stablo je stablo u kome važi:
Svaki sin nekog čvora je označen ili kao lijevi sin ili kao desni sin tog čvora.
122 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
Nijedan čvor nema više od jednog lijevog sina, niti više od jednog desnog sina.
Na slici 18 je dat grafički prikaz jednog binarnog stabla.
Podstablo Tl (ako ono postoji) čiji korijen je lijevi sin čvora v zove se lijevo
podstablo od v. Slično, podstablo Tr (ako ono postoji) čiji je korijen desni sin od v zove
se desno podstablo od v. Za svaki čvor iz Tl kažemo da se nalazi lijevo, tj. prije bilo kog
čvora iz Tr. Obično se binarno stablo predstavlja pomoću dva podniza LEFTSON i
RIGHTSON. Neka su vrhovi binarnog stabla označeni brojevima od 1 do n. Tada je
LEFTSON[i]=j akko je j lijevi sin od i. Ako čvor i nema lijevog sina, tada je
LEFTSON[i]=0. Slično se definiše i RIGHTSON. Na slici 19 je prikazano jedno
binarno stablo i njegova reprezentacija pomoću ovih nizova.
Slika 19. Jedno binarno stablo i njegova reprezentacija pomoću nizova LEFTSON i
RIGHTSON.
Potpuno binarno stablo je ono kod kojeg svaki čvor, osim listova, ima oba
potomka. Svi listovi potpunog binarnog stabla se nalaze na istoj udaljenosti (visini) od
korjena stabla. Na slici 20 je prikazano jedno potpuno binarno stablo visine 3.
124 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
Slika 22. Inorder, preorder i postorder obilasci binarnog stabla koji daju riječ KOMPAJLER.
Primjer: Zadato je binarno stablo i treba mu izračunati visinu. Neka je stablo definisano
preko LEFTSON i RIGHTSON nizova. Učitava se n čvorova. Korijenu odgovara indeks
1. Za ovo postoji efikasan rekurzivni algoritam koji se zasniva na sljedećim pravilima:
Ako nema ni lijevog ni desnog podstabla, tj. ako je r=0 i l=0, onda je visina 0.
Ako postoji i lijevo i desno podstablo, tj. ako je r0 i l0, onda se odredi visina
lijevog i desnog podstabla, pa se uzima veći od ta dva broja uvećan za 1.
Ako je r0 i l=0 onda se uzima visina desne strane uvećana za 1.
Ako je r=0 i l0 onda se uzima visina lijeve strane uvećana za 1.
Programska realizacija je data narednim kodom:
#include<stdio.h>
int n,le[100],ri[100];
int max(int,int);
int visina(int i)
{
int l,r;
l=le[i];
r=ri[i];
128 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
Zadatak 1. Napisati program koji simulira rad jednostavnog kalkulatora, koji će za dva unijeta
broja i jedan od osnovnih aritmetičkih operatora dati vrijednost izvršene operacije.
#include<stdio.h>
int main()
{
char oper;
float a,b;
printf("Unesite operator i dva broja: ");
scanf("%c%f%f",&oper,&a,&b);
switch(oper)
{
case '+':
printf("%f\n",a+b);
break;
case '‐':
printf("%f\n",a‐b);
break;
case '*':
printf("%f\n",a*b);
break;
case '/':
if (b!=0)
printf("%f\n",a/b);
else
puts("Dijeljenje nulom!");
break;
default:
puts("Ne moze taj operator");
}
}
razdvajamo bjelinama, dok se nakon posljednjeg unosa (u našem slučaju, drugog operanda) mora
pritisnuti Enter.
Izvršenje programa:
Unesite operator i dva broja: * 2 3
6.000000
Unesite operator i dva broja: %
4.5
7.8
Ne moze taj operator
int main()
{
float x=0.0,a,b,p,t;
printf("Unesite brojeve a i b\n");
scanf("%f%f",&a,&b);
t=b;
do
{
printf("%f \t %f\n",x,t);
p=t;
x+=0.1;
t=a*sin(x)+b*cos(x);
} while(t*p>0);
}
0.3 ‐4
0.000000 ‐4.000000
0.100000 ‐3.950067
0.200000 ‐3.860666
0.300000 ‐3.732690
0.400000 ‐3.567419
0.500000 ‐3.366503
0.600000 ‐3.131950
0.700000 ‐2.866103
0.800000 ‐2.571620
0.900000 ‐2.251441
1.000000 ‐1.908767
1.100000 ‐1.547022
1.200000 ‐1.169819
1.300000 ‐0.780927
1.400000 ‐0.384233
Zadatak 3. Napisati program koji za unijeti prirodni broj računa sumu i proizvod svih prirodnih
brojeva koji su manji od ili jednaki tom broju.
#include<stdio.h>
int main()
{
int a,i=1,sum=0;
long int prod=1;
puts("Unesi neki prirodni broj");
scanf("%d",&a);
while(i<=a)
{
sum+=i;
prod*=i;
i++;
}
printf("Suma prirodnih brojeva do broja %d je: %d\n",a,sum);
printf("Proizvod prirodnih brojeva do broja %d je: %ld\n",a,prod);
}
Izvršavanje programa:
Unesi neki prirodni broj
6
Suma prirodnih brojeva do broja 6 je: 21
Proizvod prirodnih brojeva do broja 6 je: 720
Zadatak 4. Napisati program koji provjerava da li je učitani prirodan broj N Hamming-ov broj.
Prirodan broj je Hamming-ov ukoliko su mu jedini prosti činioci 2, 3 ili 5.
#include<stdio.h>
int main()
{
int N;
printf("Unijeti prirodan broj: ");
scanf("%d",&N);
while(N%2==0)
N/=2;
while(N%3==0)
N/=3;
while(N%5==0)
N/=5;
if(N==1)
printf("Broj je Hamming‐ov");
else
printf("Broj nije Hamming‐ov");
}
Zadatak 5. Napisati program kojim se učitavaju prirodan broj k (stepen polinoma) i realni
brojevi a[0], a[1],…, a[k] (koeficijenti polinoma), čime se zadaje polinom
p(x)=a[0]+a[1]x+...+a[k]xk. Program treba da izračuna i odštampa vrijednost
polinoma p(x), za unijetu vrijednost realnog argumenta x.
#include<stdio.h>
132 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
int main()
{
int k,i;
float p=0,a,x,proiz=1;
printf("Unesite red polinoma: ");
scanf("%d",&k);
printf("Unesite vrijednost za x: ");
scanf("%f",&x);
printf("Unesite koeficijente polinoma ");
for(i=0;i<=k;i++)
{
scanf("%f",&a);
p+=a*proiz;
proiz*=x;
}
printf("Vrijednost polinoma za dato x je %f\n",p);
}
int main()
{
char a;
2.1 K ONTROLA TOKA PROGRAMA 133
int i,j,dim;
printf("Unesi karakter i dimenzije kvadrata: ");
scanf("%c%d",&a,&dim);
for(i=1;i<=dim;i++)
{
if(i==1||i==dim)
{
for(j=1;j<=dim;j++)
printf("%c",a);
}
else
{
printf("%c",a);
for(j=2;j<dim;j++)
printf(" ");
printf("%c",a);
}
printf("\n");
}
}
6
Priblizna vrijednost korijena je 2.44949
Zadatak 9. Napisati program koji pronalazi nulu funkcije y = x - cos(x), u intervalu x[0,/2)
iterativnim postupkom, koji se može opisati na sljedeći način:
Data je jednačina f(x) = 0, gde je f:[a,b]R, i x = g(x) je njen ekvivalentni oblik.
Ukoliko g:[a,b]R i g(x) ima izvod u svakoj tački x[a,b] takav da je |g'(x)|<1,
tada f(x) = 0 ima jedinstveno rješenje na intervalu [a,b] koje se može odrediti
iterativnom procedurom: xk+1 = g(xk), k = 0, 1, 2, ..., sa proizvoljnim x0[a,b].
#include<stdio.h>
#include<math.h>
int main()
{
float x0=1,x1,x2;
x2=cos(x0);
do{
x1=x2;
x2=cos(x1);
} while(fabs(x1‐x2)>=0.0001);
printf("Trazena nula je: %f\n",x2);
}
Zadatak 10. Napisati program koji računa najveći zajednički djelilac (NZD) brojeva a i b
pomoću Euklidovog algoritma:
Ako je a=b, tada je NZD=a i to je kraj algoritma.
U veći broj upišemo razliku većeg i manjeg broja i vraćamo se na prvi korak.
#include<stdio.h>
int main()
{
138 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
h=(b‐a)/N;
I2=I1;
I1=0;
for(i=0;i<=N;i++)
{
x=a+i*h;
fun=sin(2*x*x‐3);
I1+=((i==0)||(i==N))?fun:2*fun;
}
I1*=0.5*h;
} while(fabs(I2‐I1)>eps);
printf("Priblizna vrijednost integrala je: %f\n",I1);
}
Zadatak 12. Napisati program koji za unijeti prirodni broj računa i štampa zbir njegovih cifara.
#include<stdio.h>
int main()
{
int s=0,N;
printf("Unesi prirodan broj\n");
scanf("%d",&N);
while(N!=0)
{
s+=N%10;
N/=10;
}
printf("Suma cifara prirodnog broja je: %d\n",s>0?s:‐s);
2.1 K ONTROLA TOKA PROGRAMA 139
Zadatak 13. Učitavaju se brojevi a, b i c, koji definišu datum, a štampa se sutrašnji datum.
Recimo, ako je (a, b, c) = (31, 1, 1992), treba štampati 1.2.1992.
#include<stdio.h>
int main()
{
int a,b,c,an,bn,cn,prest;
scanf("%d%d%d",&a,&b,&c);
prest=!(c%4)&&(!(c%400)||c%100);
if((a==31)&&(b==12))
{
an=bn=1;
cn=c+1;
}
else if(a==31||(a==30&&(b==4||b==6||b==9||b==11))||(a==29&&b==2)||
(a==28&&b==2&&!prest))
{
an=1;
bn=b+1;
cn=c;
}
else
{
140 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
an=a+1;
bn=b;
cn=c;
}
printf("%d.%d.%d\n",an,bn,cn);
}
28 2 2000
29 2 2000
31 12 2003
1 1 2004
35 14 2003
36 14 2003
2.2 N IZOVI 141
2.2 Nizovi
Zadatak 1. Napisati program kojim se unosi niz cijelih brojeva, od maksimalno 20 elemenata,
i računa i štampa njegov maksimalni i minimalni element.
#include<stdio.h>
int main()
{
int a[20],min,max,i,N;
printf("Unesi broj clanova niza\n");
scanf("%d",&N);
printf("Unesi clanove niza\n");
for(i=0;i<N;i++)
scanf("%d",a+i);
max=min=a[0];
for(i=1;i<N;i++)
if(a[i]>max)
max=a[i];
else if(a[i]<min)
min=a[i];
printf("Minimum niza je: %d\nMaksimum niza je: %d ",min,max);
}
6
Unesi clanove niza
5 2 3 ‐1 11 9
Minimum niza je: ‐1
Maksimum niza je: 11
Zadatak 2. Napisati program koji računa srednju vrijednost niza cijelih brojeva.
#include<stdio.h>
int main()
{
int i,N,x[50];
float mean=0.0;
printf("Unesi broj clanova niza\n");
scanf("%d",&N);
printf("Unesi clanove niza\n");
for(i=0;i<N;i++)
scanf("%d",x+i);
for(i=0;i<N;i++)
mean+=(float)x[i]/N;
printf("Srednja vrijednost niza je: \t %f \n",mean);
}
Zadatak 3. Napisati program kojim se učitavaju prirodan broj k (stepen polinoma) i niz realnih
brojeva a[0], a[1],…, a[k] (koeficijenti polinoma), čime se zadaje polinom
p(x)=a[0]+a[1]x+...+a[k]xk. Program treba da izračuna i odštampa vrijednost
polinoma p(x), za unijetu vrijednost realnog argumenta x.
144 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
for(i=0;i<=k;i++)
scanf("%f",a+i);
for(i=0;i<=l;i++)
scanf("%f",b+i);
for(i=0;i<=k+l;i++)
c[i]=0.0;
for(i=0;i<=k;i++)
for(j=0;j<=l;j++)
c[i+j]+=a[i]*b[j];
for(i=0;i<=k+l;i++)
printf("%f\n",c[i]);
}
c m a i b j pričemu je m i j .
i, j
Predmetni program zapravo određuje datu sumu. Nakon učitavanja polinoma a i b, vrši se
inicijalizacija polinoma c (najveći red ovog polinoma je k+l). Zatim se prosto primjeni opisana
operacija. Na kraju se vrši štampanje koeficijenata dobijenog polinoma.
Izvršenje programa:
Unesi red polinoma
2 3
1 2 3
3 2 1 4
3.000000
8.000000
14.000000
12.000000
11.000000
12.000000
Zadatak 5. Napisati program koji formira podniz niza cijelih brojeva koji se sastoji od onih
elemenata koji su veći od zadatog cijelog broja.
#include<stdio.h>
#define MAX 20
int main()
{
int N,k=0,i,a[MAX],b[MAX],C;
2.2 N IZOVI 145
1 2 3 4
11 12 13 14
A
21 22 23 24
31 32 33 34
redosljed štampanja je 1, 11, 21, 31, 32, 22, 12, 2, 3, 13, 23, 33, 34, 24, 14, 4.
#include<stdio.h>
#define MAX 20
int main()
{
int A[MAX][MAX],vektor[MAX*MAX];
int i,j,N,d;
printf("Unijeti dimenzije kvadratne matrice N: ");
scanf("%d",&N);
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
printf("Unesi A[%d][%d]: ",i,j);
scanf("%d",&A[i][j]);
}
printf("\n");
}
printf("Elementi matrice na glavnoj dijagonali su:\n");
for(i=0;i<N;i++)
printf("%d\t",A[i][i]);
printf("\nUnijeti dijagonalu: 0 glavna, 1 prva sporedna gore,\n");
printf("1 prva sporedna ispod glavne itd.\n");
scanf("%d",&d);
if(d<=N‐1&&d>=1‐N)
{
if(d>=0)
for(i=0,j=d;i<N&&j<N;i++,j++)
printf("A[%d][%d]=%d ",i,j,A[i][j]);
else
2.3 M ATRICE 151
for(i=‐d,j=0;i<N&&j<N;i++,j++)
printf("A[%d][%d]=%d ",i,j,A[i][j]);
}
else
printf("Nema te dijagonale.");
printf("\n");
for(i=0;i<N;i++)
for(j=0;j<N;j++)
{
vektor[i*N+j]=A[i][j];
printf("vektor[%d]=%d \n",i*N+j,vektor[i*N+j]);
}
for(j=0;j<N;j++)
if(j%2==0)
for(i=0;i<N;i++)
printf("A[%d][%d]=%d\n",i,j,A[i][j]);
else
for(i=N‐1;i>=0;i‐‐)
printf("A[%d][%d]=%d\n",i,j,A[i][j]);
}
MTP=‐TP[i][j];
}
for(i=0;i<N;i++)
for(j=0;j<N;j++)
TM[i][j]+=TP[i][j];
p=‐p;
n=n+1;
} while(MTP>0.01*MA);
printf("Matricni sinus stampan red po red je:\n");
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
printf("%f\t",TM[i][j]);
printf("\n");
}
}
2.4 Stringovi
Zadatak 1. Učitati string koji sadrži manje od 50 karaktera i sva mala slova u tom stringu
konvertovati u velika, a ostale karaktere ne mijenjati.
#include<stdio.h>
int main()
{
char niz[50];
int i=0;
puts("Unesi neku rijec");
gets(niz);
while(niz[i]!='\0')
{
if (niz[i]>='a'&&niz[i]<='z')
niz[i]+='A'‐'a';
i++;
}
printf("Izmijenjeni string je: %s",niz);
}
Zadatak 2. Učitati evidenciju o nekoliko lica (ime, dužine manje od 15 karaktera, i ulog), a
zatim štampati imena svih onih koji imaju ulog veći od prosječnog.
#include<stdio.h>
int main()
{
char imena[20][15];
float ulog[20],prosjek=0.0;
int i,a;
printf("Ucitaj broj imena: ");
scanf("%d",&a);
printf("Ucitaj imena lica i njihov ulog: ");
for(i=0;i<a;i++)
{
scanf("%s%f",imena[i],&ulog[i]);
prosjek+=ulog[i];
}
prosjek/=a;
puts("Ulog veci od prosjeka imaju:");
for(i=0;i<a;i++)
if(ulog[i]>prosjek)
printf("%s\t\t%6.2f\n",imena[i],ulog[i]);
}
Vesna 99.50
Ana 112.40
Zadatak 3. Učitava se nekoliko riječi, pri čemu je dužina riječi manja od 11 karaktera, a zatim
se štampa koliko ima tu različitih riječi.
#include<stdio.h>
#include<string.h>
int main()
{
char rijeci[20][11];
int i,j,brRijeci,razl=0,uslov;
printf("Koliko rijeci zelite da unesete?\n");
scanf("%d",&brRijeci);
for(i=0;i<brRijeci;i++)
scanf("%s",rijeci[i]);
for(i=0;i<brRijeci‐1;i++)
{
uslov=1;
for(j=i+1;j<brRijeci;j++)
if(strcmp(rijeci[i],rijeci[j])==0)
uslov=0;
razl+=uslov;
}
printf("Ima %d razlicitih rijeci",razl);
}
Boris
Jano
Marko
Ima 5 razlicitih rijeci
Zadatak 4. Učitava se nekoliko redova teksta i broji se i štampa koliko se puta pojavljuje koje
slovo. Ostali karakteri se ne broje. Unos teksta se prekida kad se unese prazan red.
Napisati odgovarajući program.
#include<stdio.h>
int main()
{
char redovi[50][80],maloSlovo;
int brojRed=0,i,j,slova[26]={0};
printf("Unesi redove\n");
gets(redovi[brojRed]);
while(strlen(redovi[brojRed])!=0)
gets(redovi[++brojRed]);
for(i=0;i<brojRed;i++)
{
j=0;
while(redovi[i][j]!='\0')
{
if(isalpha(redovi[i][j]))
{
maloSlovo=tolower(redovi[i][j]);
slova[maloSlovo‐'a']++;
}
j++;
}
}
for(i=0;i<26;i++)
if(slova[i]>0)
printf("slova %c ima %d\n", 'a'+i,slova[i]);
}
više riječi kasnije). Napomenimo da ako se vrši alociranje prostora za jedan red teksta to se
obično vrši limitirano na 80 karaktera, kolika je dužina ekrana u komandnoj liniji. Kako redove
unosimo sve dok se ne unese prazan red, za to će nam poslužiti while petlja koja se izvršava
sve dok je dužina unijetog stringa veća od 0. Učitane stringove smještamo u matricu redovi.
Slova engleske abecede (mi nećemo praviti razliku između malih i velikih slova) ima 26, pa smo
za niz u kome čuvamo podatke o tim promjenljivim alocirali 26 mjesta i sve članove niza
inicijalizirali na nulu. Zatim se za svaki red teksta ponavlja procedura opisana drugom while
petljom. Prolazi se kroz karakter po karakter teksta i provjerava da li je dati karakter slovo
korišćenjem funkcije isalpha (biblioteka ctype). Ovu provjeru smo mogli izvršiti i direktno,
znajući činjenicu da su slova po ASCII kodu poređana jedna za drugim od malog a do malog z
i u odvojenom nizu od velikog A do velikog Z. Ovaj način ostavljamo vama za vježbu. Pošto
želimo da slova[0] odgovara karakteru a, slova[1] odgovara karakteru b itd. koristimo
kompaktan zapis slova[maloSlovo‐'a'], gde je maloSlovo tekuće slovo, prethodno
pretvoreno u malo slovo ako je bilo veliko. Pretvaranje velikih slova u mala je izvršeno
funkcijom tolower (biblioteka ctype). Uočimo da nijesmo provjeravali karaktere do kraja
reda, već do terminacionog karaktera, jer nijesmo vršili upisivanje u dio memorije nakon
terminacionog karaktera. Na kraju programa, štampamo samo one karaktere koji je pojavljuju
bar jednom.
Izvršenje programa:
Unesi redove
Tako nam i treba.
Dok nismo priznavali da ima problema, nismo ih morali rjesavati.
Vodite racuna o svom prezimenu.
Ono ce ziveti duze od vas i odgovarati za sve gluposti i greske.
slova a ima 17
slova b ima 2
slova c ima 2
slova d ima 6
slova e ima 12
slova g ima 3
slova h ima 1
slova i ima 17
slova j ima 1
slova k ima 3
slova l ima 4
slova m ima 8
slova n ima 7
slova o ima 15
slova p ima 4
slova r ima 9
slova s ima 8
slova t ima 7
slova u ima 4
slova v ima 8
slova z ima 5
168 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
Zadatak 8. Sastaviti program koji učitava prirodan broj N i štampa ga u rimskom obliku.
Recimo, učita se 149, a štampa se CXLIX.
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i=0,del,cif,N;
char pom1,pom2,pom3,rim[30];
puts("Unijeti prirodan broj");
scanf("%d",&N);
if(N<=0)
{
puts("Pogresan broj!");
exit(0);
}
while(N>=1000)
{
rim[i++]='M';
N‐=1000;
}
while(N!=0)
{
if(N>=100)
{
pom1='M'; pom2='D'; pom3='C';
del=100;
}
else if(N<100&&N>=10)
{
pom1='C'; pom2='L'; pom3='X';
del=10;
}
else
{
pom1='X'; pom2='V'; pom3='I';
2.4 S TRINGOVI 169
del=1;
}
cif=N/del;
if(cif==9)
{
rim[i++]=pom3;
rim[i++]=pom1;
}
else if(cif>=5)
{
rim[i++]=pom2;
while(cif>5)
{
rim[i++]=pom3;
cif‐‐;
}
}
else if(cif==4)
{
rim[i++]=pom3;
rim[i++]=pom2;
}
else if(cif>=1)
{
while(cif>0)
{
rim[i++]=pom3;
cif‐‐;
}
}
N=N%del;
}
rim[i]='\0';
printf("Rimski N je %s\n",rim);
}
D, koji predstavlja broj 500, praćen slovima C, koja predstavljaju stotine do odgovarajuće
vrijednosti (npr. 700=DCC). Ako je u pitanju broj 400, postavlja se kombinacija CD, koja
označava stotinu do 500. Ako je cifra stotina od 1 do 3, postavlja se odgovarajući broj karaktera
C. Sličnu proceduru treba obaviti na ciframa koje opisuju desetice i jedinice. Na osnovu datih
objašnjenja sami pokušajte da realizujete program, a zatim uporedite vaše rješenje sa ovim.
Prilikom izrade smo pretpostavili da ukoliko je broj veći od 1000 onda u njegovom rimskom
zapisu imamo onoliko slova M koliko ima hiljada u našem broju. Ovakav zapis je korišćen u
početku, a kasnije je u cilju predstavljanja brojeva dosta većih od 1000 uveden zapis po kome se
iznad postojećeg karaktera dodaje horizontalna linija da bi označila množenje vrijednosti tog
karaktera sa 1000. Tako imamo da se broj 1000 može predstaviti kao Ī, broj 5000 se može
predstaviti kao V , 10000 kao X itd. Pored toga su uvedene i vertikalne linije sa obe strane
karaktera, slično zagradama za apsolutnu vrijednost, za predstavljanje broja koji je 100 puta veći
od vrijednosti tog karaktera. U kombinaciji sa hortizontalnom linijom sad možemo pisati
izuzetno velike brojeve, kao što su | V | za 500000, | X | za 1000000 itd. Zbog nemogućnosti
jednostavnog zapisa ovih brojeva u C-u, odlučili smo se za prvobitni zapis.
Izvršenje programa:
Unijeti prirodan broj
1345
Rimski broj je MCCCXLV
Zadatak 9. Učitava se nekoliko riječi, a štampa se koliko tu ima različitih riječi. Nema uslova
o maksimalnoj dužini riječi. Jedino ograničenje je raspolaganje sa 10000 bajtova
za učitavanje.
#include<stdio.h>
#include<string.h>
int main()
{
char s[10000],*p[2000],temp[10000];
int size=1,tp=0,i,j=0,razl=0,uslov,k;
p[0]=s;
printf("Unijeti rijeci: ");
while(size)
{
gets(temp);
size=strlen(temp);
for(i=0;i<size;i++)
s[tp+i]=temp[i];
s[tp+size]='\0';
tp+=size+1;
if(size)
p[++j]=s+tp;
}
for(i=0;i<j;i++)
{
uslov=1;
2.4 S TRINGOVI 171
for(k=i+1;k<j;k++)
if(strcmp(p[i],p[k])==0)
{
uslov=0;
break;
}
razl+=uslov;
}
printf("Broj razlicitih rijeci je %d",razl);
}
2.5 Funkcije
Zadatak 1. Napisati program koji će za unesen prirodan broj štampati sve proste brojeve koji
su manji od zadatog broja. Program treba da sadrži funkciju koja određuje da li je
broj, koji joj se prosljeđuje kao argument, prost.
#include<stdio.h>
int prost(int);
int main()
{
int i,n;
printf("Unesi broj:\n");
scanf("%d",&n);
printf("Prosti brojevi su:\n");
for(i=1;i<n;i++)
if(prost(i))
printf("%d ",i);
printf("\n");
}
int prost(int n)
{
int i;
for(i=2;i*i<=n;i++)
if(n%i==0)
return 0;
return 1;
}
Izvršenje programa:
Unesi broj:
97
Prosti brojevi su:
1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89
Zadatak 2. Napisati program koji će sadržati funkciju za štampanje prvih N prostih brojeva,
pri čemu je N broj unijet sa tastature.
#include<stdio.h>
int prost(int);
int main()
{
int i=1,j=0,n;
printf("Unesi broj:\n");
scanf("%d",&n);
printf("Prvih %d prostih brojeva su:\n",n);
while(j<n)
if(prost(i++))
{
printf("%d ",i‐1);
j++;
}
printf("\n");
}
int prost(int n)
{
int i;
for(i=2;i*i<n;i++)
if(n%i==0)
return 0;
return 1;
}
Izvršenje programa:
Unesi broj:
23
Prvih 23 prostih brojeva su:
1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79
Zadatak 3. Napisati program koji sadrži funkciju koja određuje i vraća srednju vrijednost niza
realnih brojeva. Niz (dužina i elementi) se zadaje po startovanju programa i
prosljeđuje se funkciji. Odštampati vrijednost koju funkcija vraća.
#include<stdio.h>
int main()
{
int N,i;
float a[20];
puts("Unijeti broj clanova niza");
scanf("%d",&N);
puts("Unijeti elemente niza");
for(i=0;i<N;i++)
scanf("%f",a+i);
printf("Srednja vrijednost ovog niza je: %.2f\n",sred(N,a));
}
int main()
{
char kar,s[50];
int b;
puts("Unijeti karakter");
scanf("%c",&kar);
puts("Unesi string");
scanf("%s",s);
b=ukloniKarakter(s,kar);
printf("String je %s",s);
printf(", a broj uklonjenih karaktera %c je %d\n",kar,b);
}
Zadatak 10. Napisati funkciju uzastopni za uklanjanje određenog broja uzastopnih karaktera
iz stringa s. Pored pokazivača na string, argumenti funkcije su početna pozicija u
stringu od koje počinje uklanjanje i broj uzastopnih karaktera koje treba ukloniti.
#include <stdio.h>
void uzastopni(char*,int,int);
int main()
{
char s[]={"algorXYZitam"};
uzastopni(s,5,3);
printf("Novi string je s=\"%s\"",s);
}
Zadatak 11. Napisati funkciju koja broji koliko se početnih karaktera dva stringa, koji su joj
argumenti, poklapa. Napisati i glavni program koji testira ovu funkciju.
#include<stdio.h>
int main()
{
char prvi[30],drugi[20];
puts("Unesi stringove");
gets(prvi);
gets(drugi);
printf("Poklapa se %d karaktera",poklapanje(prvi,drugi));
}
Unesi stringove
Rade
Radecki
Poklapa se 4 karaktera
Zadatak 12. Napisati funkciju koja vraća broj uzastopnih karaktera sa početka prvog stringa koji
se ne pojavljuju u drugom stringu. Napisati i glavni program koji testira funkciju.
#include<stdio.h>
int main()
{
char s1[]="Petar",s2[]="Marko";
printf("%d karaktera se ne pojavljuje",nepojavljivanje(s1,s2));
}
3 karaktera se ne pojavljuje
Zadatak 13. Napisati program koji sadrži funkciju koja konvertuje cijeli broj iz dekadnog
brojnog sistema u binarni brojni sistem, koristeći zapis sa dvojnim komplementom.
Štampati binarni zapis broja.
#include<stdio.h>
#include<string.h>
int main()
{
int broj;
char s[brojBita+1];
inicijalizacija(s);
printf("Unesi prirodni broj: ");
scanf("%d",&broj);
dec2bin(broj,s);
printf("Konverzijom se dobija: %s\n",s);
}
bin[i]=(bin[i]=='0')?'1':'0';
i=brojBita‐1;
while(i>0&&bin[i]=='1')
{
bin[i]='0';
i‐‐;
}
bin[i]='1';
}
}
Zadatak 14. Napisati funkciju int2str za konvertovanje cijelog broja u string. Napisati i
glavni program koji testira funkciju.
#include<stdio.h>
int main()
{
int n;
char str[20];
printf("Unesite broj: ");
scanf("%d",&n);
int2str(n,str);
printf("Dobijeni string je %s",str);
}
Unesite broj: 0
Dobijeni string je 0
#include<stdio.h>
int main()
{
int matr[30][30],vektMax[30],pozMax[30],M,N,i,j;
int maxMatr,vektPozMax[2];
printf("Unesite dimenzije matrice: ");
scanf("%d%d",&M,&N);
for(i=0;i<M;i++)
for(j=0;j<N;j++)
scanf("%d",&matr[i][j]);
for(i=0;i<M;i++)
vektMax[i]=max(matr[i],N,&pozMax[i]);
maxMatr=max(vektMax,M,&vektPozMax[1]);
vektPozMax[0]=pozMax[vektPozMax[1]];
printf("Maksimum matrice je %d i nalazi se ",maxMatr);
printf("u koloni %d i redu %d",vektPozMax[0],vektPozMax[1]);
}
Prvo objasnimo funkciju max. Na početku funkcije se postavlja da je tekući maksimum prvi
element u nizu i da je pozicija tog maksimuma nulta. Zatim se u for petlji prolazi kroz ostale
članove niza i kada se otkrije element veći od tekućeg maksimuma taj se element postavi za
tekući maksimum i ažurira se pozicija tekućeg maksimuma. Ako ima više elemenata koji su
jednaki maksimumu funkcija određuje kao poziciju maksimuma onu vrijednost na koju se prvo
naiđe u nizu. Šta treba promijeniti u funkciji da bi se pamtila posljednja pozicija? U glavnom
programu se učitaju dimenzije i vrijednosti elemenata matrice. Za svaki red matrice se poziva
funkcija max. Maksimumi pojedinih redova se memorišu u nizu vektMax, dok se pozicije
maksimuma u redovima memorišu u pozMax. U sljedećem koraku se maksimum matrice
određuje kao maksimum niza vektMax, a pozicija maksimuma u ovom nizu odgovara redu gdje
je detektovan maksimum matrice. Pozicija maksimuma niza pozMax odgovara koloni
maksimuma matrice. Protumačite zapis pozMax[vektPozMax[1]].
Izvršenje programa:
Unesite dimenzije matrice: 4
4
1 2 3 4
2 3 4 5
1 7 2 3
4 5 9 1
Maksimum matrice je 9 i nalazi se u koloni 2 i redu 3
2.6 R EKURZIVNE FUNKCIJE 191
int nzd(int,int);
int main()
{
int m,n;
printf("Unesite brojeve: ");
scanf("%d%d",&m,&n);
printf("GCD=%d\n",nzd(m,n));
}
Zadatak 2. Napisati rekurzivnu funkciju čije je zaglavlje int nzd(int m,int n), koja
računa NZD dva pozitivna cijela broja na osnovu relacije:
m, mn
nzd(m, n) nzd(m n, n), m n
nzd(m, n m), inače.
#include<stdio.h>
int nzd(int,int);
int main()
{
int m,n;
printf("Unesite brojeve: ");
scanf("%d%d",&m,&n);
printf("GCD=%d\n", nzd(m,n));
}
int main()
{
int n;
printf("Unesite cijeli broj: ");
scanf("%d",&n);
dek2bin(n,0);
printf("Binarna verzija broja %d je %s\n",n,s);
}
Globalne promjenljive:
s string koji sadrži binarni ekvivalent prirodnog broja
Promjenljive korišćene u glavnom programu:
n prirodni broj čiji se binarni oblik određuje
Promjenljive korišćene u funkciji dek2bin
n prirodni broj čiji se binarni ekvivalent traži
i pozicija u stringu s do koje se stiglo sa upisom
Promjenljive korišćene u funkciji obrniString
i, j indeksi elemenata stringa koji mijenjaju pozicije
Konverzija prirodnog broja n u njegov binarni ekvivalent je izvšena pomoću funkcija
dek2bin i obrniString. Funkcija dek2bin vrši konverziju, ali je dobijeni redosljed binarnih
2.7 S ORTIRANJE 197
2.7 Sortiranje
Zadatak 1. Sortirati elemente unijetog niza prirodnih brojeva u neopadajući poredak uz pomoć
insertion sort algoritma. U ovom algoritmu, niz se dijeli na dva podniza, sortirani
i nesortirani. Na početku, samo prvi član niza pripada sortiranom podnizu, a svi
ostali nesortiranom. Sortiranje se obavlja dodavanjem jednog po jednog elementa
nesortiranog u sortirani podniz, tj. ako imamo podniz od prvih k sortiranih
elemenata, uzima se prvi element nesortiranog podniza i umeće se gdje mu je
mjesto u sortiranom podnizu. Poslije tog umetanja imamo podniz od k+1 sortiranih
elemenata. Proceduru vršiti dok ima elemenata u nesortiranom podnizu.
#include<stdio.h>
int main()
{
int i=0,duz,niz[100];
puts("Unijeti duzinu niza");
scanf("%d",&duz);
puts("Elementi");
for(i=0;i<duz;i++)
scanf("%d",&niz[i]);
insertionSort(niz,duz);
printf("Sortirani niz je:\n");
for(i=0;i<duz;i++)
printf("%d ",niz[i]);
}
Zadatak 2. Sortirati elemente unijetog niza prirodnih brojeva u neopadajući poredak uz pomoć
quick sort algoritma.
#include<stdio.h>
int main()
{
int i=0,duz,niz[100];
puts("Unijeti duzinu niza");
scanf("%d",&duz);
puts("Elementi");
for(i=0;i<duz;i++)
scanf("%d",&niz[i]);
quickSort(niz,0,duz‐1);
printf("Sortirani niz je:\n");
for(i=0;i<duz;i++)
printf("%d ",niz[i]);
}
2.7 S ORTIRANJE 199
Zadatak 3. Napisati program kojim se učitava niz prirodnih brojeva, sa najviše 30 članova, a
zatim se vrši preuređivanje elemenata tako da prvo dolaze parni, pa neparni brojevi.
Parni i neparni brojevi ne moraju biti uređeni ni po kom pravilu. Preuređivanje
odraditi “u mjestu” tj. bez korišćenja pomoćnih nizova. Štampati elemente tako
dobijenog niza.
#include<stdio.h>
int main()
{
int i,j,N,temp,a[30];
puts("Unijeti duzinu niza");
scanf("%d",&N);
puts("Unijeti elemente");
for(i=0;i<N;i++)
scanf("%d",a+i);
for(i=0;i<N‐1;i++)
for(j=i+1;j<N;j++)
if(a[i]%2==1&&a[j]%2==0)
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
for(i=0;i<N;i++)
printf("%d ",a[i]);
}
Zadatak 6. Napisati program koji će računati broj mandata koji dobijaju kandidatske liste po
izvršenim izborima na osnovu Dontovog (D’Hondt) izbornog sistema sa cenzusom
od p procenata. Postupak određivanja broja dobijenih mandata po Dontovom
sistemu se sastoji od sljedećeg.
U izbornoj jedinici u kojoj se bira N poslanika sa M kandidatskih lista nakon
prebrojavanja glasova:
eliminišu se sve liste sa brojem glasova ispod cenzusa;
vrši se dijeljenje broja glasova svake preostale liste sa 1, 2, 3,..., N;
sa skupa svih lista se izdvoji N najvećih prethodno dobijenih količnika;
najmanji tako dobijeni količnik se uzima za zajednički djelitelj;
svaka lista ima pravo na onoliko mandata koliko se u njenom broju glasova
sadrži zajednički djelitelj.
#include<stdio.h>
#include<math.h>
int main()
{
float lista[10],cenzus,suma,kolicnik[300],pom;
int i,j,N,M,prosloCenz=0;
printf("Unijeti broj poslanika i poslanickih lista \n");
2.7 S ORTIRANJE 205
scanf("%d%d",&N,&M);
printf("Unijeti broj glasova pojedinih poslanickih lista \n");
suma=0.0;
for(i=0;i<M;i++)
{
scanf("%f",lista+i);
suma+=lista[i];
}
printf("Unijeti cenzus (u procentima)\n");
scanf("%f",&cenzus);
for(i=0;i<M;i++)
if(lista[i]>cenzus*suma/100)
{
prosloCenz++;
for(j=0;j<N;j++)
kolicnik[(prosloCenz‐1)*N+j]=lista[i]/(j+1);
}
for(i=0;i<prosloCenz*N‐1;i++)
for(j=1;j<prosloCenz*N;j++)
if(kolicnik[i]<kolicnik[j])
{
pom=kolicnik[i];
kolicnik[i]=kolicnik[j];
kolicnik[j]=pom;
}
for(i=0;i<M;i++)
if(lista[i]>cenzus*suma/100)
printf("Lista %d je osvojila %d mandata\n",
i,(int)(lista[i]/kolicnik[N‐1]));
}
2.9 Fajlovi
Zadatak 1. Napisati program za kreiranje fajla koji sadrži sve prirodne brojeve manje od
zadatog prirodnog broja, njihove kvadrate i kubove.
#include<stdio.h>
int main()
{
int broj,i;
FILE *a;
a=fopen("Fajl.txt","w");
if(a==NULL)
exit(1);
printf("Zadajte neki prirodan broj: ");
scanf("%d",&broj);
for(i=1;i<broj;i++)
fprintf(a,"%d\t%d\t%d\n",i,i*i,i*i*i);
fclose(a);
}
Zadatak 2. Svaki red fajla sadrži po jedan cio broj. Sastaviti program koji štampa najmanji i
najveći broj u tom fajlu.
#include<stdio.h>
#include<string.h>
int main()
{
FILE *a;
int min,max,i;
if((a=fopen("CioBroj.txt","r"))==NULL)
{
puts("Nije otvoren fajl");
exit(1);
}
if(fscanf(a,"%d",&i)==EOF)
{
puts("Fajl je prazan");
exit(1);
}
min=max=i;
while(fscanf(a,"%d",&i)!=EOF)
if(i>max)
max=i;
else if(i<min)
min=i;
printf("Minimum i maksimum su: %d i %d\n",min,max);
fclose(a);
}
18
21
3
0
6
17
Izvršenje programa:
Minimum i maksimum: 0 i 21
Zadatak 3. Svaki red fajla sadrži po jedan cio broj. Formirati novi fajl koji se razlikuje od
starog po tome što su prvi, treći, peti itd. broj udvostručeni.
#include<stdio.h>
int main()
{
int broj,i;
FILE *a,*b;
if((a=fopen("CioBroj.txt","r"))==NULL)
{
puts("Greska pri otvaranju fajla");
exit(1);
}
if((b=fopen("NoviCio.txt","w"))==NULL)
{
puts("Greska pri otvaranju fajla");
exit(1);
}
i=1;
while(fscanf(a,"%d",&broj)!=EOF)
{
fprintf(b,"%d\n",(i%2==1)?2*broj:broj);
i++;
}
fclose(a);
fclose(b);
}
12
5
22
3
4
18
42
3
0
6
34
Zadatak 4. Napisati program koji u novootvoreni fajl upisuje sve trocifrene prirodne brojeve
čija je suma cifara paran broj i koji su istovremeno prosti.
#include<stdio.h>
#include<stdlib.h>
int prost(int);
int sumaCif(int);
int main()
{
int i;
FILE *a;
a=fopen("prost.txt","w");
if(a==NULL)
exit(1);
for(i=100;i<1000;i++)
if(prost(i)&&sumaCif(i)%2==0)
fprintf(a,"%d ",i);
fclose(a);
}
int prost(int n)
{
int i;
for(i=2;i*i<=n;i++)
if(n%i==0)
return 0;
return 1;
}
int sumaCif(int n)
{
if(n>0)
return n%10+sumaCif(n/10);
return 0;
222 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
Zadatak 9. Koliko se puta u određenom fajlu pojavljuje karakter ''novi red''? Kolika je najveća
dužina reda? Napisati program koji otvara tekstualni fajl i odgovara na ova pitanja.
#include<stdio.h>
int main()
{
int red=0,duzina=0,pom=0;
FILE *a;
char ch;
a=fopen("Citaj.txt","r");
while((ch=fgetc(a))!=EOF)
{
pom++;
if(ch=='\n')
{
red++;
pom‐‐;
if(pom>duzina)
duzina=pom;
pom=0;
}
}
duzina=(duzina>pom)?duzina:pom;
printf("U fajlu se znak za novi red pojavljuje %d puta\n",red);
printf("Najveca duzina je %d\n",duzina);
}
Zadatak 10. Fajl Tekst.txt sadrži više redova teksta, pri čemu dužina reda ne prelazi 80
karaktera, a red je sastavljen od velikih slova i bjelina. Učitava se jedna riječ. Treba
štampati sve redove fajla u kojima se ta riječ bar jednom pojavljuje.
#include<stdio.h>
#include<string.h>
int main()
{
FILE *a;
char rijec1[81],rijec2[81];
printf("Unesite neku rijec: ");
scanf("%s",rijec2);
a=fopen("Tekst.txt", "r");
if(a==0)
exit(1);
while(fgets(rijec1,81,a)!=NULL)
if(strstr(rijec1,rijec2))
printf("%s\n",rijec1);
fclose(a);
}
Zadatak 11. U svakom redu fajla Word.txt je upisana jedna riječ, čija dužina ne prelazi 19
karaktera. Na osnovu ovog fajla treba kreirati fajl SortWord.txt u kojem će biti
upisane sortirane riječi iz fajla Word.txt. Nakon datih obrada, zatvorite predmetne
fajlove. Vodite računa da li su fajlovi korektno otvoreni. Maksimalan broj riječi u
polaznom fajlu ne prelazi 100.
#include<stdio.h>
#include<string.h>
int main()
{
FILE *a,*b;
char rijeci[100][20], temp[20];
int i,j,n;
n=0;
if((a=fopen("Word.txt","r"))==NULL)
{
puts("Nije otvoren fajl");
exit(1);
}
if((b=fopen("SortWord.txt","w"))==NULL)
{
puts("Nije otvoren fajl");
exit(1);
}
while(fscanf(a,"%s",rijeci[n])!=EOF)
n++;
for(i=0;i<n‐1;i++)
for(j=i+1;j<n;j++)
if(strcmp(rijeci[i],rijeci[j])>0)
{
strcpy(temp,rijeci[i]);
strcpy(rijeci[i],rijeci[j]);
strcpy(rijeci[j],temp);
}
for(i=0;i<n;i++)
fprintf(b,"%s\n",rijeci[i]);
fclose(a);
2.9 FAJLOVI 225
fclose(b);
}
Zadatak 12. U svakom redu fajla Studenti.txt upisani su podaci za studente: Prezime (string do
20 karaktera), Ime (string do 20 karaktera), Dan rođenja, Mjesec rođenja (cijeli
broj) i Godina rođenja. Treba sortirati studente u fajl Sortirani.txt tako što će prvo
biti prikazani najstariji studenti, pa mlađi. U slučaju da dva studenta imaju isti
datum rođenja sortirati ih po prezimenu, pa po imenu (leksikografski). Voditi
računa da li su fajlovi pravilno otvoreni. Maksimalan broj studenata čiji su podaci
zapisani u fajlu Studenti.txt ne može da pređe 50.
#include<stdio.h>
#include<string.h>
int main()
{
FILE *a,*b;
char ime1[50][20],ime2[20],prez1[50][20],prez2[20];
int i,j=0,dan1[50],dan2,mjes1[50],mjes2,god1[50],god2;
if((a=fopen("Studenti.txt","r"))==NULL)
{
2.9 FAJLOVI 229
return 0;
if(b==2&&!pres&&a==29)
return 0;
return 1;
}
Zadatak 1. Formirati strukturu koja predstavlja kompleksni broj, kao i osnovne funkcije sa
njom (sabiranje, oduzimanje, konjugovanje, realni i imaginarni dio, množenje i
dijeljenje).
#include<stdio.h>
struct complex{
float real,imag;
};
int main()
{
struct complex a,b,c,d,e,f;
a.real=1.23;
a.imag=3.24;
b.real=1.75;
b.imag=‐2.86;
c=add(a,b);
d=sub(a,b);
e=prod(a,b);
f=div(a,b);
a=konj(a);
printf("Zbir kompleksnih brojeva je: %f %f\n",c.real,c.imag);
printf("Razlika kompleksnih brojeva je: %f %f\n",d.real,d.imag);
printf("Proizvod kompleksnih brojeva je: %f %f\n",e.real,e.imag);
printf("Kolicnik kompleksnih brojeva je: %f %f\n",f.real,f.imag);
printf("Konjugovani kompleksni broj je: %f %f\n",re(a),im(a));
}
Zadatak 2. Napisati program koji učitava podatke za nekoliko lica, pri čemu su ti podaci:
1. pol lica (muški ili ženski), 2. ime lica i 3. ime supruge/supruga. Štampati koliko
tu ima bračnih parova.
#include<stdio.h>
#include<string.h>
typedef struct{
char pol[10];
char ime[10];
char suprug[10];
} LICE;
int main()
{
LICE osobe[20];
int n,brak=0,i,j;
puts("Unesi broj lica:");
scanf("%d",&n);
puts("Unesi podatke za lica:");
for(i=0;i<n;i++)
scanf("%s%s%s",osobe[i].pol,osobe[i].ime,osobe[i].suprug);
for(i=0;i<n‐1;i++)
for(j=i+1;j<n;j++)
{
234 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
if((strcmp(osobe[i].pol,osobe[j].pol)!=0)&&
(strcmp(osobe[i].ime,osobe[j].suprug)==0)&&
(strcmp(osobe[i].suprug,osobe[j].ime)==0))
{
brak++;
break;
}
}
printf("Ima ukupno %d brakova.\n",brak);
}
Zadatak 3. Napisati program koji formira jednostruku povezanu listu. Element liste je
struktura koja sadrži cio broj. Kriterijum za prekid unosa je unos broja većeg od
30, koji se postavlja na rep liste.
#include<stdio.h>
2.10 STRUKTURE I LISTE 239
{
q=(struct lista *)malloc(sizeof(struct lista));
if(q==NULL)
exit(1);
q‐>i=n;
q‐>next=glava;
return q;
}
else
{
q=glava;
while(q‐>next!=NULL&&q‐>next‐>i<n)
q=q‐>next;
r=(struct lista *)malloc(sizeof(struct lista));
r‐>i=n;
r‐>next=q‐>next;
q‐>next=r;
return glava;
}
}
Zadatak 9. Elementi liste su cijeli brojevi. Sastaviti funkciju koja iz date liste izbacuje sve
brojeve koji su manji od zadatog broja (argument funkcije).
struct lista *izbaciManje(struct lista *glava,int n)
{
struct lista *q,*r;
while(glava!=NULL&&glava‐>i<n)
{
q=glava;
glava=glava‐>next;
free(q);
}
if(glava==NULL)
return NULL;
q=glava;
240 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
while(q‐>next!=NULL)
{
if(q‐>next‐>i<n)
{
r=q‐>next;
q‐>next=q‐>next‐>next;
free(r);
}
else
q=q‐>next;
}
return glava;
}
Zadatak 10. Napisati funkciju koja na osnovu zadate jednostruko povezane liste prirodnih
brojeva formira dvije nove liste: listu neparnih i listu parnih brojeva. Napisati i
glavni program koji testira funkciju.
#include<stdio.h>
#include<stdlib.h>
struct lista{
int i;
struct lista *next;
} *glavaPar=NULL,*glavaNepar=NULL;
int main()
{
struct lista *p,*q,*t;
int n;
printf("Unesi elemente liste. Broj >30 prekida unos. ");
p=(struct lista *)malloc(sizeof(struct lista));
p‐>next=NULL;
2.10 STRUKTURE I LISTE 241
scanf("%d",&n);
p‐>i=n;
t=p;
while(n<30)
{
q=(struct lista *)malloc(sizeof(struct lista));
scanf("%d",&n);
q‐>next=NULL;
q‐>i=n;
t‐>next=q;
t=q;
}
parniNeparni(p);
printf("Parni\n");
stampajListu(glavaPar);
printf("Neparni\n");
stampajListu(glavaNepar);
}
struct lista{
int i;
struct lista *next;
};
int main()
{
struct lista *p1,*q1,*t1,*p2,*q2,*t2,*qn;
int n,r;
printf("Unesi elemente prve liste. Broj >30 prekida unos. ");
p1=(struct lista *)malloc(sizeof(struct lista));
p1‐>next=NULL;
scanf("%d",&n);
p1‐>i=n;
t1=p1;
while(n<30)
{
q1=(struct lista *)malloc(sizeof(struct lista));
scanf("%d",&n);
q1‐>next=NULL;
q1‐>i=n;
t1‐>next=q1;
t1=q1;
}
printf("Unesi elemente druge liste. Broj >30 prekida unos. ");
p2=(struct lista *)malloc(sizeof(struct lista));
p2‐>next=NULL;
scanf("%d",&n);
p2‐>i=n;
t2=p2;
while(n<30)
{
q2=(struct lista *)malloc(sizeof(struct lista));
scanf("%d",&n);
q2‐>next=NULL;
q2‐>i=n;
t2‐>next=q2;
t2=q2;
}
qn=udruzi(p1,p2);
printf("Sjedinjena lista je: ");
stampajListu(qn);
}
p1=gl1;
p2=gl2;
glava=(struct lista *)malloc(sizeof(struct lista));
if(p1‐>i<p2‐>i)
{
glava‐>i=p1‐>i;
p1=p1‐>next;
}
else
{
glava‐>i=p2‐>i;
p2=p2‐>next;
}
glava‐>next=NULL;
tek=glava;
while(p1!=NULL||p2!=NULL)
{
q=(struct lista *)malloc(sizeof(struct lista));
tek‐>next=q;
tek=q;
q‐>next=NULL;
if(p1==NULL||(p1!=NULL&&p2!=NULL&&p2‐>i<p1‐>i))
{
q‐>i=p2‐>i;
p2=p2‐>next;
}
else
{
q‐>i=p1‐>i;
p1=p1‐>next;
}
}
return glava;
}
Zadatak 12. Elementi jednostruko povezane liste su riječi. Sastaviti funkciju koja određuje da
li su te riječi sortirane u rastući poredak (podrazumjevati leksikografsko uređenje).
U glavnom programu kreirati listu sa proizvoljnim brojem elemenata i testirati
funkciju.
#include<stdio.h>
#include<string.h>
struct lista{
char rijec[10];
struct lista *next;
};
int main()
{
struct lista *p,*q,*t;
int n=0,ind;
puts("Unijeti rijeci: ");
p=(struct lista *)malloc(sizeof(struct lista));
if(p==NULL)
exit(1);
p‐>next=NULL;
2.10 STRUKTURE I LISTE 247
scanf("%s",p‐>rijec);
t=p;
while(n++<6)
{
q=(struct lista *)malloc(sizeof(struct lista));
if(q==NULL)
exit(1);
scanf("%s",q‐>rijec);
q‐>next=NULL;
t‐>next=q;
t=q;
}
ind=jeLiSortirana(p);
if(ind)
puts("Rijeci su leksikografski poredjane!");
else
puts("Rijeci nijesu leksikografski poredjane!");
}
Bilja
Ceca
Dana
Jaca
Mara
Stevo
Rijeci su leksikografski poredjane!
Unijeti rijeci:
Vuko
Djuro
Niksa
Petar
Idzo
Dusko
Simo
Rijeci nijesu leksikografski poredjane!
Zadatak 13. Sastaviti funkciju za preokretanje redosljeda elemenata jednostruko povezane liste.
Funkcija vraća pokazivač na glavu preokrenute liste.
struct lista *preokreni(struct lista *glava)
{
struct lista *preth=NULL,*tek=glava,*sljed;
while(tek‐>next!=NULL)
{
sljed=tek‐>next;
tek‐>next=preth;
preth=tek;
tek=sljed;
}
tek‐>next=preth;
return tek;
}
glava=glava‐>next;
}
printf("\n");
}
}
Zadatak 16. Napisati funkciju za brisanje odgovarajućeg čvora iz dvostruko povezane liste, koji
sadrži broj n, proslijeđen kao argument. Struktura za čvor liste je ista kao u
prethodnom zadatku.
struct lista2P *brisiElement2P(struct lista2P *glava,int n)
{
struct lista2P *q,*r;
if(glava‐>i==n)
{
q=glava‐>next;
q‐>prev=NULL;
254 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
free(glava);
return q;
}
else
{
q=glava;
while(q‐>next!=NULL&&q‐>next‐>i!=n)
q=q‐>next;
if(q‐>next!=NULL&&q‐>next‐>i==n)
{
r=q‐>next;
if(q‐>next‐>next!=NULL)
q‐>next‐>next‐>prev=q;
q‐>next=q‐>next‐>next;
free(r);
}
return glava;
}
}
Zadatak 17. Sastaviti program koji zadate riječi upisuje u dvostruko povezanu listu. Zatim se
učitava jednu riječ i provjerava da li ta riječ postoji u listi.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct lista2P{
char rijec[20];
struct lista2P *next;
struct lista2P *prev;
};
int main()
{
struct lista2P *p,*q,*t;
char s[20];
p=(struct lista2P *)malloc(sizeof(struct lista2P));
p‐>next=NULL;
p‐>prev=NULL;
puts("Unesi rijeci u listu: ");
gets(s);
strcpy(p‐>rijec,s);
2.10 STRUKTURE I LISTE 255
t=p;
while(strcmp(s,"kraj")!=0)
{
q=(struct lista2P *)malloc(sizeof(struct lista2P));
gets(s);
q‐>next=NULL;
q‐>prev=t;
strcpy(q‐>rijec,s);
t‐>next=q;
t=q;
}
puts("Unesi rijec koja se trazi u listi:");
scanf("%s",s);
if(postojanje(p,s))
puts("Postoji!");
else
puts("Ne postoji!");
}
#include<stdio.h>
#include<stdlib.h>
struct stablo{
int i;
struct stablo *left;
struct stablo *right;
};
int main()
{
struct stablo *p=NULL,*q=NULL,*r=NULL,*t=NULL;
p=(struct stablo *)malloc(sizeof(struct stablo));
if(p==NULL)
exit(1);
p‐>i=1;
p‐>left=NULL;
p‐>right=NULL;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=2;
q‐>left=NULL;
q‐>right=NULL;
266 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
p‐>left=q;
r=(struct stablo *)malloc(sizeof(struct stablo));
if(r==NULL)
exit(1);
r‐>i=3;
r‐>left=NULL;
r‐>right=NULL;
p‐>right=r;
t=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=4;
q‐>left=NULL;
q‐>right=NULL;
t‐>left=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=5;
q‐>left=NULL;
q‐>right=NULL;
t‐>right=q;
t=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=6;
q‐>left=NULL;
q‐>right=NULL;
t‐>left=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=7;
q‐>left=NULL;
q‐>right=NULL;
r‐>right=q;
}
Nakon alokacije memorije za čvor (praćene sa provjerom uspješnosti ove operacije) vrši se
alokacija lijevog i desnog sina (pokazivači q i r). Zatim preko odgovarajućih pokazivača korijen
stabla pokaže na ove čvorove. U ostatku programa, promjenljiva q se koristi za alokaciju
pojedinih podataka članova, dok se kao pomoćna promjenljiva koja pamti pojedine čvorove
koristi promjenljiva t. Ovaj program je dat kao potencijalni test primjer za algoritme koji će biti
rađeni kasnije.
Zadatak 2. Svakom čvoru pridružen je cijeli broj. Cijele brojeve koji pripadaju čvoru treba
štampati u skladu sa preorder obilaskom.
void stampaPreorder(struct stablo *root)
{
printf("%d\n",root‐>i);
if(root‐>left!=NULL)
stampaPreorder(root‐>left);
if(root‐>right!=NULL)
stampaPreorder(root‐>right);
}
Zadatak 3. Napisati funkciju koja određuje težinu stabla (broj čvorova stabla).
int tezina(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
return 1+tezina(root‐>left)+tezina(root‐>right);
else if(root‐>left!=NULL)
return 1+tezina(root‐>left);
else if(root‐>right!=NULL)
return 1+tezina(root‐>right);
else
return 1;
}
Zadatak 4. Sastaviti funkciju koja određuje najveći cijeli broj upisan u čvorove binarnog
stabla.
int maksCvor(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
return max(max(root‐>i,maksCvor(root‐>left)),maksCvor(root‐>right));
else if(root‐>left!=NULL)
return max(root‐>i,maksCvor(root‐>left));
else if(root‐>right!=NULL)
return max(root‐>i,maksCvor(root‐>right));
return root‐>i;
}
Zadatak 5. Svakom čvoru pridružen je jedan cijeli broj. Sastaviti funkciju koja provjerava da
li na svakom putu od korijena ka listovima brojevi opadaju.
int opada(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
if(root‐>i>root‐>left‐>i && root‐>i>root‐>right‐>i)
return opada(root‐>left)*opada(root‐>right);
else
return 0;
if(root‐>left!=NULL)
if(root‐>i>root‐>left‐>i)
return opada(root‐>left);
else
return 0;
if(root‐>right!=NULL)
if(root‐>i>root‐>right‐>i)
2.12 B INARNO STABLO 269
return opada(root‐>right);
else return 0;
return 1;
}
Zadatak 6. Sastaviti funkciju koja vraća koliko puta se dešava da je ocu i sinu u stablu
pridružen isti broj.
int otacSinIsti(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
return (root‐>i==root‐>left‐>i)+(root‐>i==root‐>right‐>i)+
otacSinIsti(root‐>right)+otacSinIsti(root‐>left);
else if(root‐>left!=NULL)
return (root‐>i==root‐>left‐>i)+otacSinIsti(root‐>left);
else if(root‐>right!=NULL)
return (root‐>i==root‐>right‐>i)+otacSinIsti(root‐>right);
else
return 0;
}
Zadatak 7. Svakom čvoru pridružen je jedan cijeli broj. Da li postoji barem jedan put od
korijena ka bilo kom listu u kome su upisani samo parni brojevi? Sastaviti
odgovarajuću funkciju.
270 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
Zadatak 8. Svakom čvoru pridružena je jedna riječ dužine manje od 11 karaktera. Sastaviti
funkciju koja određuje i vraća koliko puta se pojavljuje data riječ.
struct stablo{
char rijec[12];
struct stablo *left;
struct stablo *right;
};
Zadatak 9. Napisati funkciju koji određuje širinu stabla. Širina stabla se definiše kao najveći
broj čvorova duž jedne horizontale, tj. na istom rastojanju čvorova od korijena
stabla.
int sirina(struct stablo *root)
{
int N=visina(root);
int k,m=1,broj;
for(k=1;k<=N;k++)
{
broj=brojNivo(root,k);
if(broj>m)
m=broj;
}
return m;
}
2.13 Igre
Zadatak 1. Položaj figure na šahovskoj tabli zadaje se parom brojeva (brojevi su od 0 do 7).
Na tabli su samo jedan lovac i jedan skakač. Koliko je ukupno polja napadnuto sa
ove dvije figure?
#include<stdio.h>
int main()
{
int h1,v1,h2,v2,i,j,polja=0,A,B,C,D,E;
printf("Unijeti poziciju lovca: ");
scanf("%d %d",&h1,&v1);
printf("Unijeti poziciju skakaca: ");
scanf("%d %d",&h2,&v2);
for(i=0;i<8;i++)
for(j=0;j<8;j++)
{
A=!((i==h1&&j==v1)||(i==h2&&j==v2));
B=(i‐h2)*(i‐h2)+(j‐v2)*(j‐v2)==5;
C=(i‐h1==j‐v1)||(i‐h1==v1‐j);
D=!((v1‐v2==h2‐h1)||(v1‐v2==h1‐h2));
E=!((h2‐h1)*(h2‐h1)+(v2‐v1)*(v2‐v1)<(h1‐i)*(h1‐i)+(v1‐j)*(v1‐j)
&& (h2‐i)*(h2‐i)+(v2‐j)*(v2‐j)<(h1‐i)*(h1‐i)+(v1‐j)*(v1‐j));
if(A&&(B||(C&&(D||E))))
polja++;
}
printf("Broj napadnutih polja je: %d",polja);
}
napadnutih polja smo realizovali prolaskom kroz sva polja šahovske table i preko 5 pomoćnih
logičkih promjenljivih A, B, C, D i E. Promjenljiva A ispituje da li je tekuće polje (i,j) različito
od pozicija dvije unijete figure. Ovaj logički uslov mora biti ispunjen. Promjenljiva B ispituje da
li poziciju napada skakač. Ako su skakač i polje razmaknuti za 2 po jednoj koordinati i 1 po
drugoj koordinati, odnosno ako je kvadrat distance između njih 5, onda je polje napadnuto.
Promjenljiva C ispituje da li bi polje napadao lovac da nema skakača na tabli. Lovac napada polja
po glavnoj dijagonali u odnosu na njega, a to su polja za koja važi i‐h1==j‐v1, kao i polja na
sporednoj dijagonali u odnosu na njega, a to su polja kod kojih važi i‐h1==v1‐j. Uslov D
provjerava da li je skakač na nekoj od dijagonala u odnosu na lovca. Ako jeste (D će biti jednako
0), skakač može da preklopi neko od polja koje bi inače lovac napadao. Posljednji uslov E
provjerava da li je skakač bliži tekućoj tački i lovcu nego što je međusobno rastojanje između
lovca i tačke. Dakle, ako je skakač na poziciji koju napada lovac i ako skakač pokriva polje koje
bi inače bilo napadnuto, onda je rastojanje od skakača do posmatranog polja i skakača do lovca
manje od međusobnog rastojanja između lovca i skakača. Da bi polje bilo napadnuto treba da je
ispunjen uslov A, a onda je dovoljno da je ispunjen uslov B, a može i sa kombinacijom da je
ispunjen uslov C i barem jedan od uslova D i E.
Izvršenje programa:
Unijeti poziciju lovca: 2 3
Unijeti poziciju skakaca: 4 1
Broj napadnutih polja je: 15
Na slici ispod je ilustrovana opisana pozicija (L označava poziciju lovca, S poziciju skakača,
dok su X napadnuta polja).
7 X
6 X
5 X X
4 X X
3 L X X
2 X X X X
1 X S
0 X X
0 1 2 3 4 5 6 7
Zadatak 2. Napisati program koji rekurzivno rješava problem Hanojske kule (eng. Tower of
Hanoi). Postavka problema je data u objašnjenju izrade zadatka.
#include<stdio.h>
2.14 IGRE 277
#include<dos.h>
int N,pocetni[11],krajnji[11],pomocni[11];
char znak='*';
int main()
{
int i;
printf("Unijeti broj diskova (ne veci od 10)\n");
scanf("%d",&N);
while(N>10)
{
printf("Unijeti broj diskova (ne veci od 10)\n");
scanf("%d",&N);
}
for(i=0;i<N;i++)
pocetni[i]=N‐i;
puts("Prebacivanje izgleda:");
Hanoj(pocetni,krajnji,pomocni,N);
}
j++;
b[j]=a[i];
a[i]=0;
}
void crtaj(void)
{
int i=0;
for(i=0;i<N;i++)
{
crtajDisk(pocetni,i);
printf(" ");
crtajDisk(krajnji,i);
printf(" ");
crtajDisk(pomocni,i);
printf("\n");
}
printf("\n\n\n");
delay(1000);
}
Za prebacivanje najvećeg diska sa štapa A na štap B ne treba biti genijalac, ali kako prebaciti
N-1 diskova na štap C, pridržavajući se pomenutih pravila? Odgovor se bazira na već pomenutoj
logici. Naime, da bismo prebacili N-1 najmanjih diskova na štap C potrebno je prvo prebaciti N-
2 najmanjih diskova na štap B, nakon toga prebaciti drugi po veličini disk na štap C (disk 4 sa
gornjih slika), a zatim N-2 diskova sa štapa B prebaciti na disk C. Analogno, da bismo prebacili
N-2 najmanjih diskova na štap B, potrebno je prvo prebaciti N-3 najmanjih diskova na štap C,
zatim prebaciti treći po veličini disk na štap B (disk 3 sa gornjih slika), a zatim N-3 diskova sa
štapa C prebaciti na disk B. Ova rekurzija se nastavlja sve dok broj diskova koji treba prebaciti
na štap B ili C ne postane 1. Ovo ujedno predstavlja i najbrže moguće rješenje datog problema.
Srce programa predstavlja rekurzivna funkcija Hanoj. Njeni argumenti su nizovi cijelih
brojeva a, b i c. Niz a, zapravo, predstavlja štap sa kojeg prebacujemo diskove, niz b predstavlja
štap na koji prebacujemo diskove i niz c predstavlja pomoćni štap u proceduri prebacivanja.
Diskovi na štapovima su predstavljeni cijelim brojevima od 1 (najmanji disk) do N (najveći disk).
Posljednji argument funkcije Hanoj je N, broj diskova koji se prebacuje sa štapa a na štap b. U
prvom pozivu ove funkcije, u okviru funkcije main, njoj se prosljeđuje ukupan broj diskova na
početnom štapu. Ukoliko je taj broj veći od 1 onda ova funkcija rekurzivno poziva sebe dva puta:
Hanoj(a,c,b,N‐1) i Hanoj(c,b,a,N‐1). Prvi poziv prebacuje N-1 diskova sa štapa a na
štap c, koristeći štap b kao pomoćni, dok drugi prebacuje N-1 diskova sa štapa c na štap b,
koristeći štap a kao pomoćni. Vidimo da to upravo odgovara opisanoj proceduri. U okviru
funkcije Hanoj postoje još dvije funkcije: prebaci i crtaj. Prva služi za prebacivanje diska
280 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
sa štapa a na štap b, dok druga služi za crtanje diskova na sva tri štapa. Pošto štapove
predstavljamo nizovima cijelih brojeva, prebacivanje predstavlja uzimanje posljednjeg elementa
niza a (tj. uzimanje sa vrha štapa) i njegovo nadovezivanje na niz b (tj. smještanje na vrh štapa).
Nakon toga u posljednji element niza a upisujemo 0 (tj. brišemo ga sa vrha štapa). Funkcija
crtaj crta diskove smještene na štapovima koristeći funkciju crtajDisk, kojoj se pored
pokazivača na niz cijelih brojeva a prosljeđuje i redni broj elementa i, tj. disk koji crtamo.
Ukoliko je taj element jednak nuli (na datom štapu ne postoji disk na “visini” i, pri čemu i=1
odgovara disku na samom dnu štapa, i=2 disku koji se nalazi na tom disku na dnu, itd.) onda na
ekranu ispisuje blanko znake, a ako nije jednak nuli onda se dati disk crta karakterom znak, koji
je deklarisan kao globalna promjenljiva, i to sa duplo više znakova nego što je veličina diska. Na
primjer, disk veličine 4 se crta sa 8 karaktera znak. Pored karaktera znak i nizovi koji
predstavljaju štapove se deklarišu kao globalne promjenljive. Pošto nizovi nisu eksplicitno
inicijalizovani oni se, kao globalne promjenljive, inicijalizuju na 0. Primjetimo da je u funkciji
crtaj korišćena bibliotečka rutina delay, koja odlaže izvršavanje programa za cio broj
milisekundi, koji joj se prosljeđuje kao argument. Da bi koristili ovu rutinu potrebno je uključiti
zaglavlje dos.h. U glavnoj funkciji se, nakon unosa broja diskova N, for petljom vrši
smještanje tih diskova na početni štap tako da bude pocetni[0]=N, pocetni[1]=N‐1, ...,
pocetni[N‐1]=1. Primjetimo da smo while petljom ograničili broj diskova na maksimalno
10. Razlog tome je broj operacija, tj. premještanja, koji treba izvršiti, a koji eksponencijalno raste
sa brojem diskova. Naime, ukoliko je broj diskova koji treba prebaciti sa početnog na krajnji štap
jednak N, onda je, po našoj proceduri, potrebno izvršiti 2N-1 premještanja. Napominjemo još
jednom da je ovo optimalna procedura sa stanovišta broja izvršenih premještanja. Studentima se
za vježbu ostavlja iterativno rješenje problema Hanojske kule.
Tvorac problema Hanojske kule je francuski matematičar Edouard Lucas. Originalna
postavka ovog problema sadrži N=8 diskova. Lucas je zagonetku “začinio” zanimljivom
legendom o mnogo većoj Brahma kuli, koja ima 64 diska od čistog zlata, smještenih na tri
dijamantske igle. U trenutku stvaranja svijeta hindu bog Brahma je sve diskove smjestio na prvu
iglu, poređavši ih od najvećeg ka najmanjem, i naložio hindu svještenicima da sve diskove
prebace na drugu iglu, koristeći treću kao pomoćnu, u skladu sa opisanim pravilima
premještanja. Od tad svještenici neprekidno rade na tom zadatku. U trenutku završetka zadatka
kula će se srušiti i uništiti univerzum. Ukoliko pretpostavimo da je svještenicima u prosjeku
potrebna 1 sekunda za premještanja jednog diska, onda im je za rješavanje čitavog problema
potrebno 264-1 sekundi, što je približno 585 milijardi godina, što je više od procijenjenog trajanja
univerzuma. Dakle, bez brige.
Zadatak 3. Napisati program koji simulira poznatu igru iks-oks. Igra se na tabli 3×3 na kojoj
prvi igrač postavlja X, drugi O i tako naizmjenično. Pobjeđuje onaj igrač koji u bilo
kom redu, koloni ili na dijagonalama postavi tri ista karaktera. Ako je polje zauzeto
preko njega se ne može unijeti novi karakter. Igra se može završiti i ako su sva
polja popunjena i pri tom nema pobjednika (neriješeno).
Program treba da omogući igračima da zadaju pozicije karaktera. Ako je zadata
pozicija van opsega ili ako je zadato polje zauzeto daje se upozorenje i zahtjeva
novi unos. Nakon svakog poteza takmičara provjerava se da li je takmičar pobjedio
(ako se ovo dogodi štampa se prigodna poruka) i vrši prikaz tabele. Posljednji
(deveti) forsirani potez na jedino preostalo mjesto program sam postavlja. Ako se
igra završi bilo neriješeno, bilo pobjedom jednog od takmičara, program pita da li
se želi nova igra i u slučaju odrečnog odgovora prekida se izvršavanje.
2.14 IGRE 281
#include<stdio.h>
int main()
{
int i,j,k,x,y,p1,p2,unos=0,pobjeda=0,losUnos;
char iks_oks[3][3],p[3]="XO",nova;
do
{
if(unos==0)
{
for(i=0;i<3;i++)
for(j=0;j<3;j++)
iks_oks[i][j]=' ';
}
losUnos=1;
if(unos<8)
{
if(unos%2==0)
printf("Unijeti poziciju prvog igraca ‐ X\n");
else
printf("Unijeti poziciju drugog igraca ‐ O\n");
scanf("%d%d",&x,&y);
if(x<0||x>2||y<0||y>2||iks_oks[x][y]=='X'||iks_oks[x][y]=='O')
losUnos=0;
}
else if(unos==8)
{
for(i=0;i<3;i++)
for(j=0;j<3;j++)
if(iks_oks[i][j]==' ')
iks_oks[i][j]='X';
}
if(losUnos==1)
{
if(unos<8)
{
if(unos%2==0)
iks_oks[x][y]='X';
else
iks_oks[x][y]='O';
}
unos++;
for(k=0;k<7;k++)
{
if(k==0)
printf(" | 0 | 1 | 2 \n");
if(k!=0&&k%2!=0)
printf("‐‐‐ ‐‐‐ ‐‐‐ ‐‐‐\n");
else if(k!=0&&k%2==0)
282 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
{
printf(" %d ",k/2‐1);
for(i=0;i<3;i++)
printf("| %c ",iks_oks[k/2‐1][i]);
printf("\n");
}
}
printf("\n");
for(k=0;k<2;k++)
{
for(i=0;i<3;i++)
{
p1=p2=1;
for(j=0;j<3;j++)
{
p1*=(iks_oks[i][j]==p[k]);
p2*=(iks_oks[j][i]==p[k]);
}
pobjeda=pobjeda||(p1||p2);
}
p1=p2=1;
for(i=0;i<3;i++)
{
p1*=(iks_oks[i][i]==p[k]);
p2*=(iks_oks[i][2‐i]==p[k]);
}
pobjeda=pobjeda||(p1||p2);
if(pobjeda)
break;
}
}
if(pobjeda==1||unos==9)
{
if(pobjeda==1)
printf("Pobjedio je %s takmicar!",k==0?"prvi":"drugi");
else
printf("Nerijesena partija!\n");
printf("\n\nZelite li novu partiju?\n\td ‐ Da \n\tn ‐ Ne\n");
do
{
scanf("%c",&nova);
} while(nova!='d'&&nova!='n');
if(nova=='d')
unos=pobjeda=0;
else
break;
}
if(losUnos==0)
printf("Pogresan unos!\n");
} while(1);
2.14 IGRE 283
Zadatak 4. Napisati program koji simulira kockarsku igru craps, koja se igra na sljedeći način.
Bacaju se dvije kocke na kojima se sa jednakom vjerovatnoćom mogu pojaviti
brojevi od 1 do 6. Nakon prvog bacanja sabiraju se dobijeni brojevi na kockama.
Ako je suma 2, 3 ili 12 (tzv. craps) učesnik gubi, a ako je suma 7 ili 11 učesnik
dobija. Ako suma na kockama nije nijedan od ovih brojeva dobijeni broj predstavlja
cilj učesnika u narednim bacanjima. Učesnik nastavlja da baca sve dok ne ponovi
ciljnu sumu ili baci sumu 7. Ako baci ciljnu sumu pobjedio je, a ako baci 7 izgubio
je. Program napraviti tako da učesnik na početku raspolaže sa određenim ulogom.
Dio uloga može da uloži u svaku pojedinačnu igru. Ulog se povećava za uloženi
dio ako je igra uspješna i smanjuje ako nije. Učesnik nakon svake igre, bilo
uspješne ili ne, može da odustane. Igra se prekida i ako učesnik ostane bez novca.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int bacaj(void);
int main()
{
int suma,cilj,status,pot,ulog;
char nova='d';
do
{
printf("Unijeti pocetni ulog kao pozitivan cijeli broj: ");
scanf("%d",&pot);
} while(pot<=0);
srand((unsigned)time(0));
while (nova=='d'&&pot>0) {
printf("\nNa raspolaganju imate %d eura.\n",pot);
do
{
2.14 IGRE 285
int bacaj(void)
{
int kocka1,kocka2,suma;
kocka1=1+rand()%6;
286 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
kocka2=1+rand()%6;
suma=kocka1+kocka2;
printf("Igrac je bacio %d + %d = %d\n",kocka1,kocka2,suma);
return suma;
}
sume promjenljiva status postaje jednaka 1 (pobjeda), -1 (poraz) ili 0 (nastavak igre). U slučaju
da je status=0, dobijena suma postaje ciljna suma i ide se na ponovno bacanje kocki, sve dok
suma ne postane jednaka ciljnoj sumi ili broju 7. Nakon toga se ispisuje poruka o ishodu igre,
pot se uvećava ili smanjuje u zavisnosti od tog ishoda i, u slučaju da još ima novca na potu,
igrač se pita da li želi da nastavi sa igrom. U slučaju da ne želi, kao i u slučaju da više nema
novca na potu, izlazi se iz while petlje i prekida izvršavanje programa.
Izvršenje programa:
Unijeti pocetni ulog kao pozitivan cijeli broj: 210
Igrac je bacio 1 + 1 = 2
Igrac je izgubio!
Igrac je bacio 6 + 1 = 7
Igrac je pobjedio!
Igrac je bacio 5 + 1 = 6
Ciljna suma je 6
Igrac je bacio 1 + 2 = 3
Igrac je bacio 1 + 2 = 3
Igrac je bacio 3 + 6 = 9
Igrac je bacio 2 + 5 = 7
Igrac je izgubio!
Ostali ste bez novca!
Zadatak 5. Napisati program kojim se simulira miješanje i dijeljenje špila karata. Miješanje i
dijeljenje obaviti pomoću zasebnih funkcija.
#include<stdio.h>
288 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
#include<stdlib.h>
#include<time.h>
int main()
{
const char *znak[4]={"Herc","Karo","Tref","Pik"};
const char *broj[13]={"As","Dvojka","Trojka","Cetvorka","Petica",
"Sestica","Sedmica","Osmica","Devetka",
"Desetka","Pub","Kraljica","Kralj"};
int spil[4][13]={0};
srand(time(0));
mijesaj(spil);
printf("\n");
dijeli(spil,broj,znak);
}
break;
}
if(spil[vrsta][kolona]==karta&&kolona<=12)
break;
}
}
Naravno, može se desiti da ove dvije naredbe generišu isti broj vrste i kolone. To znači da
pokušavamo da smjestimo kartu na poziciju u špilu koja je već zauzeta, tj. u kojoj je upisan cijeli
broj veći od 0. U tom je slučaju while uslov ispunjen i ide se na novo izvršavanje do...while
petlje. Tek kad dobijemo poziciju u matrici u kojoj je upisana 0, tj. nije smještena nijedna karta,
možemo upisati tekuću kartu. Jedan mogući izgled matrice spil nakon izvršenja funkcije
mijesaj je dat na narednoj slici.
Funkcija dijeli vrši dijeljenje karata koristeći tri ugniježdene for petlje. U spoljašnjoj
petlji promjenljiva karta uzima vrijednosti od 1 do 52, dok dvije unutrašnje for petlje vrše
pretragu matrice spil tražeći element matrice u koji je upisana tekuća vrijednost promjenljive
karta. Kada se pronađe traženi element vrši se štampanje karte koja odgovara tom elementu,
tako što se odštampa element niza broj koji odgovara rednom broju kolone traženog elementa
matrice i element niza znak koji odgovara rednom broju vrste traženog elementa matrice. Na
primjer, ukoliko pretpostavimo da je karta=1 smještena u elementu spil[1][5], kao što je
prikazano na slici iznad, onda će biti odštampana karta šestica karo. Onoga trenutka kada se
pronađe željeni element matrice spil izlazi se iz for petlje koja se kreće po promjenljivoj
kolona naredbom break, a zatim i iz for petlje koja se kreće po promjenljivoj vrsta
naredbom break. Prekidanje izvršavanja unutrašnjih for petlji naredbom break se vrši iz
razloga optimizacije vremena pretraživanja matrice spil. Onog trenutka kada pronađemo
željeni element matrice nema potrebe da pretražujemo ostatak matrice, već možemo preći na
traženje sljedećeg elementa. Radi pogodnosti ispisa izabrali smo da se u svakom redu ispišu po
dvije karte. U tu svrhu smo nakon svake neparne karte odštampali tabulator (\t), dok smo nakon
svake parne karte odštampali sekvencu za prelaz u novi red (\n).
Izvršavanje programa:
Desetka Pik Sestica Herc
Kraljica Tref Cetvorka Tref
As Pik Pub Karo
Sedmica Pik Devetka Karo
Petica Karo Trojka Karo
Kralj Pik Kraljica Herc
Petica Tref Desetka Karo
As Tref Pub Pik
As Herc Osmica Karo
Devetka Pik Osmica Tref
Kralj Herc Trojka Pik
Pub Herc Pub Tref
Osmica Pik Trojka Herc
Kraljica Karo Sedmica Karo
Devetka Tref Osmica Herc
Cetvorka Pik Devetka Herc
2.14 IGRE 291
Zadatak 6. Napisati program kojim se simulira prolazak kroz lavirint. Lavirint je predstavljen
pomoću matrice čiji su elementi karakteri '#' i '.', pri čemu tarabe predstavljaju
zidove, a tačke hodnike lavirinta. Kretanje kroz lavirint se može vršiti samo duž
hodnika. Mi ćemo posmatrati lavirint dimenzija 8×8 karaktera prikazan u postavci.
Prilikom prolaska, pridržavati se algoritma koji garantuje izlaz iz lavirinta, ukoliko
izlaz postoji. Algoritam se može opisati na sljedeći način. Prvo, stavimo desnu ruku
na zid lavirinta sa naše desne strane i započnimo kretanje. Prilikom kretanja ne
sklanjati ruku sa zida. Pridržavajući se ovog algoritma sigurno ćemo naći izlaz iz
lavirinta. Program treba da sadrži rekurzivnu funkciju koja simulira prolazak kroz
lavirint pridržavajući se opisanog algoritma, kao i funkciju koja crta lavirint nakon
svakog pomjeraja duž hodnika.
############
# . . . #. . . . . .#
. .# . #. ####.#
### . #. . . . #.#
# . . . . ###. #. .
#### . #. #. #.#
# . .# . #. #. #.#
## .# . #. #. #.#
# . . . . . . . . #.#
###### . ###.#
# . . . . . . #. . .#
############
#include<stdio.h>
char lavirint[12][13]=
{"############",
"#...#......#",
"..#.#.####.#",
"###.#....#.#",
"#....###.#..",
"####.#.#.#.#",
"#..#.#.#.#.#",
"##.#.#.#.#.#",
"#........#.#",
"######.###.#",
292 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
"#......#...#",
"############"};
int xUl,yUl,xIz,yIz,smjer;
int main()
{
stampajLavirint(lavirint,‐1,‐1);
do
{
printf("Unijeti koordinate tacke od koje se krece: ");
scanf("%d%d",&xUl,&yUl);
} while(lavirint[xUl][yUl]=='#');
do
{
printf("Unijeti koordinate izlaza iz lavirinta: ");
scanf("%d%d",&xIz,&yIz);
} while(lavirint[xIz][yIz]=='#');
do
{
printf("Pocetni smjer kretanja: 1‐desno,2‐lijevo,3‐gore,4‐dolje");
scanf("%d",&smjer);
} while(smjer>4||smjer<1);
obilazak(lavirint,xUl,yUl);
}
switch(smjer)
{
case 1:
if(a[x+1][y]=='.')
{
smjer=4;
obilazak(a,x+1,y);
}
else if(a[x][y+1]=='.')
obilazak(a,x,y+1);
else if(a[x‐1][y]=='.')
{
smjer=3;
obilazak(a,x‐1,y);
}
else
{
smjer=2;
obilazak(a,x,y‐1);
}
break;
case 2:
if(a[x‐1][y]=='.')
{
smjer=3;
obilazak(a,x‐1,y);
}
else if(a[x][y‐1]=='.')
obilazak(a,x,y‐1);
else if(a[x+1][y]=='.')
{
smjer=4;
obilazak(a,x+1,y);
}
else
{
smjer=1;
obilazak(a,x,y+1);
}
break;
case 3:
if(a[x][y+1]=='.')
{
smjer=1;
obilazak(a,x,y+1);
}
else if(a[x‐1][y]=='.')
obilazak(a,x‐1,y);
else if(a[x][y‐1]=='.')
294 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA
{
smjer=2;
obilazak(a,x,y‐1);
}
else
{
smjer=4;
obilazak(a,x+1,y);
}
break;
case 4:
if(a[x][y‐1]=='.')
{
smjer=2;
obilazak(a,x,y‐1);
}
else if(a[x+1][y]=='.')
obilazak(a,x+1,y);
else if(a[x][y+1]=='.')
{
smjer=1;
obilazak(a,x,y+1);
}
else
{
smjer=3;
obilazak(a,x‐1,y);
}
break;
default:
break;
}
}
Globalne promjenljive:
lavirint matrica karaktera koja predstavlja lavirint
xUl, yUl koordinate početne tačke kretanja
xIz, yIz koordinate izlaza
smjer cjelobrojna konstanta koja predstavlja smjer kretanja
Promjenljive korišćene u funkciji stampajLavirint:
a matrica karaktera koja predstavlja lavirint
x, y cjelobrojne promjenljive koje predstavljaju koordinate tekuće pozicije
prilikom kretanja kroz lavirint
i, j pomoćne brojačke promjenljive
Promjenljive korišćene u funkciji obilazak:
a matrica karaktera koja predstavlja lavirint
2.14 IGRE 295
Zadatak 7. Napisati program koji simulira igru pogađanja sekvence od 4 cijela broja, pri čemu
su brojevi iz skupa {1,2,3,4,5,6}. Naime, program prvo generiše slučajnu sekvencu
brojeva iz datog skupa, pri čemu su dozvoljena ponavljanja brojeva u okviru
sekvence. Sekvenca se generiše u formi stringa. Neki primjeri slučajnih sekvenci
su 1231, 2216, 5436, 3334 itd. Zatim korisnik (igrač) nagađa generisanu sekvencu
unoseći string dužine 4 karaktera. Nakon unosa sekvence, program poredi
generisanu i unesenu sekvencu brojeva, i ispisuje koliko je cifara pogođeno i koliko
od njih se nalazi na pravoj poziciji. Korisnik ima mogućnost da 10 puta nagađa
sekvencu. Nagađanje se završava ukoliko je korisnik pogodio generisanu sekvencu
ili ukoliko je bezuspješno iskoristio svih 10 mogućnosti, nakon čega se korisnik
pita da li želi da prekine ili nastavi igru nagađanjem nove sekvence.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int i,j,pokusaj,p1,p2;
char genSek[5],nagSek[5],nast='d';
srand(time(0));
while(nast=='d')
{
for(i=0;i<4;i++)
genSek[i]=1+rand()%6+'0';
genSek[i]='\0';
pokusaj=1;
while(pokusaj<=10)
{
p1=p2=0;
printf("Unijeti sekvencu. Imate jos %d pokusaja ",10‐pokusaj+1);
scanf("%s",nagSek);
if(strcmp(nagSek,genSek)==0)
2.14 IGRE 297
{
printf("Pogodak!\n");
break;
}
for(i=0;i<4;i++)
if(nagSek[i]==genSek[i])
p1++;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
if(nagSek[j]==genSek[i])
{
p2++;
nagSek[j]='0';
break;
}
printf("Imate %d %s,od toga %d na pravoj poziciji\n",
p2,(p2>1?"pogotka":"pogodak"),p1);
pokusaj++;
}
if(pokusaj>10)
printf("Bezuspjesno ste iskoristili svih 10 pokusaja\n");
do
{
printf("Zelite li novu igru?\t n ‐ ne\t d ‐ da: ");
scanf("%*c%c",&nast);
} while(nast!='d'&&nast!='n');
}
}
Čitava igra je realizovana u okviru while petlje koja se izvršava sve dok nast ima
vrijednost 'd'. Na početku te petlje se generiše sekvenca od 4 broja iz skupa {1,2,3,4,5,6} i to
u obliku stringa. Rastumačiti zapis genSek[i]=1+rand()%6+'0'. Nakon toga se ulazi u
while petlju koja se izvršava sve dok je redni broj pokušaja, tj. vrijednost promjenljive
pokusaj, manji ili jednak 10. Prvo se od korisnika traži unos sekvence, takođe u obliku stringa,
i vrši se poređenje generisane i nagađane sekvence funkcijom strcmp. Ukoliko su odgovarajući
stringovi identični, ispisuje se odgovarajuća poruka i izlazi se iz unutrašnje while petlje
naredbom break. Ukoliko to nije slučaj, ide se na određivanje koliko je brojeva pogođeno i
koliko od njih se nalazi na pravim pozicijama. Prva for petlja poredi stringove, karakter po
karakter, i svaki put kad se naiđe na poklapanje karaktera, tj. brojeva sekvence, inkrementira se
promjenljiva p1, čija je početna vrijednost 0 postavljena na početku spoljašnje while petlje.
Nakon završetka te for petlje, p1 sadrži podatak o broju pogodaka na pravoj poziciji. Ukupan
broj pogodaka, bez obzira da li se nalaze na pravoj poziciji, se određuje korišćenjem dvije
ugnježđene for petlje, pri čemu se spoljašnja kreće po elementima generisane sekvence, a
unutrašnja po elementima nagađane sekvence. Onoga trenutka kada ustanovimo da se određeni
broj iz nagađane sekvence nalazi u generisanoj sekvenci inkrementiramo p2, a zatim na poziciji
pronađenog karaktera u nagađanoj sekvenci upišemo karakter '0', kako taj pronađeni karakter
više ne bi uzimali u obzir prilikom pretrage. Nakon izvršenja ovih petlji idemo na ispisivanje
poruke o tome koliko ima pogodaka i koliko se nalazi na pravoj poziciji. Na kraju spoljašnje
while petlje korisnik se pita da li želi da nastavi igru pogađanjem nove sekvence, što postiže
unošenjem karaktera 'd', ili da prekine igre unošenjem karaktera 'n'.
Izvršenje programa:
Unijeti sekvencu. Imate jos 10 pokusaja 2233
Imate 0 pogodak,od toga 0 na pravoj poziciji
Unijeti sekvencu. Imate jos 9 pokusaja 1144
Imate 2 pogotka,od toga 1 na pravoj poziciji
Unijeti sekvencu. Imate jos 8 pokusaja 1455
Imate 2 pogotka,od toga 0 na pravoj poziciji
Unijeti sekvencu. Imate jos 7 pokusaja 6614
Pogodak!
Zelite li novu igru? n ‐ ne d ‐ da: n