226
Programiranje 2 Programski jezik C — Zadaci sa veˇ zbi — Milena Vujoˇ sevi´ c - Janiˇ ci´ c 2008/2009

Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Embed Size (px)

Citation preview

Page 1: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Programiranje 2Programski jezik C

— Zadaci sa vezbi —

Milena Vujosevic - Janicic 2008/2009

Page 2: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Predgovor

Ovo je prateci materijal za vezbe koje drzim iz predmenta Programiranje 2. On nemoze zameniti pohadanje vezbi niti koriscenje druge preporucene literature.

Deo materijala cine zadaci i resenja mr Filipa Marica (raspolozivi nawww.matf.bg.ac.yu/~filip/pp/0405/index.pl).Takode koriscen je i materijal sa sajta koleginice Jelene Grmusewww.matf.bg.ac.yu/~jelenagr

kolege Miroslava Maricawww.matf.bg.ac.yu/~maricm

i kolege Milana Bankovicawww.matf.bg.ac.yu/~milan.Tekstovi i objasnjenja su uglavnom zasnovani na knjizi Programski jezik C, autoraKerninghan & Ritchie.

Zahvaljujem svojim studentima na aktivnom ucescu u nastavi cime su mi pomogliu uoblicavanju ovog materijala.

Svi komentari i sugestije vezane za ovaj materijal bice veoma dobrodosli.

Milena Vujosevic-Janicicwww.matf.bg.ac.yu/~milena

1

Page 3: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Sadrzaj

1 Argumenti komandne linije 4

2 Datoteke 7

3 Bitski operatori 19

4 Vezbanje 304.1 Polinomi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304.2 Veliki brojevi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

5 Sortiranje 405.1 Vremenska slozenost . . . . . . . . . . . . . . . . . . . . . . . . . . . 405.2 Selection sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415.3 Bubble sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435.4 Insertion sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455.5 Razni zadaci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

6 Pokazivaci 586.1 Pokazivacka aritmetika — primeri . . . . . . . . . . . . . . . . . . . . 596.2 Niska karaktera i pokazivac na konstantnu nisku . . . . . . . . . . . . 646.3 Pokazivaci na pokazivace . . . . . . . . . . . . . . . . . . . . . . . . . 656.4 Nizovi pokazivaca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

7 Rekurzija 697.1 Osnovni primeri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707.2 Binarna pretraga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817.3 MergeSort algoritam . . . . . . . . . . . . . . . . . . . . . . . . . . . 827.4 QuickSort algoritam . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

8 Pokazivaci na funkcije 868.1 Funkcija qsort iz standardne biblioteke . . . . . . . . . . . . . . . . . 898.2 Funkcija bsearch iz standardne biblioteke . . . . . . . . . . . . . . . 938.3 Funkcije lsearch i lfind . . . . . . . . . . . . . . . . . . . . . . . . 978.4 Pokazivaci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

2

Page 4: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

SADRZAJ SADRZAJ

9 Dinamicka alokacija memorije 999.1 Matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059.2 Realokacija memorije . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

10 Liste 11810.1 Implementacija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11810.2 Kruzna (ciklicna) lista . . . . . . . . . . . . . . . . . . . . . . . . . . 14310.3 Red . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14410.4 Stek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15110.5 Dvostruko povezane liste . . . . . . . . . . . . . . . . . . . . . . . . . 160

11 Stabla 16611.1 Binarno pretrazivacko stablo . . . . . . . . . . . . . . . . . . . . . . . 167

12 Grafovi 209

3

Page 5: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 1

Argumenti komandne linije

Primer 1.1 Argumenti komandne linije programa.

/* Program pozivati sa npr.:

a.out

a.out prvi

a.out prvi drugi treci

a.out -a -bc ime.txt

*/

#include <stdio.h>

/* Imena ovih promenljivih mogu biti proizvoljna.

Npr:

main (int br_argumenata, char** argumenti);

ipak, uobicajeno je da se koriste sledeca imena:

main(int argc, char** argv)

Cesto se koristi i sledeca (ekvivalentna) notacija

main(int argc, char* argv[])

*/

main(int argc, char* argv[])

{

int i;

printf("argc = %d\n", argc);

/* Nulti argument uvek je ime programa (a.out)*/

for (i = 0; i<argc; i++)

printf("argv[%d] = %s\n", i, argv[i]);

}

Primer 1.2 Program ispisuje opcije navedene u komandnoj liniji.

/* Opcije se navode koriscenjem znaka -, pri cemu je moguce da

4

Page 6: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Argumenti komandne linije

iza jednog znaka - sledi i nekoliko opcija.

Npr. za

a.out -abc -d -fg dat.txt -vxy

su prisutne opcije a b c d f g */

#include <stdio.h>

main(int argc, char** argv)

{

/* Za svaki argument komande linije, pocevsi od argv[1]

(preskacemo ime programa) */

int i;

for (i = 1; i < argc; i++)

{

/* Ukoliko i-ti argument pocinje crticom */

if (argv[i][0] == ’-’)

{

/* Ispisujemo sva njegova slova pocevsi od pozicije 1 */

int j;

for (j = 1; argv[i][j] != ’\0’; j++)

printf("Prisutna je opcija : %c\n", argv[i][j]);

}

/* Ukoliko ne pocinje crticom, prekidamo */

else

break;

}

}

Izlaz:

Prisutna opcija : a

Prisutna opcija : b

Prisutna opcija : c

Prisutna opcija : d

Prisutna opcija : f

Prisutna opcija : g

Primer 1.3 Program ispisuje opcije navedene u komandnoj liniji. K&R resenje.

#include <stdio.h>

/* Resnje se intenzivno zasniva na pokazivackoj aritmetici

i prioritetu operatora */

main(int argc, char** argv)

{

char c;

/* Dok jos ima argumenata i dok je karakter na poziciji

0 upravo crtica */

while(--argc>0 && (*++argv)[0]==’-’)

5

Page 7: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Argumenti komandne linije

/* Dok god ne dodjemo do kraja tekuceg stringa */

while (c=*++argv[0])

printf("Prisutna opcija : %c\n",c);

}

6

Page 8: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 2

Datoteke

Primer 2.1 Program demonstrira otvaranje datoteka ("r" - read i "w" - write mod)i osnovne tehnike rada sa datotekama.

/* U datoteku se upisuje prvih 10 prirodnih

brojeva, a zatim se iz iste datoteke

citaju brojevi dok se ne stigne do kraja i

ispisuju se na standardni izlaz */

#include <stdio.h>

#include <stdlib.h>

main()

{

int i;

/* Otvaramo datoteku sa imenom podaci.txt za pisanje */

FILE* f = fopen("podaci.txt", "w");

/* Ukoliko otvaranje nije uspelo, fopen vraca NULL.

U tom slucaju, prijavljujemo gresku i zavrsavamo program */

if (f == NULL)

{

printf("Greska prilikom otvaranja datoteke podaci.txt za pisanje\n");

exit(1);

}

/* Upisujemo u datoteku prvih 10 prirodnih brojeva

(svaki u posebnom redu) */

for (i = 0; i<10; i++)

fprintf(f, "%d\n", i);

/* Zatvaramo datoteku */

7

Page 9: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

fclose(f);

/* Otvaramo datoteku sa imenom podaci.txt za citanje */

f = fopen("podaci.txt", "r");

/* Ukoliko otvaranje nije uspelo, fopen vraca NULL.

U tom slucaju, prijavljujemo gresku i zavrsavamo program */

if (f == NULL) {

printf("Greska prilikom otvaranja datoteke podaci.txt za citanje\n");

exit(1);

}

/* Citamo brojeve iz datoteke dok ne stignemo do kraja i ispisujemo ih

na standardni izlaz */

while(1) {

int br;

/* Pokusavamo da procitamo broj */

fscanf(f, "%d", &br);

/* Ukoliko smo dosli do kraja datoteke, prekidamo */

if (feof(f))

break;

/* Ispisujemo procitani broj */

printf("Procitano : %d\n", br);

}

/* Funkciju feof ne treba pozivati pre pokusaja citanja.

Sledeci kod moze dovesti do greske:

while (!feof(f))

fscanf(f,"%d",&br);

*/

/* Zatvaramo datoteku */

fclose(f);

}

Pokazivaci stdin, stdout i stderr su definisani u okviru stdio.h i mogu sekoristiti za pisanje na standardni ulaz, izlaz i izlaz za gresku.

FILE* stdin;

FILE* stdout;

FILE* stderr;

Primer 2.2 Program demonstrira ”a” - append mod datoteka - nadovezivanje.

#include <stdio.h>

8

Page 10: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

#include <stdlib.h>

main()

{

FILE* datoteka;

/* Otvaramo datoteku za nadovezivanje

i proveravamo da li je doslo do greske */

if ( (datoteka=fopen("dat.txt","a"))==NULL)

{

/*Funkcija fprintf u odnosu na funkciju printf ima

jedan argument vise --- prvi argument je pokazivac

na datoteku u koju treba upisati poruku*/

fprintf(stderr,"Greska prilikom otvaranja dat.txt\n");

/*exit je funkcija iz biblioteke stdlib koja omogucava

trenutno zaustavljanje rada programa*/

exit(1);

}

/* Upisujemo sadrzaj u datoteku */

fprintf(datoteka,"Zdravo svima\n");

/* Zatvaramo datoteku */

fclose(datoteka);

}

Primer 2.3 Program kopira sadrzaj datoteke ulaz.txt u datoteku izlaz.txt.

#include <stdio.h>

#include <stdlib.h>

main()

{

int c;

FILE *in, *out;

if ((in = fopen("ulaz.txt","r")) == NULL)

{

fprintf(stderr, "Neuspesno otvaranje datoteke ulaz.txt\n");

exit(1);

}

if ((out = fopen("izlaz.txt","w")) == NULL)

{

fprintf(stderr, "Neuspesno otvaranje datoteke izlaz.txt\n");

9

Page 11: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

exit(1);

}

/*Funkcija fgetc ucitava iz datoteke jedan karakter.

Funkcija fputc upisuje jedan karakter u datoteku. */

while ((c=fgetc(in)) != EOF)

fputc(c,out);

/* Zatvaramo datoteke */

fclose(in);

fclose(out);

}

Primer 2.4 Program kopira n puta sadrzaj datoteke ulaz.txt u datoteku izlaz.txt.

#include <stdio.h>

#include <stdlib.h>

main()

{

int c, n;

FILE *in, *out;

if ((in = fopen("ulaz.txt","r")) == NULL)

{

fprintf(stderr, "Neuspesno otvaranje datoteke ulaz.txt\n");

exit(1);

}

if ((out = fopen("izlaz.txt","w")) == NULL)

{

fprintf(stderr, "Neuspesno otvaranje datoteke izlaz.txt\n");

exit(1);

}

printf("Unesi prirodan broj\n");

scanf("%d", &n);

for(i=0; i<n; i++)

{

/* Prepisuje se sadrzaj datoteke*/

while ((c=fgetc(in)) != EOF)

fputc(c,out);

/* in se pozicionira na pocetak datoteke*/

rewind(in);

10

Page 12: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

}

/* Zatvaramo datoteke */

fclose(in);

fclose(out);

}

Primer 2.5 Program ilustruje rad sa datotekama. Program kopira datoteku cije seime zadaje kao prvi argument komandne linije u datoteku cije se ime zadaje kaodrugi argument komandne linije. Uz svaku liniju se zapisuje i njen redni broj.

#include <stdio.h>

#include <stdlib.h>

#define MAX_LINE 256

/* Funkcija fgets definisana je u stdio.h

char* fgets(char *s, int n, FILE* stream)

fgets ucitava najvise sledecih n-1 znakova

u polje niza karaktera s, zaustavljajuci se

ako naidje na novu liniju koju takodje

upisuje u polje. Na kraju upisuje ’\0’.

Funkcija vraca s ili NULL ako dodje do kraja

datoteke ili se pojavi greska.

Funkcija fputs definisana je u stdio.h

int fputs(char *s, FILE *stream)

fputs upisuje nisku s u datoteku na koju

pokazuje stream, vraca nenegativnu vrednost ili

EOF ako se pojavi greska

*/

main(int argc, char* argv[])

{

char line[MAX_LINE];

FILE *in, *out;

int line_num;

if (argc != 3) {

fprintf(stderr,"Neispravna upotreba programa!\n

Pravilna upotreba:

%s ulazna_datoteka izlazna_datoteka\n",argv[0]);

11

Page 13: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

exit(1);

}

if ((in = fopen(argv[1],"r")) == NULL)

{

fprintf(stderr, "Neuspesno otvaranje datoteke %s\n", argv[1]);

exit(1);

}

if ((out = fopen(argv[2],"w")) == NULL)

{

fprintf(stderr, "Neuspesno otvaranje datoteke %s\n", argv[2]);

exit(1);

}

line_num = 1;

/* Citamo liniju po liniju sa ulaza*/

while (fgets(line, MAX_LINE, in) != NULL)

{

/* Ispisujemo broj linije i sadrzaj linije na izlaz */

fprintf(out, "%d :\t", line_num++);

fputs(line, out);

}

/* Zatvaramo datoteke */

fclose(in);

fclose(out);

}

Primer 2.6 Prodavnica - ilustruje citanje niza struktura iz tektsualne datoteke.

/* Datoteka, cije se ime zadaje kao argument komandne linije

ili ako se ne zada onda se ime unosi sa standardnog

ulaza, sadrzi podatke o proizvodima koji se prodaju

u okviru odredjene prodavnice. Svaki

proizvod se odlikuje sledecim podacima:

bar-kod - petocifreni pozitivan broj

ime - niska karaktera

cena - realan broj zaokruzen na dve decimale

pdv - stopa poreza - realan broj zaokruzen na dve decimale

Pretpostavljamo da su podaci u datoteci

korektno zadati.

Pretpostavljamo da se u prodavnici ne

prodaje vise od 1000 razlicitih artikala

Na standardni izlaz ispisujemo podatke o

12

Page 14: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

svim proizvodima koji se prodaju u prodavnici.

Zadatak je moguce resiti i bez koriscenja nizova

(i takvo resenje je bolje). */

#include <stdio.h>

#include <stdlib.h>

/* Maksimalna duzina imena proizvoda */

#define MAX_IME 30

/* Maksimalni broj artikala */

#define MAX_ARTIKALA 1000

/* Struktura za cuvanje podataka o jednom artiklu */

typedef struct _artikal {

int bar_kod;

char ime[MAX_IME];

float cena;

float pdv;

} artikal;

/* Ucitava podatke o jednom artiklu iz date datoteke.

Vraca da li su podaci uspesno procitani */

int ucitaj_artikal(FILE* f, artikal* a)

{

/* Citamo bar kod, ime, cenu, pdv */

fscanf(f, "%d%s%f%f", &(a->bar_kod), a->ime, &(a->cena), &(a->pdv));

/* Ukoliko smo dosli do kraja datoteke prilikom pokusaja ucitavanja

prijavljujemo neuspeh */

if (feof(f))

return 0;

/* Prijavljujemo uspeh */

return 1;

}

/* Izracunava ukupnu cenu datog artikla */

float cena(artikal a)

{

return a.cena*(1+a.pdv);

}

13

Page 15: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

/* Ispisuje podatke o svim artiklima */

void ispisi_artikle(artikal artikli[], int br_artikala)

{

int i;

for (i = 0; i<br_artikala; i++)

printf("%-5d %-10s %.2f %.2f = %.2f\n",

artikli[i].bar_kod, artikli[i].ime,

artikli[i].cena, artikli[i].pdv,

cena(artikli[i]));

}

main(int argc, char* argv[])

{

FILE* f;

/* Niz struktura u kome se cuvaju podaci o artiklima */

artikal artikli[MAX_ARTIKALA];

/* Broj trenutno ucitanih artikala */

int br_artikala;

/* Ukoliko nije navedeno ime kao argument komandne linije, trazimo

od korisnika da ga unese */

if (argc<2) {

/* Ucitavamo ime datoteke */

char ime_datoteke[256];

printf("U kojoj datoteci se nalaze podaci o proizvodima: ");

scanf("%s", ime_datoteke);

/* Otvaramo datoteku i proveravamo da li smo uspeli */

if ( (f = fopen(ime_datoteke, "r")) == NULL)

{

printf("Greska prilikom otvaranja datoteke %s\n", ime_datoteke);

exit(1);

}

}

/* Ime datoteke je prvi argument komandne linije */

else {

/* Otvaramo datoteku i proveravamo da li smo uspeli */

if ( (f = fopen(argv[1], "r")) == NULL)

{

printf("Greska : datoteka %s ne moze biti otvorena\n", argv[1]);

exit(1);

}

14

Page 16: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

}

br_artikala=0;

/* Ucitavamo artikle */

while (ucitaj_artikal(f, &artikli[br_artikala]))

br_artikala++;

/* Ispisujemo podatke o svim artiklima */

ispisi_artikle(artikli, br_artikala);

/* Zatvara se datoteka*/

fclose(f);

}

Primer 2.7 Program u datoteku cije se ime zadaje kao prvi argument komandnelinije upisuje prvih N prirodnih brojeva (N se zadaje sa standardnog ulaza) i zatimizracunava duzinu datoteke u bajtovima.

#include <stdio.h>

#include <stdlib.h>

/*Deklaracija funkcije za racunanje duzine datoteke*/

long filesize(FILE *f);

void main()

{

FILE *f;

int i, n;

/* Ukoliko nije navedeno ime datoteke kao

argument komandne linije, zavrsavamo rad programa*/

if (argc<2)

{

printf("Program se pokrece sa a.out ime_datoteke!\n");

exit(1);

}

/* Otvaramo datoteku i proveravamo da li smo uspeli */

if ( (f = fopen(argv[1], "w+")) == NULL)

{

printf("Greska: datoteka %s ne moze biti otvorena\n", argv[1]);

exit(1);

}

printf("Unesi prirodan broj\n");

scanf("%d", &n);

15

Page 17: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

/* Upisuje se sadrzaj u datoteku*/

for(i=0; i<n; i++)

fprintf(f, "%d ", i);

printf("Velicina datoteke je %ld bajtova\n", filesize(f));

fclose(f);

}

/*Funkcija koja racuna duzinu datoteke*/

long filesize(FILE *f)

{

long trenutna_pozicija, duzina;

/*Funkcija ftell vraca trenutnu poziciju u datoteci*/

/*Pamti se trenutna pozicija da bismo

mogli na nju da se vratimo*/

trenutna_pozicija = ftell(f);

/* Funkcija fseek se pozicionira na odredjeno

mesto u datoteci i ima sledece argumente

1. FILE* f - datoteka sa kojom se radi

2. int offset - rastojanje u bajtovima

od odgovarajuce pozicije

3. int origin - pozicija u odnosu na koju se gleda offset

i moze da ima vrednost jedne od

sledecih konstanti

SEEK_END - kraj

SEEK_SET - pocetak

SEEK_CUR - trenutna pozicija */

/*pozicioniranje na kraj fajla*/

fseek(f, 0, SEEK_END);

/*uzimamo poziciju u broju bajtova od

pocetka fajla, to je duzina*/

duzina = ftell(f);

/*vracamo se nazad na zapamcenu poziciju*/

fseek(f, trenutna_pozicija, SEEK_SET);

return duzina;

}

Primer 2.8 Napisati program koji u datoteci cije se ime zadaje kao prvi argument

16

Page 18: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

komandne linije zamenjuje sva pojavljivanja niske koja se zadaje kao drugi argumentkomandne linije niskom koja se zadaje kao treci argument komandne linije (pret-postaviti da ce obe niske iste duzine i da nisu duze od 20 karaktera). Na primer, akose program pokrene saa.out datoteka.txt ana Ana

tada se svako pojavljivanje reci ana u datoteci datoteka.txt zamenjuje sa Ana.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main(int argc, char* argv[])

{

FILE *f;

int c;

int i;

long pos;

int duzina;

char rec1[20];

char rec2[20];

/* Ukoliko nisu navedeni odgovarajuci

argumenti program se zavrsava */

if (argc<4)

{

printf("Program se pokrece sa a.out datoteka rec zamena!\n");

exit(1);

}

/* Otvaramo datoteku i proveravamo da li smo uspeli */

if ( (f = fopen(argv[1], "r+")) == NULL)

{

printf("Greska: datoteka %s ne moze biti otvorena\n", argv[1]);

exit(1);

}

/*Prepisujemo argumente komandne linije u rec1 i rec2*/

strcpy(rec1, argv[2]);

strcpy(rec2, argv[3]);

while ((c = fgetc(f))!=EOF)

{

/*ako smo naisli na prvi karakter niske*/

if (c == rec1[0])

{

17

Page 19: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Datoteke

pos = ftell(f); /*zapamtimo trenutnu poziciju*/

duzina = strlen(rec1);

/*petlja koja prepoznaje rec*/

for(i=1; i<duzina; i++)

/*ako se karakteri ne poklapaju izlazimo iz petlje

if (rec1[i]!=fgetc(f))

break;

/*ako je bila pronadjena cela rec prepisujemo

je zamenom*/

if (i==duzina)

{

/*vracamo se na pocetak reci da bismo je prepisali

fseek(f,pos-1,SEEK_SET);

for(i=0; rec2[i]; i++)

fputc(rec2[i], f);

fseek(f,0,SEEK_CUR); /*pozicionira na tu poziciju

(jer inace fputc smatra da upisuje

na kraj fajla pa fseek pocisti taj

fleg*/

}

else

/*vracamo se na pocetak reci ali iza prvog slova*/

fseek(f, pos, SEEK_SET);

}

}

fclose(f);

}

18

Page 20: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 3

Bitski operatori

Bitski operatori su operatori koji deluju nad bitovima i vazno je razlikovati ih odlogickih operatora.

& bitsko AND

| bitsko OR

^ bitsko ekskluzivno OR

<< levo pomeranje

>> desno pomeranje

~ jedinicni komplement

Primer 3.1 Demonstracija bitskih operatora

#include <stdio.h>

main()

{ printf("%o %o\n",255,15);

printf( "255 & 15 = %d\n", 255 & 15 );

printf( "255 | 15 = %d\n", 255 | 15 );

printf( "255 ^ 15 = %d\n", 255 ^ 15 );

printf( "2 << 2 = %d\n", 2 << 2 );

printf( "16 >> 2 = %d\n", 16 >> 2 );

}

Izlaz iz programa je:

377 17

255 & 15 = 15

255 | 15 = 255

255 ^ 15 = 240

2 << 2 = 8

16 >> 2 = 4

Primer 3.2 print bits - stampa bitove u zapisu datog celog broja x.

#include <stdio.h>

19

Page 21: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

/* Funkcija stampa bitove datog celog broja x.

Vrednost bita na poziciji i je 0 ako i

samo ako se pri konjunkciji broja x sa maskom

000..010....000 - sve 0 osim 1 na poziciji i,

dobija 0.

Funkcija krece od pozicije najvece tezine

kreirajuci masku pomeranjem jedinice u levo

za duzina(x) - 1 mesto, i zatim pomerajuci

ovu masku za jedno mesto u levo u svakoj

sledecoj iteraciji sve dok maska ne postane 0.

*/

void print_bits(int x)

{

/* Broj bitova tipa unsigned */

int wl = sizeof(int)*8;

unsigned mask;

for (mask = 1<<wl-1; mask; mask >>= 1)

putchar(x&mask ? ’1’ : ’0’);

putchar(’\n’);

}

main()

{

print_bits(127);

print_bits(128);

print_bits(0x00FF00FF);

print_bits(0xFFFFFFFF);

}

Izlaz iz programa:

00000000000000000000000001111111

00000000000000000000000010000000

00000000111111110000000011111111

11111111111111111111111111111111

Primer 3.3 Program proverava da li se na k-tom mestu broja n nalazi bit koji imavrednost 1.

#include <stdio.h>

/* Pozicije brojimo kao u sledecem primeru:

poz: ... 10 9 8 7 6 5 4 3 2 1 0

20

Page 22: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

bit: ... 0 0 0 1 1 1 0 0 1 1 0 */

main()

{

int n,k;

printf("Unesite broj i poziciju tog broja koju zelite da proverite:\n");

scanf("%d%d",&n,&k);

if ((n & (1 << k))!=0)

printf("Bit je 1\n");

else

printf("Bit je 0\n");

}

Primer 3.4 Program postavlja na k-to mesto broja n bit 1.

#include <stdio.h>

void print_bits(int x)

{

/* Broj bitova tipa unsigned */

int wl = sizeof(int)*8;

unsigned mask;

for (mask = 1<<wl-1; mask; mask >>= 1)

putchar(x&mask ? ’1’ : ’0’);

putchar(’\n’);

}

main(){

int n,k;

printf("Unesite broj i poziciju tog broja koju zelite da postavite na 1:\n");

scanf("%d%d",&n,&k);

printf("Binarno, uneseni broj je\n");

print_bits(n);

printf("Novi broj je %d\n",(n |(1<<k)));

printf("Binarno, novi broj je\n");

print_bits((n |(1<<k)));

}

Izrazom a>>b vrsi se pomeranje sadrzaja operanda a predstavljenog u binarnomobliku za b mesta u desno. Popunjavanje upraznjenih mesta na levoj strani zavisi odtipa podataka i vrste racunara. Ako se pomeranje primenjuje nad operandom tipaunsigned popunjavanje je nulama. Ako se radi o oznacenom operandu popunjavanjeje jedinicama kada je u krajnjem levom bitu jedinica, a nulama kada je u krajnjemlevom bitu nula.

21

Page 23: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

Da li vazi sledeca jednakost (za prirodan broj k):((~0) >> k) == (~0)?Posto je 0 konstanta celobrojnog tipa, to je ~0 takodje tipa int, pri cemu je tobroj koji je zapisan svim jedinicama. Prema tome, njegovim siftovanjem u desnodobijamo isti taj broj, jer se upraznjen prostor sa desne strane popunjava jedinicama.

VAZNO:Vrednost izraza a<<b je jednaka vrednosti a · 2b.Vrednost izraza a>>b je jednaka vrednosti a/2b.Bitovski operatori su efikasniji od operacija * i / tako da svaki put kad se javipotreba za mnozenjem ili deljenjem stepenom broja dva, treba koristiti operacije <<i >>.

Primer 3.5 sum of bits - izracunava sumu bitova datog neoznacenog broja.

#include <stdio.h>

/* Pomocna funkcija - stampa bitove neoznacenog broja */

void print_bits(unsigned x)

{

int wl = sizeof(unsigned)*8;

unsigned mask;

for (mask = 1<<wl-1; mask; mask >>= 1)

putchar(x&mask ? ’1’ : ’0’);

putchar(’\n’);

}

int sum_of_bits(unsigned x)

{

int br;

for (br = 0; x; x>>=1)

if (x&1)

br++;

/* Drugo resenje:

int wl = sizeof(unsigned)*8;

int br = 0;

unsigned mask;

for (mask = 1<<wl-1; mask; mask >>= 1)

if(x&mask) br++ ;

*/

return br;

}

22

Page 24: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

main()

{

printf("Binarni zapis broja 127 je\n");

print_bits(127);

printf("Suma bitova broja 127 je %d\n",sum_of_bits(127));

printf("Binarni zapis broja 128 je\n");

print_bits(128);

printf("Suma bitova broja 128 je %d\n",sum_of_bits(128));

printf("Binarni zapis broja 0x00FF00FF je\n");

print_bits(0x00FF00FF);

printf("Suma bitova broja 0x00FF00FF je %d\n",sum_of_bits(0x00FF00FF));

printf("Binarni zapis broja 0xFFFFFFFF je\n");

print_bits(0xFFFFFFFF);

printf("Suma bitova broja 0xFFFFFFFF je %d\n",sum_of_bits(0xFFFFFFFF));

}

Izlaz iz programa:

Binarni zapis broja 127 je

00000000000000000000000001111111

Suma bitova broja 127 je 7

Binarni zapis broja 128 je

00000000000000000000000010000000

Suma bitova broja 128 je 1

Binarni zapis broja 0x00FF00FF je

00000000111111110000000011111111

Suma bitova broja 0x00FF00FF je 16

Binarni zapis broja 0xFFFFFFFF je

11111111111111111111111111111111

Suma bitova broja 0xFFFFFFFF je 32

Primer 3.6 get_bits, set_bits, invert_bits - izdvajanje, postavljanje i inver-tovanje pojedinacnih bitova.

#include <stdio.h>

/* Pomocna funkcija - stampa bitove neoznacenog broja */

void print_bits(unsigned x)

{

int wl = sizeof(unsigned)*8;

unsigned mask;

for (mask = 1<<wl-1; mask; mask >>= 1)

putchar(x&mask ? ’1’ : ’0’);

putchar(’\n’);

}

23

Page 25: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

/* Funkcija vraca n bitova broja x koji pocinju na poziciji p */

unsigned get_bits(unsigned x, int p, int n)

{

/* Gradimo masku koja ima poslednjih n jedinica

0000000...00011111

tako sto sve jedinice ~0 pomerimo u levo za n mesta

1111111...1100000

a zatim komplementiramo

*/

unsigned last_n_1 = ~(~0 << n);

/* x pomerimo u desno za odgovarajuci broj mesta, a zatim

konjunkcijom sa konstruisanom maskom obrisemo pocetne cifre */

return (x >> p+1-n) & last_n_1;

}

/* Funkcija vraca modifikovano x tako sto mu je izmenjeno n bitova

pocevsi od pozicije p i na ta mesta je upisano poslednjih n bitova

broja y */

unsigned set_bits(unsigned x, int p, int n, unsigned y)

{

/* Maska 000000...000111111 - poslednjih n jedinica */

unsigned last_n_1 = ~(~0 << n);

/* Maska 1111100..000111111 - n nula pocevsi od pozicije p */

unsigned middle_n_0 = ~(last_n_1 << p+1-n);

/* Brisemo n bitova pocevsi od pozicije p */

x = x & middle_n_0;

/* Izdvajamo poslednjih n bitova broja y i pomeramo ih

na poziciju p */

y = (y & last_n_1) << p+1-n;

/* Upisujemo bitove broja y u broj x i vracamo rezultat */

return x | y;

}

/* Invertuje n bitova broja x pocevsi od pozicije p */

unsigned invert_bits(unsigned x, int p, int n)

{

/* Maska 000000111...1100000 - n jedinica pocevsi od pozicije p */

24

Page 26: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

unsigned middle_n_1 = ~(~0 << n) << p+1-n;

/* Invertujemo koristeci ekskluzivnu disjunkciju */

return x ^ middle_n_1;

}

main()

{

unsigned x = 0x0AA0AFA0;

print_bits(x);

print_bits(get_bits(x, 15, 8));

print_bits(set_bits(x, 15, 8, 0xFF));

print_bits(invert_bits(x, 15, 8));

}

Izlaz iz programa:

00001010101000001010111110100000

00000000000000000000000010101111

00001010101000001111111110100000

00001010101000000101000010100000

Primer 3.7 right_rotate_bits, mirror_bits - rotiranje i simetrija bitova.

#include <stdio.h>

/* Pomocna funkcija - stampa bitove neoznacenog broja */

void print_bits(unsigned x)

{

int wl = sizeof(unsigned)*8;

unsigned mask;

for (mask = 1<<wl-1; mask; mask >>= 1)

putchar(x&mask ? ’1’ : ’0’);

putchar(’\n’);

}

/* Funkcija vrsi rotaciju neoznacenog broja x za n pozicija u desno */

unsigned right_rotate(unsigned x, int n)

{

int i;

int wl = sizeof(unsigned)*8;

/* Postupak se ponavlja n puta */

for (i = 0; i < n; i++)

25

Page 27: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

{

/* Poslednji bit broja x */

unsigned last_bit = x & 1;

/* x pomeramo za jedno mesto u desno */

x >>= 1;

/* Zapamceni poslednji bit stavljamo na pocetak broja x*/

x |= last_bit<<wl-1;

}

return x;

}

/* Funkcija obrce binarni zapis neoznacenog broja x tako sto bitove

cita unatrag */

unsigned mirror(unsigned x)

{

int i;

int wl = sizeof(unsigned)*8;

/* Rezultat inicijalizujemo na poslednji bit broja x */

unsigned y = x & 1;

/* Postupak se ponavlja wl-1 puta */

for (i = 1; i<wl; i++)

{

/* x se pomera u desno za jedno mesto */

x >>= 1;

/* rezultat se pomera u levo za jedno mesto */

y <<= 1;

/* Poslednji bit broja x upisujemo na poslednje

mesto rezultata */

y |= x & 1;

}

return y;

}

main()

{

unsigned x = 0xFAF0FAF0;

print_bits(x);

26

Page 28: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

print_bits(mirror(x));

print_bits(right_rotate(x, 2));

}

Izlaz iz programa:

11111010111100001111101011110000

00001111010111110000111101011111

00111110101111000011111010111100

Primer 3.8 Promeljiva c je tipa char. Sta ce biti vrednost promenljive c posle izrazadodele

• c=(15|3)>>1;?

• c=(15 & 3)<<1; ?

Primer 3.9 Napisati program koji sa standardnog ulaza uccitava pozitivan ceo broj,a na standardni izlaz ispisuje vrednost tog broja sa razmenjenim vrednostima bitovana pozicijama i, j. Pozicije i, j se ucitavaju kao parametri komandne linije. Sma-trati da je krajnji desni bit binarne reprezentacije 0-ti bit. Pri resavanju nije dozvol-jeno koristiti pomocni niz niti aritmeticke operatore +,-,/,*,%.

#include <stdio.h>

#include <stdlib.h>

int razmeni(unsigned n, unsigned i, unsigned j)

{

int rezultat = n;

/* Broj bitova tipa unsigned */

int wl = sizeof(int)*8;

unsigned mask_i = (1 << i);

unsigned mask_j = (1 << j);

if(n & mask_i)

/* Ako je na i-tom mestu 1, upisujemo

to na j-to mesto*/

rezultat |= mask_j;

/* Ako je na i-tom mestu 0, upisujemo

to na j-to mesto*/

else

rezultat &= ~mask_j;

if(n & mask_j)

/* Ako je na j-tom mestu 1, upisujemo

to na i-to mesto*/

27

Page 29: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

rezultat |= mask_i;

/* Ako je na j-tom mestu 0, upisujemo

to na i-to mesto*/

else

rezultat &= ~mask_i;

return rezultat;

}

int main(int argc, char** argv)

{

unsigned n, i, j, m;

if(argc < 3)

{

fprintf(stderr, "Neispravna upotreba programa!\n");

exit(1);

}

printf("Unesi broj\n");

scanf("%u", &n);

/* Racunaju se brojevi na osnovu argumenata

komandne linije.

*/

i = atoi(argv[1]);

j = atoi(argv[2]);

m = razmeni(n, i, j);

printf("Broj nastao razmenom bitova (%u, %u) broja %u je %u\n",

i, j, n, m);

return 0;

}

Primer 3.10 Napisati funkciju koja prima ceo broj n i ispisuje razliku broja jedinicana parnim i neparnim pozicijama u binarnom zapisu broja n.

PRIMER: za n = 19 = (10011)2 izlaz je 1.Napisati program koji pozivom ove funkcije racuna i stampa razliku broja jedinica

za sve argumente komandne linije (pretpostavka je da su argumenti komandne linijezadati korektno).

#include <stdio.h>

#include <stdlib.h>

int par_nepar(int n)

{

28

Page 30: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Bitski operatori

int br1_par = 0; /* int rezultat=0;*/

int br1_nepar = 0;

/* Broj bitova tipa unsigned */

int wl = sizeof(int)*8;

unsigned mask=1;

char par = 1;

for( ; mask; mask <<= 1)

{

if(mask&n)

if(par) br1_par++; /* if(par) rezultat++; */

else br1_nepar++; /* else rezultat--; */

par = 1 - par;

}

return br1_par - br1_nepar; /* return rezultat;*/

}

main(int argc, char** argv)

{

unsigned n, i, m;

if(argc < 2)

{

fprintf(stderr, "Neispravna upotreba programa!\n");

exit(1);

}

for(i=1; i<argc; i++)

{

n = atoi(argv[i]);

m = par_nepar(n);

printf("Razlika jedinica na parnim i neparnim za broj %u je %d\n", n, m);

}

}

29

Page 31: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 4

Vezbanje

4.1 Polinomi

Primer 4.1 Program ilustruje rad sa polinomima.

#include <stdio.h>

#include <math.h>

#define max(a, b) ((a) > (b) ? (a) : (b))

/*

* Polinom 3*x^3 + 2*x + 1.5 se predstavlja kao:

* stepen -> 3

* koeficijenti -> 1.5 2 0 3 x x x x x ... x

*/

typedef struct polinom

{

float koeficijenti[21];

int stepen;

} Polinom;

/*

* Funkcija vraca koeficijent uz x^i u polinomu p

* Zbog efikasnosti ne prenosimo celu strukturu vec

* samo pokazivac na strukturu.

*/

float vratiKoeficijent(Polinom* p, int i)

{

return i <= p->stepen ? p->koeficijenti[i] : 0.0f;

}

/*

* Funkcija postavlja koeficijent uz x^i u polinomu p na

* dati koeficijent k

30

Page 32: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.1 Polinomi

*/

void postaviKoeficijent(Polinom* p, int i, float k)

{

int j;

/*

Ukoliko je stepen polinoma bio manji, postavljamo

sve koeficijente izmedju na 0.0 i uvecavamo stepen

*/

if (i > p->stepen)

{

for (j = p->stepen+1; j < i; j++)

p->koeficijenti[j] = 0.0f;

p->stepen = i;

}

p->koeficijenti[i] = k;

}

/*

* Funkcija kreira polinom datog stepena sa datim nizom

* koeficijenata. Pretpostavlja se da su koeficijenti

* u datom nizu koeficijenti[] uredjeni opadajuce po stepenima

* polinoma.

*/

Polinom napraviPolinom(int stepen, float koeficijenti[])

{

int i;

Polinom p;

p.stepen = stepen;

for (i = 0; i <= stepen; i++)

{

postaviKoeficijent(&p, i, koeficijenti[stepen - i]);

}

return p;

}

/*

* Funkcija ispisuje polinom u citljivijem obliku.

* Na primer: 3.0*x^3 + 0.0*x^2 + 2.0*x^1 + 1.5*x^0

*/

void ispisiPolinom(Polinom* p)

{

int i;

for (i = p->stepen; i >= 0; i--)

31

Page 33: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.1 Polinomi

{

printf("%.2f*x^%d", vratiKoeficijent(p, i), i);

if (i > 0)

printf(" + ");

}

printf("\n");

}

/*

* Funkcija izracunava vrednost polinoma u tacki x

* Hornerovom shemom. Na primer,

* 3*x^3 + 2*x + 1.5 =

* (((0*x + 3)*x + 0)*x + 2)*x + 1.5

* Postupak izracunavanja p(10):

* 0.0

* 10 * 0.0 + 3 = 3.0

* 10 * 3.0 + 0 = 30.0

* 10 * 30.0 + 2 = 302.0

* 10 * 302.0 + 1.5 = 3021.5

*/

float vrednost(Polinom* p, float x)

{

int i;

float suma = 0.0f;

for (i = p->stepen; i >= 0; i--)

{

suma = suma*x + vratiKoeficijent(p, i);

}

return suma;

}

/*

* Funkcija sabira dva polinoma

*/

Polinom saberi(Polinom* p, Polinom* q)

{

int i;

Polinom zbir;

zbir.stepen = max(p->stepen, q->stepen);

for (i = 0; i <= zbir.stepen; i++)

postaviKoeficijent(&zbir, i,

vratiKoeficijent(p, i)

+ vratiKoeficijent(q, i));

return zbir;

32

Page 34: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.1 Polinomi

}

/*

* Funkcija mnozi dva polinoma. Na primer,

*

* 1*x^2 + 2*x + 3

* 4*x + 7

*

* 0.0*x^3 + 0.0*x^2 + 0.0*x + 0.0

* 0.0*x^3 + 0.0*x^2 + 0.0*x + 21.0 i=0 j=0

* 0.0*x^3 + 0.0*x^2 + 12.0*x + 21.0 i=0 j=1

* 0.0*x^3 + 0.0*x^2 + 26.0*x + 21.0 i=1 j=0

* 0.0*x^3 + 8.0*x^2 + 26.0*x + 21.0 i=1 j=1

* 0.0*x^3 + 15.0*x^2 + 26.0*x + 21.0 i=2 j=0

* 4.0*x^3 + 15.0*x^2 + 26.0*x + 21.0 i=2 j=1

*/

Polinom pomnozi(Polinom* p, Polinom* q)

{

int i, j;

Polinom proizvod;

proizvod.stepen = p->stepen + q->stepen;

for (i = 0; i <= proizvod.stepen; i++)

{

postaviKoeficijent(&proizvod, i, 0.0f);

}

for (i = 0; i <= p->stepen; i++)

{

for (j = 0; j <= q->stepen; j++)

{

/* r[i+j] = r[i+j] + p[i]*q[j] */

postaviKoeficijent(&proizvod, i+j,

vratiKoeficijent(&proizvod, i+j) +

vratiKoeficijent(p, i) * vratiKoeficijent(q, j));

}

}

return proizvod;

}

main()

{

float pkoeficijenti[] = {1.0f, 2.0f, 1.0f};

Polinom p = napraviPolinom(2, pkoeficijenti);

float qkoeficijenti[] = {1.0f, 1.0f};

Polinom q = napraviPolinom(1, qkoeficijenti);

Polinom r = pomnozi(&p, &q);

33

Page 35: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.2 Veliki brojevi

ispisiPolinom(&r);

}

4.2 Veliki brojevi

Primer 4.2 Program ilustruje rad sa velikim celim brojevima. Brojevi se internoreprezentuju preko niza svojih cifara.

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#define MAX_CIFRE 100

/* Funkcija obrce cifre prosledjenog niza */

void obrni_cifre (int cifre[], int duzina)

{

int i, j;

for (i=0, j=duzina-1; i<j; i++, j--)

{

int pom = cifre[i];

cifre[i] = cifre[j];

cifre[j] = pom;

}

}

/* Funkcija sa standardnog ulaza ucitava niz cifara, duzine

najvise max_cifre i smesta ga u niz brojeva cifre[]. Zatim

se niz cifre[] obrce kako bi cifre najmanje tezine bile

na pocetku niza.

Kao rezultat, funkcija vraca broj cifara ucitanog broja */

int uzmi_broj (int cifre[], int max_cifre)

{

int duzina = 0;

char c;

while ( --max_cifre>0 && isdigit(c=getchar()))

cifre[duzina++]=c-’0’;

obrni_cifre(cifre, duzina);

return duzina;

}

/* Funkcija ispisuje "veliki" broj predstavljen

nizom cifara cifre, duzine duzina, na standardni

izlaz imajuci u vidu da su cifre u nizu zapisane

"naopako" tj. pocevsi od cifre najmanje tezine */

void ispisi_broj(int cifre[],int duzina)

34

Page 36: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.2 Veliki brojevi

{

int i;

for (i=duzina-1; i>=0; i--)

printf("%d",cifre[i]);

putchar(’\n’);

}

/* Da li su dva broja data svojim nizovima cifara i duzinama jednaka?

Funkcija vraca 1 ako jesu, a 0 ako nisu */

int jednaki(int a[], int duzina_a, int b[], int duzina_b)

{

int i;

/* Poredimo duzine */

if (duzina_a != duzina_b)

return 0;

/* Ako su brojevi iste duzine, poredimo cifru po cifru*/

for (i=0; i<duzina_a; i++)

if (a[i] != b[i])

return 0;

return 1;

}

/* Funckija poredi dva broja a i b, data svojim nizovima cifara i duzinama

i vraca:

1 ako je a>b

0 ako je a=b

-1 ako je b>a

*/

int uporedi(int a[], int duzina_a, int b[], int duzina_b)

{

int i;

/* Uporedjujemo duzine brojeva a i b */

if (duzina_a > duzina_b)

return 1;

if (duzina_a < duzina_b)

return -1;

/* U ovom trenutku znamo da su brojevi iste duzine, tako da

prelazimo na poredjenje cifre po cifre, pocevsi od cifre

najvece tezine */

for (i=duzina_a-1; i>=0; i--)

{

if (a[i] > b[i])

35

Page 37: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.2 Veliki brojevi

return 1;

if (a[i] < b[i])

return -1;

}

return 0;

}

/* Funkcija sabira dva broja data svojim nizovima cifara

i duzinama */

void saberi(int a[], int duzina_a, int b[], int duzina_b,

int rezultat[],

int* duzina_rezultata)

{

int i;

/* Prenos sa prethodne pozicije */

int prenos = 0;

/* Sabiranje vrsimo dok ne prodjemo sve cifre duzeg od brojeva a i b */

for(i=0; i<duzina_a || i<duzina_b; i++)

{

/* Sabiramo i-tu cifra broja a (ako postoji) sa i-tom cifrom

broja b (ako postoji) i prenos sa prethodne pozicije */

int cifra_rezulata = ((i<duzina_a)? a[i] : 0) +

((i<duzina_b)? b[i] : 0) +

prenos;

/* Nova cifra rezultata */

rezultat[i] = cifra_rezulata%10;

/* Prenos na sledecu poziciju */

prenos = cifra_rezulata/10;

}

/* Kada smo zavrsili sa svim ciframa brojeva a i b moguce je da je

postojao prenos na sledecu poziciju u kom slucaju uvecavamo duzinu

rezultata. Inace duzinu rezultata postavljamo na duzinu duzeg od

brojeva a i b, koja se nalazi trenutno u promenjivoj i */

if (prenos != 0)

{

if (i>=MAX_CIFRE)

{printf("Doslo je do prekoracenja!\n"); exit(1);}

else

{

36

Page 38: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.2 Veliki brojevi

rezultat[i] = prenos;

*duzina_rezultata = i+1;

}

}

else

*duzina_rezultata=i;

return;

}

/* Funkcija mnozi broj, dat svojim nizom cifara i duzinom, datom cifrom c */

void pomnozi_cifrom (int a[], int duzina_a, int cifra,

int rezultat[],

int* duzina_rezultata)

{

int i, prenos = 0;

for (i=0; i<duzina_a; i++)

{

/* Svaku cifru broja a mnozimo cifrom c, dodajemo na to

prenos sa prethodne pozicije i to smestamo u promenljivu

pom */

int pom = a[i]*cifra + prenos;

/* Nova cifra rezultata */

rezultat[i] = pom%10;

/* Prenos na sledecu poziciju */

prenos = pom/10;

}

/* Kada smo zavrsili sa svim ciframa broja a, moguce je da je

postojao prenos na sledecu poziciju u kom slucaju uvecavamo

duzinu rezultata. Inace duzinu rezultata postavljamo na duzinu

broja a, koja se nalazi trenutno u promenjivoj i */

if (prenos != 0)

{

if (i>=MAX_CIFRE)

{printf("Doslo je do prekoracenja !\n"); exit(1);}

else

{

rezultat[i] = prenos;

*duzina_rezultata = duzina_a+1;

}

}

else

37

Page 39: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.2 Veliki brojevi

*duzina_rezultata = duzina_a;

}

/* Funkcija mnozi dva broja data svojim nizovima cifara i duzinama*/

void pomnozi (int a[], int duzina_a, int b[], int duzina_b,

int rezultat[],

int* duzina_rezultata)

{

/* Ova funkcija se gradi kombinovanjem algoritama mnozenja broja cifrom i

sabiranja dva broja */

int i,j,k;

/* Broj pom ce da sadrzi rezultat mnozenja broja a jednom po jednom

cifrom broja b, dok ce na broj rezultat da se dodaje svaki

put (10^i)*pom */

int pom[MAX_CIFRE], duzina_pom, prenos;

*duzina_rezultata = 0;

/*

b=351 * a=123

---------------

1 korak ===> rez: 351 * 3 = 1053

2 korak ===> 351 * 2 = 702

------------------

rez: 8073

3 korak ===> 351 * 1 = 351

------------------

rez: 43173

*/

/* Za svaku cifru broja a */

for (i=0; i<duzina_a; i++)

{

/* vrsimo mnozenje broja b i-tom cifrom broja a */

pomnozi_cifrom(b, duzina_b, a[i], pom, &duzina_pom);

/* Zatim dodajemo broj pom na rezultat, ali dodavanje pocinjemo od

i-te cifre rezultata.*/

prenos = 0;

for (k=0; k<duzina_pom; k++)

{

int pm=((i+k<*duzina_rezultata) ? rezultat[i+k] : 0) + pom[k] + prenos;

rezultat[i+k] = pm%10;

prenos = pm/10;

38

Page 40: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 4.2 Veliki brojevi

}

if (prenos)

{

rezultat[i+k] = prenos;

*duzina_rezultata = i+k+1;

}

else

*duzina_rezultata = i+k;

}

}

/* Primer koriscenja funkcija */

main()

{

/* duzina brojeva a i b */

int duzina_a, duzina_b;

/* nizovi cifara brojeva a i b */

int a[MAX_CIFRE], b[MAX_CIFRE], zbir[MAX_CIFRE], proizvod[MAX_CIFRE];

/* Ucitavaju se brojevi */

printf("Unesite prvi broj : ");

duzina_a = uzmi_broj(a,MAX_CIFRE);

printf("Unesite drugi broj : ");

duzina_b = uzmi_broj(b, MAX_CIFRE);

/* Sabiraju se i ispisuje se zbir */

saberi(a, duzina_a, b, duzina_b, zbir, &duzina_zbira);

printf("Zbir je : ");

ispisi_broj(zbir, duzina_zbira);

/* Mnoze se i ispisuje se proizvod */

pomnozi(a, duzina_a, b, duzina_b, proizvod, &duzina_proizvoda);

printf("Proizvod je : ");

ispisi_broj(proizvod, duzina_proizvoda);

}

39

Page 41: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 5

Sortiranje

Niz moze biti sortiran ili uredjen u

• opadajucem poretku:

niz[0] > niz[1] > ... > niz[i] > niz[i + 1] > ... > niz[n − 1]

• rastucem poretku:

niz[0] < niz[1] < ... < niz[i] < niz[i + 1] < ... < niz[n − 1]

• neopadajucem poretku:

niz[0] ≤ niz[1] ≤ ... ≤ niz[i] ≤ niz[i + 1] ≤ ... ≤ niz[n − 1]

• nerastucem poretku:

niz[0] ≥ niz[1] ≥ ... ≥ niz[i] ≥ niz[i + 1] ≥ ... ≥ niz[n − 1]

U nastavku su dati primeri algoritama za sortiranje u nerastucem poretku celobro-jnog niza. Jednostavnim modifikacijama svakim od ovih algoritama niz se mozesortirati i u opadajucem, rastucem ili neopadajucem poretku.

5.1 Vremenska slozenost

Pogledati slike koje oslikavaju vremensku slozenost za razlicite algoritme i razlicitenizove:

• Rezultati dobijeni za slucajno generisane nizove:http://www.matf.bg.ac.yu/~filip/pp/0405/random-sort.gif

• Rezultati dobijeni za vec sortirane nizove:http://www.matf.bg.ac.yu/~filip/pp/0405/sorted-sort.gif

• Rezultati dobijeni za naopako sortirane nizove:http://www.matf.bg.ac.yu/~filip/pp/0405/reverse-sorted-sort.gif

40

Page 42: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.2 Selection sort

5.2 Selection sort

Primer 5.1 (Selection sort) U prvom prolazu se razmenjuju vrednosti a[0] saonim clanovima ostatka niza koji su veci od njega. Na taj nacin ce se posle prvogprolaza kroz niz a[0] postaviti na najveci element niza. U svakom sledecem prolazu,postavlja se na i-to mesto najveci element niza a[i], a[i+1] ... a[n-1].

Primer sortiranja:

n = 6 a: 1 3 5 2 6 8

-------------

i = 0 j = 1 => a: 3 1 5 2 6 8

i = 0 j = 2 => a: 5 1 3 2 6 8

i = 0 j = 3 => a: 5 1 3 2 6 8

i = 0 j = 4 => a: 6 1 3 2 5 8

i = 0 j = 5 => a: 8 1 3 2 5 6

-------------

i = 1 j = 2 => a: 8 3 1 2 5 6

i = 1 j = 3 => a: 8 3 1 2 5 6

i = 1 j = 4 => a: 8 5 1 2 3 6

i = 1 j = 5 => a: 8 6 1 2 3 5

...

#include<stdio.h>

#include<stdlib.h>

#define MAXDUZ 100

void selection(int a[], int n)

{

/* pomocna i brojacke promenljive */

int pom,i,j;

/*Sortiranje*/

for(i=0; i<n-1; i++)

for(j=i+1; j<n; j++)

if(a[i]<a[j])

{

pom=a[i];

a[i]=a[j];

a[j]=pom;

}

}

main()

{

/* Niz od maksimalno MAXDUZ elemenata*/

41

Page 43: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.2 Selection sort

int a[MAXDUZ];

/* Dimenzija niza, pomocna i brojacke promenljive */

int n,i;

printf("Unesite dimenziju niza\n");

scanf("%d",&n);

if (n>MAXDUZ)

{

printf("Nedozvoljena vrednost za n\n");

exit(1);

}

/* Unos clanova niza */

for(i=0; i<n; i++)

{

printf("Unesite %d. clan niza\n",i+1);

scanf("%d",&a[i]);

}

selection(a,n);

/* Ispis niza */

printf("Sortirani niz:\n");

for(i=0; i<n; i++)

printf("%d\t",a[i]);

putchar(’\n’);

}

Primer 5.2 (Selection sort2) Modifikacija prethodnog resenja radi dobijanja naefikasnosti. Ne vrse se zamene svaki put vec samo jednom, kada se pronade odgo-varajuci element u nizu sa kojim treba izvrsiti zamenu tako da u nizu bude postavljentrenutno najveci element na odgovarajuce mesto.

Primer sortiranja:

n = 6 a: 1 3 5 2 6 8

-------------

i = 0 => a: 8 1 5 2 6 3

-------------

i = 1 => a: 8 6 5 2 1 3

-------------

i = 2 => a: 8 6 5 2 1 3

-------------

42

Page 44: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.3 Bubble sort

...

void selection2(int a[], int n)

{

int i, j, max;

/*Sortiranje - bez stalnih zamena vec se

pronalazi indeks trenutno najveceg clana niza*/

for(i=0; i<n-1; i++)

{

max = i;

for(j=i+1; j<n; j++)

if(a[max]<a[j])

max=j;

/* Vrsi se zamena onda kada na i-tom mestu

nije najveci element. Tada se na i-to mesto

postavlja najveci element koji se nalazio na

mestu ind. */

if(i != max)

{

pom=a[max];

a[max]=a[i];

a[i]=pom;

}

}

}

5.3 Bubble sort

Primer 5.3 (Bubble sort) Algoritam sortiranja buble sort poredi dva susedna el-ementa niza i ako su pogresno rasporedeni zamenjuje im mesta. Posle poredenjasvih susednih parova najmanji od njih ce isplivati na kraj niza. Zbog toga se ovajmetod naziva metod mehurica. Da bi se najmanji broj nesortiranog dela niza doveona svoje mesto treba ponoviti postupak.

Primer sortiranja:

n = 6 a: 1 3 5 2 6 8

-------------

i = 5 j = 0 => a: 3 1 5 2 6 8

i = 5 j = 1 => a: 3 5 1 2 6 8

i = 5 j = 2 => a: 3 5 2 1 6 8

i = 5 j = 3 => a: 3 5 2 6 1 8

i = 5 j = 4 => a: 3 5 2 6 8 1

-------------

43

Page 45: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.3 Bubble sort

i = 4 j = 0 => a: 5 3 2 6 8 1

...

void bubble(int a[], int n)

{

int i, j, pom;

for(i=n-1; i>0; i--)

for(j=0; j<i; j++)

if(a[j]<a[j+1])

{

pom=a[j];

a[j]=a[j+1];

a[j+1]=pom;

}

}

Primer 5.4 (Bubble sort 2) Unapredjujemo prethodni algoritam kako bismo obezbedlida se ne vrse provere onda kada je niz vec sortiran nego da se u tom slucaju prekinerad.

void bubble2(int a[], int n)

{

/* Promenljiva koja govori da li je izvrsena

zamena u i-tom prolazu kroz niz pa ako nije

sortiranje je zavrseno jer su svaka dva

susedna elementa niza u odgovarajucem poretku */

int zam;

zam=1;

for(i=n-1; zam && i>0; i--)

for(zam=0,j=0; j<i; j++)

if(a[j]<a[j+1])

{

/* Zamena odgovarajucih clanova niza */

pom=a[j];

a[j]=a[j+1];

a[j+1]=pom;

/* Posto je u i-tom prolazu

izvrsena bar ova zamena zam

se postavlja na 1 sto

nastavlja sortiranje */

zam=1;

}

44

Page 46: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.4 Insertion sort

}

5.4 Insertion sort

Primer 5.5 (Insertion sort) U svakom trenutku je pocetak niza sortiran, a sorti-ranje se vrsi tako sto se jedan po jedan element niza sa kraja ubacuje na odgovarajucemesto.

Primer sortiranja:

n = 6 a: 1 3 5 2 6 8

-------------

i = 1 j = 1 => a: 3 1 5 2 6 8

-------------

i = 2 j = 2 => a: 3 5 1 2 6 8

i = 2 j = 1 => a: 5 3 1 2 6 8

-------------

i = 3 j = 3 => a: 5 3 2 1 6 8

-------------

i = 4 j = 4 => a: 5 3 2 6 1 8

...

void insertion(int a[], int n)

{

/* Pomocna i brojacke promenljive */

int pom,i,j;

for(i=1; i<n; i++)

for(j=i; (j>0) && (a[j]>a[j-1]); j--)

{

pom=a[j];

a[j]=a[j-1];

a[j-1]=pom;

}

}

Primer 5.6 (Insertion sort 2) Broj dodela se moze redukovati tako sto se umestostalnih zamena koristi dodela privremenoj promenljivoj — elemente niza pomeramoza jedno mesto sve dok ne nademo poziciju na koju treba da postavimo dati element(koji je sacuvan u privremenoj promenljivoj).

void insertion2(int a[], int n)

{

int i, j, tmp;

for (i = 1; i < n; i++)

{

int tmp = a[i];

45

Page 47: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

for (j = i; j > 0 && a[j-1] < tmp; j--){

a[j] = a[j-1];

}

a[j] = tmp;

}

}

5.5 Razni zadaci

Primer 5.7 Napisati funkciju koja od dva sortirana niza formira treci sortiran niz(nizovi su sortirani neopadajucie).

#include <stdio.h>

/* Funkcija spaja dva uredjena niza:

a koji ima na elemenata i

b koji ima nb elemenata

i na osnovu njih gradi uredjeni niz c ciji

broj elemenata vraca.

Pretpostavlja se da u nizu c ima dovoljno prostora.

*/

int merge(int a[], int na, int b[], int nb, int c[])

{

/* Dok god ima elemenata u oba niza uzima se

manji od dva tekuca i prepisuje u niz c */

int i = 0, j = 0, k = 0;

while(i < na && j < nb)

{

if (a[i] < b[j])

c[k++] = a[i++];

else

c[k++] = b[j++];

}

/* Prepisujemo eventualno preostale elemente niza a */

for (; i < na; i++, k++)

c[k] = a[i];

/* Prepisujemo eventualno preostale elemente niza b */

for (; j < nb; j++, k++)

c[k] = b[j];

return k;

}

46

Page 48: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

main() {

int a[] = {1, 5, 6, 9, 13};

int b[] = {2, 3, 7, 11, 12, 15};

int c[30];

int na = sizeof(a)/sizeof(int);

int nb = sizeof(b)/sizeof(int);

int nc = merge(a, na, b, nb, c);

int i;

for (i = 0; i < nc; i++)

printf("%d ", c[i]);

printf("\n");

}

Primer 5.8 Program ucitava niz studenata i sortira ih po njihovim ocenama.

#include <stdio.h>

#include <ctype.h>

#define MAX_IME 20

typedef struct _student

{

char ime[MAX_IME];

char prezime[MAX_IME];

int ocena;

} student;

/* Funkcija ucitava rec i vraca njenu duzinu ili

-1 ukoliko smo dosli do znaka EOF*/

int getword(char word[],int max)

{

int c, i=0;

while (isspace(c=getchar()))

;

while(!isspace(c) && c!=EOF && i<max-1)

{

word[i++]=c;

c = getchar();

}

word[i]=’\0’;

if (c==EOF) return -1;

47

Page 49: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

else return i;

}

/* Funkcija ucitava niz studenata, vraca duzinu niza koji ucita */

int UcitajPodatke(student studenti[], int max)

{

int i=0;

while(i<max && getword(studenti[i].ime, MAX_IME)>0)

{

if (getword(studenti[i].prezime, MAX_IME) < 0)

break;

scanf("%d",&studenti[i].ocena);

i++;

}

return i;

}

void IspisiPodatke(student studenti[], int br_studenata)

{

int i;

printf("IME PREZIME OCENA\n");

printf("--------------------------------------\n");

for (i=0; i<br_studenata; i++)

printf("%-20s %-20s %5d\n",studenti[i].

ime, studenti[i].prezime, studenti[i].ocena);

}

/* Sortiranje studenata po ocenama */

void SelectionSort(student studenti[], int br_studenata)

{

int i,j;

for (i=0; i<br_studenata-1; i++)

for (j=i; j<br_studenata; j++)

if (studenti[i].ocena<studenti[j].ocena)

{ student tmp=studenti[i];

studenti[i]=studenti[j];

studenti[j]=tmp;

}

}

main()

{

student studenti[100];

48

Page 50: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

int br_studenata = UcitajPodatke(studenti,100);

SelectionSort(studenti, br_studenata);

IspisiPodatke(studenti, br_studenata);

}

Primer 5.9 Program ucitava informacije o studentima iz datoteke cije se ime zadajeiz komandne linije, i koja je u formatu:

n

prezime1 ime1 smer1 prosek1

prezime2 ime2 smer2 prosek2

...

prezimen imen smern prosekn

Studenti se sortiraju leksikografski po prezimenima i imenima, a zatim se njihovipodaci tako sortirani ispisuju na izlaz, ili u izlazni fajl, ako je njegovo ime zadato ukomandnoj liniji.

#include <stdio.h>

#include <string.h>

#define MAX_IME 20

#define MAX_PREZIME 30

#define MAX_SMER 10

#define MAX_STUDENATA 100

/* Struktura koja predstavlja informacije o studentu */

typedef struct student {

char ime[MAX_IME];

char prezime[MAX_PREZIME];

char smer[MAX_SMER];

double prosek;

} Student;

/* Funkcija uporedjuje dva studenta po leksikografskom poretku

njihovih imena i prezimena. Funkcija vraca vrednost manju od

nule ako je prvi student "manji" od drugog, vrednost vecu

od nule ako je prvi student "veci" od drugog, dok nulu vraca

ako se studenti zovu isto. */

int uporedi(Student *s, Student *p)

{

int t;

49

Page 51: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

/* Najpre poredimo prezimena. Ako su prezimena ista, onda poredimo

i imena. */

if((t = strcmp(s->prezime, p->prezime)) != 0)

return t;

else

return strcmp(s->ime, p->ime);

}

/* Funkcija sortira niz studenata u leksikografskom poretku */

void sortiraj(Student studenti[], int n)

{

int i, j;

int min;

Student pom;

/* Studenti se porede funkcijom uporedi(). Zamena elemenata

niza se obavlja na uobicajen nacin, zato sto je dodela

struktura legitimna operacija i zato sto struktura ne

sadrzi pokazivace za koje je memorija alocirana dinamicki. */

for (i = 0; i < n - 1; i++)

{

min = i;

for (j = i + 1; j < n; j++)

if (uporedi(&studenti[j], &studenti[min]) < 0)

min = j;

if (min != i)

{

pom = studenti[i];

studenti[i] = studenti[min];

studenti[min] = pom;

}

}

}

/* Funkcija ucitava podatke o studentu */

void ucitaj(FILE *in, Student *s)

{

fscanf(in, "%s", s->prezime);

fscanf(in, "%s", s->ime);

fscanf(in, "%s", s->smer);

50

Page 52: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

fscanf(in, "%lf", &s->prosek);

}

/* Ispisujemo informacije o studentu */

void ispisi(FILE *out, Student *s)

{

fprintf(out, "%s %s %s %f\n", s->prezime, s->ime, s->smer, s->prosek);

}

/* Funkcija main */

int main (int argc, char ** argv)

{

int n, i;

FILE *in = stdin, *out = stdout;

Student studenti[MAX_STUDENATA];

/* Ako je korisnik uneo ime ulaznog fajla...*/

if(argc > 1)

if((in = fopen(argv[1], "r")) == NULL)

{

fprintf(stderr, "Greska prilikom otvaranja fajla!\n");

return 1;

}

/* Ako je korisnik uneo i ime izlaznog fajla...*/

if(argc > 2)

if((out = fopen(argv[2], "w")) == NULL)

{

fprintf(stderr, "Greska prilikom otvaranja fajla!\n");

return 1;

}

/* Ucitavamo broj studenata */

fscanf(in, "%d", &n);

/* Ucitavamo informacije o studentima */

for(i = 0; i < n; i++)

ucitaj(in, &studenti[i]);

/* Sortiramo niz studenata */

sortiraj(studenti, n);

/* Ispisujemo niz studenata */

51

Page 53: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

for(i = 0; i < n; i++)

ispisi(out, &studenti[i]);

fclose(in);

fclose(out);

return 0;

}

Primer 5.10 Program utvrduje da li su dve niske karaktera anagrami. Dve niskesu anagrami ako se sastoje od istog broja istih karaktera. Na primer, niske ”trave”i ”vetar” jesu anagrami, dok ”vetar” i ”vatra” nisu.

#include <stdio.h>

#include <string.h>

#define MAX 1024

/* Funkcija sortira karaktere stringa s */

void sortiraj (char s[])

{

int i, j;

int min;

char pom;

int n;

/* Racunamo duzinu stringa */

for (n = 0; s[n] != ’\0’; n++)

;

/* Ostatak funkcije je identican funkciji

za sortiranje celih brojeva. */

for (i = 0; i < n - 1; i++)

{

min = i;

for (j = i + 1; j < n; j++)

if (s[j] < s[min])

min = j;

if (min != i)

{

pom = s[i];

s[i] = s[min];

s[min] = pom;

}

}

52

Page 54: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

}

/* Funkcija utvrdjuje da li su reci s i t anagrami.

Za reci kazemo da su anagrami, ako se jedna rec

moze dobiti od druge premestanjem slova u reci */

int anagrami (char *s, char *t)

{

char sp[MAX];

char tp[MAX];

/* Kopiramo stringove (reci) u pomocne nizove sp i tp

zato sto ne zelimo da nasa funkcija promeni originalne

nizove. */

strcpy (sp, s);

strcpy (tp, t);

/* Sortiramo karaktere stringova u pomocnim nizovima */

sortiraj (sp);

sortiraj (tp);

/* Ako su stringovi nakon sortiranja jednaki, tada su polazne reci

bile anagrami */

return strcmp (sp, tp) == 0;

}

/* Test program */

int main ()

{

char s[MAX], t[MAX];

/* Ucitavamo dve reci */

printf ("Uneti prvu rec: ");

scanf ("%s", s);

printf ("Uneti drugu rec: ");

scanf ("%s", t);

/* Utvrdjujemo da li su anagrami i ispisujemo poruku */

if (anagrami (s, t))

printf ("Reci %s i %s su anagrami\n", s, t);

else

printf ("Reci %s i %s nisu anagrami\n", s, t);

53

Page 55: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

return 0;

}

Primer 5.11 Interpolaciona pretraga se moze porediti sa pretragom recnika:ako neko trazi rec na slovo B, sigurno nece da otvori recnik na polovini, vec verovatnonegde blize pocetku.

/* Funkcija trazi u SORTIRANOM nizu a[] duzine n

broj x. Vraca indeks pozicije nadjenog elementa

ili -1, ako element nije pronadjen */

int interpolaciona_pretraga (int a[], int n, int x)

{

int l = 0;

int d = n - 1;

int s;

/* Dokle god je indeks l levo od indeksa d... */

while (l <= d)

{

/* Ako je element manji od pocetnog ili veci od poslednjeg

clana u delu niza a[l],...,a[d] tada nije u tom delu niza.

Ova provera je neophodna, da se ne bi dogodilo da se prilikom

izracunavanja indeksa s izadje izvan opsega indeksa [l,d]

*/

if(x < a[l] || x > a[d])

return -1;

/* U suprotnom, x je izmedju a[l] i a[d], pa ako su a[l] i a[d]

jednaki, tada je jasno da je x jednako ovim vrednostima, pa

vracamo indeks l (ili indeks d, sve jedno je).Ova provera je

neophodna, zato sto bismo inace prilikom izracunavanja s imali

deljenje nulom.

*/

else if(a[l] == a[d])

return l;

/* Indeks s je uvek izmedju l i d, ali ce

verovatno biti blize trazenoj vrednosti nego da

smo prosto uvek uzimali srednji element.*/

/* Racunamo sredisnji indeks */

s = l + ((double)(x - a[l]) / (a[d] - a[l])) * (d - l);

/* Ako je sredisnji element veci od x,

tada se x mora nalaziti u levoj polovini niza */

if (x < a[s])

d = s - 1;

54

Page 56: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

/* Ako je sredisnji element manji od x,

tada se x mora nalaziti u desnoj polovini niza

*/

else if (x > a[s])

l = s + 1;

else

/* Ako je sredisnji element jednak x,

tada smo pronasli x na poziciji s

*/

return s;

}

/* ako nije pronadjen vracamo -1 */

return -1;

}

Primer 5.12 Data su dva rastuca niza A i B, napisati funkciju koja formira niz C

koji sadrzi:

(a) zajednicke elemente nizova A i B (presek skupova A i B).

(b) elemente koje sadrzi niz A a ne sadrzi niz B (razlika skupova A i B).

(c) sve elemente koje sadrze nizovi A i B (unija skupova A i B) — ovo je ekvivalentnospajanju dva uredena niza — primer 5.7.

/* Funkcija kreira presek dva rastuca niza a i b i rezultat smesta u

niz c (takodje u rastucem radosledu). Funkcija vraca broj elemenata

preseka */

int kreiraj_presek(int a[], int n_a, int b[], int n_b, int c[])

{

int i = 0, j = 0, k = 0;

/* Dokle god ima elemenata u oba niza... */

while (i < n_a && j < n_b)

{

/* Ako su jednaki, ubacujemo vrednost u presek,

i prelazimo na sledece elemente u oba niza. */

if(a[i] == b[j])

{

c[k++] = a[i];

i++;

j++;

}

/* Ako je element niza a manji, tada u tom nizu prelazimo

na sledeci element */

else if(a[i] < b[j])

55

Page 57: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

i++;

/* Ako je element niza b manji, tada u tom nizu prelazimo

na sledeci element */

else

j++;

}

/* Vracamo broj elemenata preseka */

return k;

}

/* Funkcija kreira razliku dva rastuca niza a i b i rezultat smesta u

niz c (takodje u rastucem radosledu). Funkcija vraca broj elemenata

razlike */

int kreiraj_razliku (int a[], int n_a, int b[], int n_b, int c[])

{

int i = 0, j = 0 , k = 0;

/* Sve dok ima elemenata u oba niza...*/

while(i < n_a && j < n_b)

{

/* Ako je tekuci element niza a manji, tada

je on u razlici, jer su svi sledeci elementi

niza b jos veci. Ubacujemo ga u razliku i

prelazimo na sledeci element niza a */

if(a[i] < b[j])

c[k++] = a[i++];

/* Ako je tekuci element niza b manji, tada

prelazimo na sledeci element u nizu b */

else if (a[i] > b[j])

j++;

/* Ako su jednaki, tada ih oba preskacemo. Tekuci

element niza a ocigledno nije u razlici. */

else

{

i++;

j++;

}

}

/* Ako su preostali elementi niza a, tada su oni u razlici

jer su svi elementi niza b bili manji od tekuceg elementa

niza a, i svih koji za njim slede */

while (i < n_a)

56

Page 58: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 5.5 Razni zadaci

c[k++] = a[i++];

/* Vracamo broj elemenata u razlici */

return k;

}

57

Page 59: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 6

Pokazivaci

Pokazivac je promenljiva koja sadrzi adresu promenljive.

int x=1, y=1;

int *ip; /* ip je pokazivac na int, odnosno *ip je tipa int */

ip = &x; /* ip cuva adresu promenljive x, tj ip pokazuje na x */

y=*ip; /* y dobija vrednost onoga sto se nalazi na adresi koju

cuva promenljiva ip, tj posto ip cuva adresu promenljive

x, a x ima vrednost 1 to je i y sada 1 */

*ip = 0; /* preko pokazivaca njema se sadrzaj na adresi koju cuva ip,

prema tome, x je sada 0 */

*ip+=10; /* x je sada 10*/

++*ip; /* x je sada 11*/

(*ip)++; /* x je sada 12,

zagrada neophodna zbog prioriteta operatora*/

Pored pokazivaca na osnovne tipove, postoji i pokazivac na prazan tip — void.

void *pp;

Njemu moze da se dodeli da pokazuje na int, char, float ili na bilo koji drugi tipali je neophodno eksplicitno naglasiti na koji tip ukazuje pokazivac svaki put kadazelimo da koristimo sadrzaj adrese koju pokazivac cuva.

Primer 6.1 Upotreba pokazivaca na prazan tip.

#include<stdio.h>

main()

{

void *pp; /* Ovaj pokazivac moze da pokazuje na adrese

na kojima se nalaze razliciti tipovi

podataka. */

int x=2;

58

Page 60: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.1 Pokazivacka aritmetika — primeri

char c=’a’;

pp = &x; /* Pokazivac sada sadrzi adresu promenljive tipa int*/

*(int *)pp = 17; /* x postaje 17, kroz pokazivac pp je

promenjena vrednost promenljive x.

Kastovanje (int *) ukazuje na to da se prilikom

citanja vrednosti koja se nalazi na

adresi koju cuva pokazivac pp, ovaj pokazivac

tretira kao pokazivac na tip int */

printf("Adresa od x je %p\n ", &x);

printf("%d i %p\n",*(int*)pp,(int * )pp);

pp = &c; /* Pokazivac sada sadrzi adresu promenljive tipa char*/

printf("Adresa od c je %p\n", &c);

printf("%c i %p\n",*(char*)pp,(char * )pp);

/* Kastovanje (char *) ukazuje na to da se prilikom

citanja vrednosti koja se nalazi na

adresi koju cuva pokazivac pp, ovaj pokazivac

tretira kao pokazivac na tip char */

}

/*

Adresa od x je 0012FF78

17 i 0012FF78

Adresa od c je 0012FF74

a i 0012FF74

*/

Posebna konstanta koja se koristi da se oznaci da pokazivac ne pokazuje na nekomesto u memoriji je NULL. Ova konstanta je definisana u biblioteci stdio.h i imavrednost 0.

6.1 Pokazivacka aritmetika — primeri

Primer 6.2 Funkcija proverava da li se neka vrednost x nalazi u nizu niz dimenzijen, bez koriscenja indeksiranja. Funkcija vraca pokazivac na poziciju pronadjenogelementa ili NULL ukoliko element nije pronaden.

#include <stdio.h>

int* nadjiint(int* niz, int n, int x)

{

while (--n >= 0 && *niz != x)

niz++;

59

Page 61: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.1 Pokazivacka aritmetika — primeri

return (n>=0) ? niz : NULL;

}

main()

{

int a[]={1,2,3,4,5,6,7,8};

int* poz=nadjiint(a,sizeof(a)/sizeof(int),4);

if (poz!=NULL)

printf("Element pronadjen na poziciji %d\n",poz-a);

}

Primer 6.3 Funkcije izracunavaju duzinu niske koriscenjem pokazivaca.

int string_length1(char *s)

{

int n;

for(n=0; *s != ’\0’; s++)

n++;

return n;

}

/* Druga varijanta iste funkcije */

int string_length2(char *s)

{

char* t;

for (t = s; *t; t++)

;

return t - s;

}

Primer 6.4 Funkcija kopira string t u string s (podrazumeva se da je za string s

rezervisano dovoljno mesta).

void copy(char* dest, char* src)

{

while (*dest++=*src++)

;

}

/* Ovo je bio skraceni zapis za sledeci kod

while(*src != ’\0’)

{

*dest=*src;

dest++;

src++;

60

Page 62: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.1 Pokazivacka aritmetika — primeri

}

*dest = ’\0’;

*/

Primer 6.5 Nadovezuje string t na kraj stringa s. Pretpostavlja da u s ima do-voljno prostora.

void string_concatenate(char *s, char *t)

{

/* Pronalazimo kraj stringa s */

while (*s) /* while (*s != 0)*/

s++;

/* Nakon prethodne petlje, s pokazuje na ’\0’, kopiranje

pocinje od tog mesta, tako da ce znak ’\0’ biti prepisan. */

/* Kopiranje se vrisi slicno funkciji string_copy */

while (*s++ = *t++)

;

}

Primer 6.6 Funkcija strcmp vrsi leksikografsko poredenje dva stringa. Vraca:0 — ukoliko su stringovi jednaki

<0 — ukoliko je s leksikografski ispred t>0 — ukoliko je s leksikografski iza t

int strcmp(char *s, char *t)

{

/* Petlja tece sve dok ne naidjemo

na prvi razliciti karakter */

for (; *s == *t; s++, t++)

if (*s == ’\0’) /* Naisli smo na kraj

oba stringa, a nismo nasli razliku */

return 0;

/* *s i *t su prvi karakteri u kojima se niske

razlikuju. Na osnovu njihovog odnosa,

odredjuje se odnos stringova */

return *s - *t;

}

Primer 6.7 Pronalazi prvu poziciju karaktera c u stringu s, i vraca pokazivac nanju, odnosno NULL ukoliko s ne sadrzi c.

char* string_char(char *s, char c)

{

61

Page 63: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.1 Pokazivacka aritmetika — primeri

int i;

for (; *s; s++)

if (*s == c)

return s;

/* Nije nadjeno */

return NULL;

}

Primer 6.8 Pronalazi poslednju poziciju karaktera c u stringu s, i vraca pokazivacna nju, odnosno NULL ukoliko s ne sadrzi c.

char* string_last_char(char *s, char c)

{

char *t = s;

/* Pronalazimo kraj stringa s */

while (*t)

t++;

/* Krecemo od kraja i trazimo c unazad */

for (t--; t >= s; t--)

if (*t == c)

return t;

/* Nije nadjeno */

return NULL;

}

Primer 6.9 Za svaku liniju ucitanu sa ulaza proverava se da li sadrzi rec zdravo.

#include <stdio.h>

/* Proverava da li se niska t nalazi unutar niske s

Vraca poziciju na kojoj string pocinje odnosno

-1 ukoliko niska t nije podniska niske s. */

int sadrzi_string(char s[], char t[])

{

int i;

for (i = 0; s[i]; i++)

{

int j;

for (j=0, k=0; s[i+j]==t[j]; j++)

if (t[j+1]==’\0’)

return i;

}

return -1;

62

Page 64: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.1 Pokazivacka aritmetika — primeri

}

/* Proverava da li string str sadrzi string sub.

Vraca pokazivac na kojoj sub pocinje,

odnosno NULL ukoliko ga nema. */

char* string_string(char *str, char *sub)

{

char *s, *t;

/* Proveravamo da li sub pocinje na svakoj poziciji i */

for (; *str; str++)

/* Poredimo sub sa str pocevsi od pozicije na koju ukazuje

str sve dok ne naidjemo na razliku */

for (s = str, t = sub; *s == *t; s++, t++)

/* Nismo naisli na razliku a ispitali smo sve

karaktere niske sub */

if (*(t+1) == ’\0’)

return str;

/* Nije nadjeno */

return NULL;

}

/* Cita liniju sa stadnardnog ulaza i vraca njenu duzinu */

int getline(char* line, int max)

{

char *s=line;

int c;

while ( max-->0 && (c=getchar())!=’\n’ && c!=EOF)

*s++ = c; /* ekvivalentno sa *s=c; s++; */

if (c==’\n’)

*s++ = c;

*s = ’\0’;

return s - line;

}

main()

{

char rec[]="zdravo";

char linija[100];

while (getline(linija, 100))

if (sadrzi_string_pok(linija, rec))

63

Page 65: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.2 Niska karaktera i pokazivac na konstantnu nisku

printf("%s",linija);

}

6.2 Niska karaktera i pokazivac na konstantnu nisku

Prilikom deklaracije treba praviti razliku izmedu niza znakova i pokazivaca na kon-stantnu nisku. Ukoliko su date deklaracije:

char poruka[]="danas je lep dan!";

char *pporuka = "danas je lep dan!";

tada vazi:

• poruka je niz znakova koji sadrzi tekst danas je lep dan!. Pojedine znakemoguce je promeniti, na primer, nakon naredbeporuka[0] = ’D’;

poruka ce sadrzati tekst Danas je lep dan!. Niska poruka se uvek odnosi nanovo mesto u memoriji.

• pporuka je pokazivac, koji je inicijalizovan da pokazuje na konstantnu nisku,on moze biti preusmeren da pokazuje na nesto drugo, na primer, moguce jepreusmeriti ovaj pokazivac sledecom naredbompporuka = poruka;

i u ovom slucaju ce pokazivac pporuka pokazivati na mesto u memoriji nakojem se nalazi poruka. Pokusaj modifikacije elementa niske karaktera na kojuukazuje pporuka nije definisan (jer je to konstantna niska). Na primer, rezultatnaredbe pporuka[0]=’D’ je nedefinisan.

Ako deklarisemo

char *pporuka1 = "danas je lep dan!";

char *pporuka2 = "danas je lep dan!";

char *pporuka3 = "danas pada kisa";

tada ce pokazivaci pporuka1 i pporuka2 pokazivati na isto mesto u memoriji, apporuka3 na drugo mesto u memoriji.

Ako uporedimo(pporuka1==pporuka3)

uporedice se vrednosti pokazivaca. Ako uporedimo(pporuka1 < pporuka2)

uporedice se vrednosti pokazivaca. Ako dodelimopporuka1=pporuka3

tada ce pporuka1 dobiti vrednost pokazivaca pporuka3 i pokazivace na isto mestou memoriji. Nece se izvrsiti kopiranje sadrzaja memorije.

Primer 6.10 Nakon deklaracije

char a[] = "informatika";

char *p = "informatika";

64

Page 66: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.3 Pokazivaci na pokazivace

• Loliko elemenata ima niz a?

• Koju vrednost ima a?

• Da li se moze promeniti vrednost a?

• Koju vrednost ima a[0]?

• Da li se moze promeniti vrednost a[0]?

• Koju vrednost ima p?

• Da li se moze promeniti vrednost p?

• Koju vrednost ima p[0]?

• Da li se moze promeniti vrednost p[0]?

6.3 Pokazivaci na pokazivace

Pokazivacke promenljive su podaci kao i svi drugi, pa samim tim imaju svoju adresuu memoriji. Zbog toga je moguce govoriti o pokazivacima na pokazivace. Na primer,ako je definisan pokazivac na int:

int *p;

tada mozemo definisati pokazivac na pokazivac na int:

int **pp;

Ovaj pokazivac moze uzeti adresu pokazivacke promenljive p:

pp = &p;

Nakon toga se pokazivackoj promenljivoj p moze pristupiti i indirektno, preko pokazivacana pokazivac pp, zato sto je *pp ekvivalentno sa p. Takode, celom broju na kojipokazuje p moze se pristupiti i sa **pp zato sto je **pp ekvivalentno sa *p.

6.4 Nizovi pokazivaca

Elementi nizova mogu biti bilo kog tipa, pa i pokazivaci. Na primer, niz pokazivacana karaktere se moze definisati na sledeci nacin:

char * pokazivaci[100];

Ovim se rezervise prostor za niz od 100 pokazivaca na karaktere. Njima se pris-tupa putem indeksa, kako je i uobicajeno. S obzirom da se ime niza uvek ponasakao adresa prvog objekta u nizu, sledi da je izraz pokazivaci tipa ”pokazivac napokazivac na char”. Otuda se uobicajena pokazivacka aritmetika moze primenjivatii na nizove pokazivaca.

65

Page 67: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.4 Nizovi pokazivaca

Primer 6.11 Program ucitava linije iz fajla ulaz.txt i zatim ih ispisuje na stan-dardnom izlazu sortirane po duzini, pocev od najkrace linije.

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <string.h>

#define MAX_RED 1024

#define MAX_REDOVA 1024

/* Funkcija sortira niz pokazivaca na karaktere, pri cemu

je kriterijum sortiranja duzina stringova na koje pokazuju

ti pokazivaci. */

void sortiraj(char *redovi[], int n);

int main()

{

char redovi[MAX_REDOVA][MAX_RED];

char *p_redovi[MAX_REDOVA];

int i, n;

FILE * in;

/* Otvaramo fajl */

in = fopen("ulaz.txt", "r");

/* Proveravamo da li smo uspesno otvorili fajl */

if(in == NULL)

{

printf("Greska! Fajl nije uspesno otvoren!\n");

exit(1);

}

/* Citamo linije, i smestamo ih u dvodimenzioni niz karaktera redovi[] (i-tu

liniju smestamo u niz redovi[i]). Citamo najvise MAX_REDOVA. Pokazivac

p_redovi[i] postavljamo da pokazuje na prvi karakter u nizu redovi[i]. */

for(i = 0; i < MAX_REDOVA; i++)

if(fgets(redovi[i], MAX_RED, in) != NULL)

p_redovi[i] = redovi[i];

else

break;

n = i;

66

Page 68: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.4 Nizovi pokazivaca

/* Zatavaramo fajl */

fclose(in);

/* NAPOMENA: Umesto da sortiramo niz nizova, sto podrazumeva zamenu

vrednosti citavih nizova, efikasnije je da definisemo niz pokazivaca

na karaktere koje cemo da usmerimo najpre na odgovarajuce stringove

koji se nalaze u nizu nizova redovi[] (Pokazivac p_redovi[i] se

se usmerava na string u nizu redovi[i]). Nakon toga se sortira niz

pokazivaca, tj. pokazivaci se ispremestaju tako da redom pokazuju

na stringove po rastucim duzinama. Ovim je sortiranje bitno efikasnije

jer je brze razmeniti dva pokazivaca nego citave nizove. */

/* Sortiramo niz pokazivaca */

sortiraj(p_redovi, n);

/* Prikazujemo linije u poretku rasta duzina */

for(i = 0; i < n; i++)

fputs(p_redovi[i], stdout);

return 0;

}

/* Funkcija razmenjuje vrednosti dva pokazivaca na karaktere.

S obzirom da je potrebno preneti adrese ovih promenljivih,

parametri su tipa "pokazivac na pokazivac na char". */

void razmeni(char **s, char **t)

{

char *p;

p = *s;

*s = *t;

*t = p;

}

/* Funkcija implementira uobicajeni algoritam sortiranja izborom

najmanjeg elementa, pri cemu se pod najmanjim elementom ovde

podrazumeva pokazivac koji pokazuje na string koji je najkraci */

void sortiraj(char *redovi[], int n)

{

int i,j, min;

67

Page 69: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 6.4 Nizovi pokazivaca

for(i = 0; i < n - 1; i++)

{

min = i;

for(j = i + 1; j < n; j++)

if(strlen(redovi[j]) < strlen(redovi[min]))

min = j;

if(min != i)

{

razmeni(&redovi[min], &redovi[i]);

}

}

}

68

Page 70: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 7

Rekurzija

Funkcija1 moze da poziva samu sebe, neposredno ili posredno. Ova pojava se zoverekurzija. Ovakav pristup se cesto koristi prilikom resavanja problema koji imajuprirodnu rekurzivnu definiciju, tj. kod kojih se problem dimenzije n moze jednos-tavno svesti na problem dimenzije n-1 ili neke druge manje dimenzije. Rekurzija jeanalogna matematickoj indukciji.

Ono sto je potencijalni problem kod razumevanja rekurzije je to sto se u jednomtrenutku mogu izvrsavati vise poziva jedne iste funkcije. Na primer, ako funkcijaf() prilikom svog izvrsavanja pozove samu sebe, tada se zapocinje novi poziv istefunkcije. Prethodni poziv ceka da se zavrsi tekuci, a zatim nastavlja sa radom. Pozivrekurzivne funkcije koji je zapocet, a cije izvrsavanje jos nije zavrseno nazivamoaktivni poziv. Svaki poziv funkcije izvrsava se nezavisno od svih ostalih aktivnihpoziva u tom trenutku, zahvaljujuci cinjenici da svaki od aktivnih poziva ima svojesopostvene kopije formalnih parametara i lokalnih podataka. Kada se neki od pozivazavrsi, njegove kopije nestaju iz memorije, ali kopije ostalih poziva i dalje postoje umemoriji. Posledica ovog pristupa je da kada rekurzivna funkcija promeni vrednostisvojih lokalnih podataka, ove promene ne uticu na ostale aktivne pozive, zato stooni imaju svoje lokalne kopije istih podataka.

Kreiranje i odrzavanje lokalnih kopija se jednostavno i efikasno ostvaruje zah-valjujuci tome sto su parametri funkcije i lokalne nestaticke promenljive smestenena sistemskom steku — u pitanju je struktura podataka u kojoj se novi podaci uveksmestaju na vrh a prilikom uklanjanja podataka takodje se uklanja podatak sa vrha,tj. podatak koji je poslednji dodat (LIFO struktura — last in, first out). Prilikompozivanja bilo koje funkcije najpre se na stek smeste njeni argumenti (one vrednostikoje su predate prilikom poziva) a zatim se na vrh steka smeste i lokalne promenljive.Nakon toga se zapocne izvrsavanje tela funkcije. Ako se tom prilikom pozove nekadruga funkcija (ili ta ista, ako je rekurzija u pitanju) tada se na vrh steka dodajuargumenti ovog poziva, kao i lokalne promenljive te funkcije, itd. Kada se funkci-jski poziv zavrsi, tada se sa vrha steka skidaju njegove lokalne promenljive, kao iparametri poziva, nakon cega na vrhu steka ostaju lokalne promenljive prethodnogpoziva itd.

1Tekst preuzet sa www.matf.bg.ac.rs/milan

69

Page 71: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

Lokalni staticki podaci (promenljive deklarisane kao static unutar funkcije) sene kreiraju na steku, vec u statickoj zoni memorije, i zajednicki su za sve pozive.Zato se promene vrednosti ovih promenljivih u jednom pozivu vide i iz drugih ak-tivnih poziva iste funkcije. Zato treba biti oprezan prilikom koriscenja statickihpromenljivih u rekurzivnim funkcijama (obicno se to i ne radi).

Svaka rekurzivna funkcija mora imati izlaz iz rekurzije kao i rekurzivni pozivkojim se problem svodi na problem nizeg reda. Izlaz iz rekurzije je najcesce jednos-tavan, ali ne mora uvek da bude tako.

7.1 Osnovni primeri

Primer 7.1 Racunanje faktorijela prirodnog broja.

#include<stdio.h>

unsigned long faktorijel_iterativno(int n)

{

long f = 1;

int i;

for (i = 1; i<=n; i++)

f *= i;

return f;

}

unsigned long faktorijel(int n)

{

if(n==0)

return 1;

else

return n*faktorijel(n-1);

/* Alternativni zapis:

return n == 0 ? 1 : n*faktorijel(n-1); */

}

main()

{

int n;

unsigned long f;

printf("Unesite n\n");

scanf("%d", &n);

f = faktorijel(n);

70

Page 72: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

printf("f = %d\n",f);

}

Primer 7.2 Racunanje sume prvih n prirodnih brojeva.

#include<stdio.h>

unsigned suma(unsigned n)

{

if(n==0)

return 0;

else

return (n + suma(n-1));

/* Najefikasniji nacin za resavanje ovog problema je

return (n*(n+1))/2; */

}

main()

{

int s,n;

printf("Unesite n\n");

scanf("%d", &n);

s = suma(n);

printf("s = %d\n",s);

}

Primer 7.3 Iterativna i rekurzivna varijanta racunanja sume niza.

int suma_niza_iterativno(int a[], int n)

{

int suma = 0;

int i;

for (i = 0; i<n; i++)

suma+=a[i];

return suma;

}

int suma_niza(int a[], int n)

{

if (n == 1)

return a[0];

else

return suma_niza(a,n-1) + a[n-1];

/* Skracen zapis je:

71

Page 73: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

return n == 1 ? a[0] : suma_niza(a,n-1) + a[n-1];*/

}

Primer 7.4 Stampanje celog broja.

#include<stdio.h>

void printb(long int n)

{

if(n<0)

{

putchar(’-’);

n=-n;

}

if(n>=10)

printb(n/10);

putchar(n % 10 + ’0’);

}

int main()

{

long int b=-1234;

printb(b);

putchar(’\n’);

return 0;

}

Kad funkcija rekurzivno pozove sebe, svakim pozivom pojavljuje se novi skup svihautomatskih promenljivih, koji je nezavisan od prethodonog skupa. Prva funkcijaprintb kao argument dobija broj -12345, ona prenosi 1234 u drugu printb funkciju,koja dalje prenosi 123 u trecu, i tako redom do poslednje koja prima 1 kao argument.Ta funkcija stampa 1 i zavrsava sa radom tako da se vraca na prethodni nivo, nakome se stampa dva i tako redom.

Primer 7.5 Stepenovanje prirodnog broja

• iterativno

• rekurzivno, koristeci cinjenicu da je xk = x · xk−1 i x0 = 1

• rekurzivno, koristeci cinjenicu da je xk = (x2)k

2 ako je k parno ili xk = x(x2)k−1

2

ako je k neparno, i x0 = 1 i x1 = x.

#include <stdio.h>

/* Iterativna verzija prethodne funkcije */

int stepen_iterativno(int x, int k)

{

int i;

72

Page 74: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

int s = 1;

for (i = 0; i<k; i++)

s*=x;

return s;

}

int stepen(int x, int k)

{

printf("Racunam stepen(%d, %d)\n",x,k); /* Ilustracije radi! */

if (k == 0)

return 1;

else

return x*stepen(x, k-1);

/* Krace se moze zapisati na sledeci nacin

return k==0 ? 1 : x*stepen(x, k-1); */

}

/* Efikasnija verzija rekurzivne funkcije. */

int stepen2(int x, int k)

{

printf("Racunam stepen2(%d, %d)\n",x,k); /* Ilustracije radi! */

if (k == 0)

return 1;

else

if (k == 1)

return x;

else

/* Ako je stepen paran*/

if((k%2)==0)

return stepen2(x*x, k/2);

else

return x * stepen2(x*x, k/2);

}

/* Alternativna funkcija stepen2

int stepen2(int n, int k)

{

int p, s;

if (k == 0) s = 1;

else

if (k == 1) s = n;

else

73

Page 75: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

{

p = stepen(n, k/2);

if(k%2 == 0) s = p*p;

else s = p*p*n;

}

return s;

}

*/

main()

{

printf("Stepen je: %d\n", stepen(2, 8));

printf("------------------\n");

printf("Stepen je: %d\n", stepen2(2, 8));

}

/* Izlaz iz programa:

Racunam stepen(2, 8)

Racunam stepen(2, 7)

Racunam stepen(2, 6)

Racunam stepen(2, 5)

Racunam stepen(2, 4)

Racunam stepen(2, 3)

Racunam stepen(2, 2)

Racunam stepen(2, 1)

Racunam stepen(2, 0)

Stepen je: 256

------------------

Racunam stepen2(2, 8)

Racunam stepen2(4, 4)

Racunam stepen2(16, 2)

Racunam stepen2(256, 1)

Stepen je: 256

*/

Primer 7.6 Fibonacijevi brojevi se definisu rekurentno na sledeci nacin:

f(0) = 1, f(1) = 1, f(n) = f(n-1) + f(n-2)

Napisati funkciju koja izracunava n-ti Fibonacijev broj.

#include <stdio.h>

#include <stdlib.h>

/* Rekurzivna implementacija - obratiti paznju na neefikasnost funkcije */

int Fib(int n)

{

printf("Racunam Fib(%d)\n",n); /* Ilustracije radi! */

74

Page 76: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

if((n==0)||(n==1))

return 1;

else

return(Fib(n-1)+Fib(n-2));

/* Alternativni zapis:

return (n == 0 || n == 1) ? 1 : Fib(n-1) + Fib(n-2); */

}

/* Iterativna verzija bez niza */

int Fib_iterativno(int n)

{

/* Promenljiva pp cuva pretposlednji, a p poslednji element niza */

int pp = 1, p = 1;

int i;

for (i = 0; i <= n-2; i++)

{

int pom = pp;

pp = p;

p = p + pom;

}

return p;

}

main()

{

printf("Fib(5) = %d\n", Fib(5));

}

/*

Izlaz iz programa:

Racunam Fib(5)

Racunam Fib(4)

Racunam Fib(3)

Racunam Fib(2)

Racunam Fib(1)

Racunam Fib(0)

Racunam Fib(1)

Racunam Fib(2)

Racunam Fib(1)

Racunam Fib(0)

Racunam Fib(3)

Racunam Fib(2)

Racunam Fib(1)

Racunam Fib(0)

75

Page 77: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

Racunam Fib(1)

Fib(5) = 8

Kako se izvrsava program za vrednost 5:

_______________________________________________________________

Fib(5)?

=> 8 = 5+3

/ \

/ \

/ \

Fib(4)? Fib(3)?

=> 5=3+2 => 3=2+1

/ \ / \

Fib(3)? Fib(2)? Fib(2)? Fib(1)?

=> 3=2+1 => 2=1+1 => 2=1+1 |

/ \ / \ / \ 1

Fib(2)? Fib(1) Fib(1) Fib(0) Fib(1) Fib(0)

=> 2=1+1 | | | | |

/ \ 1 1 1 1 1

Fib(1) Fib(0)

| |

1 1

_______________________________________________________________

*/

Primer 7.7 U slucaju da se rekurzijom problem svodi na vise manjih podproblemakoji se mogu preklapati, postoji opasnost da se pojedini podproblemi manjih dimenz-ija resavaju veci broj puta. Na primer,fibonacci(20) = fibonacci(19) + fibonacci(18)fibonacci(19) = fibonacci(18) + fibonacci(17)tj. problem fibonacci(18) se resava dva puta. Problemi manjih dimenzija ce seresavati jos veci broj puta. Resenje za ovaj problem je kombinacija rekurzije sa tzv.”dinamickim programiranjem” – podproblemi se resavaju samo jednom, a njihovaresenja se pamte u memoriji (obicno u nizovima ili matricama), odakle se koristeako tokom resavanja ponovo budu potrebni.

#include <stdio.h>

#include <stdlib.h>

#define MAX 50

/* Niz koji cuva resenja podproblema. */

int f[MAX];

76

Page 78: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

/* Funkcija izracunava n-ti fibonacijev broj */

int fibonacci(int n)

{

/* Ako je podproblem vec resen, uzimamo gotovo resenje! */

if(f[n] != 0)

return f[n];

/* Izlaz iz rekurzije */

if(n < 2)

return f[n] = 1;

else

/* Rekurzivni pozivi */

return f[n] = fibonacci(n - 1) + fibonacci(n - 2);

}

/* Test program */

int main()

{

int n, i;

/*Inicijalizuje se niz*/

for(i=0; i<MAX; i++)

f[i] = 0;

scanf("%d", &n);

printf("%d\n", fibonacci(n));

return 0;

}

Primer 7.8 Program resava problem tzv. ”hanojskih kula”: data su tri vertikalnastapa, na jednom se nalazi n diskova poluprecnika 1,2,3,... do n, tako da se najvecinalazi na dnu, a najmanji na vrhu. Ostala dva stapa su prazna. Potrebno je pre-mestiti diskove na drugi stap tako da budu u istom redosledu, premestajuci jedan pojedan disk, pri cemu se ni u jednom trenutku ne sme staviti veci disk preko manjeg.

77

Page 79: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

/*

| | |

=== | |

===== | |

======= | |

========= | |

----------- ----------- -----------

X Y Z

===>

...

| | |

| | |

| | ===

| | =====

========= | =======

----------- ----------- -----------

X Y Z

...

===>

| | |

| === |

| ===== |

| ======= |

| ========= |

----------- ----------- -----------

X Y Z

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MAX_TOWER 100

#define MAX_NAME 64

/* Stuktura definise jednu hanojsku kulu */

typedef struct {

int s[MAX_TOWER]; /* Stap sa diskovima */

int n; /* Broj diskova na stapu */

char name[MAX_NAME]; /* Naziv kule */

} Tower;

/* Funkcija postavlja naziv kule na dato ime, a zatim na stap postavlja

78

Page 80: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

diskove velicine n, n-1, ..., 2, 1 redom */

void init_tower(Tower * tower, char * name, int n)

{

int i;

strcpy(tower->name, name);

tower->n = n;

for(i = 0; i < n; i++)

tower->s[i] = n - i;

}

/* Funkcija prikazuje sadrzaj na datoj kuli */

void print_tower(Tower * tower)

{

int i;

printf("%s: ", tower->name);

for(i = 0; i < tower->n; i++)

printf("%d ", tower->s[i]);

putchar(’\n’);

}

/* Funkcija premesta jedan disk sa vrha prve kule na vrh druge kule */

void move(Tower *from, Tower * to)

{

/* Proveravamo da li je potez ispravan */

if(from->n == 0 || (to->n > 0 && from->s[from->n - 1] >= to->s[to->n - 1]))

{

printf("Ilegal move: %d from %s to %s!!\n",

from->s[from->n - 1],

from->name,

to->name );

exit(1);

}

else

{

/* Prikaz opisa poteza */

printf("Moving disc %d from %s to %s\n",

from->s[from->n - 1],

from->name,

to->name);

/* Premestanje diska */

to->s[to->n++] = from->s[--from->n];

79

Page 81: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.1 Osnovni primeri

}

}

/* Rekurzivna funkcija koja premesta n diska sa kule x na kulu y. Kao

pomocna kula koristi se kula z. */

void hanoi(Tower *x, Tower *y, Tower * z, int n)

{

/* Izlaz iz rekurzije */

if(n == 0)

return;

/* Rekurzivno premestamo n - 1 disk sa x na z, pomocu kule y */

hanoi(x, z, y, n - 1);

/* Premestamo jedan disk sa x na y */

move(x,y);

/* Prikaz stanja kula nakon poteza */

print_tower(x);

print_tower(y);

print_tower(z);

/* Premestamo n - 1 disk sa z na y, pomocu kule x */

hanoi(z, y, x, n - 1);

}

/* Test program */

int main()

{

Tower x, y, z;

int n;

/* Ucitavamo dimenziju problema */

scanf("%d", &n);

/* Inicijalizujemo kule. Kula x ima n diskova, ostale su prazne */

init_tower(&x, "X", n);

init_tower(&y, "Y", 0);

init_tower(&z, "Z", 0);

/* Prikaz kula na pocetku */

print_tower(&x);

print_tower(&y);

80

Page 82: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.2 Binarna pretraga

print_tower(&z);

/* Poziv funkcije hanoi() */

hanoi(&x, &y, &z, n);

return 0;

}

7.2 Binarna pretraga

Primer 7.9 Rekurzivna varijanta algoritma binarne pretrage.

#include <stdio.h>

/* Funkcija proverava da li se element x javlja unutar niza

celih brojeva a.

Funkcija vraca poziciju na kojoj je element nadjen odnosno

-1 ako ga nema.

!!!!! VAZNO !!!!!

Pretpostavka je da je niz a uredjen po velicini

*/

int binary_search(int a[], int l, int d, int x)

{

/* Ukoliko je interval prazan, elementa nema */

if (l > d)

return -1;

/* Srednja pozicija intervala [l, d] */

int s = (l+d)/2;

/* Ispitujemo odnos x-a i srednjeg elementa */

if (x == a[s])

/* Element je pronadjen */

return s;

else if (x < a[s])

/* Pretrazujemo interval [l, s-1] */

return binary_search(a, l, s-1, x);

else

/* Pretrazujemo interval [s+1, d] */

return binary_search(a, s+1, d, x);

}

main()

81

Page 83: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.3 MergeSort algoritam

{

int a[] = {3, 5, 7, 9, 11, 13, 15};

int x;

int i;

printf("Unesi element kojega trazimo : ");

scanf("%d",&x);

i = binary_search(a, 0, sizeof(a)/sizeof(int)-1, x);

if (i==-1)

printf("Elementa %d nema\n", x);

else

printf("Pronadjen na poziciji %d\n", i);

}

7.3 MergeSort algoritam

Primer 7.10 Sortiranje niza celih brojeva ucesljavanjem - MergeSort algoritam.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MAX 100

/* sortiranje ucesljavanjem */

void merge_sort (int a[], int l, int d)

{

int s;

int b[MAX]; /* pomocni niz */

int i, j, k;

/* Izlaz iz rekurzije */

if (l >= d)

return;

/* Odredjujemo sredisnji indeks */

s = (l + d) / 2;

/* rekurzivni pozivi */

merge_sort (a, l, s);

merge_sort (a, s + 1, d);

/* Inicijalizacija indeksa. Indeks i prolazi

krozi levu polovinu niza, dok indeks j

82

Page 84: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.3 MergeSort algoritam

prolazi kroz desnu polovinu niza. Indeks

k prolazi kroz pomocni niz b[] */

i = l;

j = s + 1;

k = 0;

/* "ucesljavanje" koriscenjem pomocnog niza b[] */

while(i <= s && j <= d)

{

if(a[i] < a[j])

b[k++] = a[i++];

else

b[k++] = a[j++];

}

while(i <= s)

b[k++] = a[i++];

while(j <= d)

b[k++] = a[j++];

/* prepisujemo "ucesljani" niz u originalni niz */

for (k = 0, i = l; i <= d; i++, k++)

a[i] = b[k];

}

/* Test program */

int main(int argc, char ** argv)

{

int a[MAX];

int n, i;

int x;

/* Niz brojeva se zadaje na komandnoj liniji */

for(n = 0; n + 1 < argc && n < MAX; n++)

a[n] = atoi(argv[n + 1]);

/* Poziv funkcije */

merge_sort(a, 0, n - 1);

/* Prikaz niza */

for(i = 0; i < n; i++)

printf("%d ", a[i]);

83

Page 85: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.4 QuickSort algoritam

putchar(’\n’);

return 0;

}

7.4 QuickSort algoritam

Primer 7.11 Sortiranje niza celih brojeva - QuickSort algoritam.

/* Funkcija menja mesto i-tom i j-tom elementu niza a */

void swap(int a[], int i, int j)

{

int tmp = a[i];

a[i] = a[j];

a[j] = tmp;

}

/* Funkcija particionise interval [l, r] niza a, tako da levo od pivota

budu svi elementi manji od njega, a desno svi veci od njega.

Ovi podnizovi ne moraju biti sortirani.

Funkcija vraca poziciju pivota.

*/

int partition(int a[], int l, int r)

{

int last, i;

/* Srednji element uzimamo za pivot i postavljamo ga na pocetak */

swap(a, l, (l + r)/2);

/* Niz organizujemo tako da pivot postavimo na pocetak, zatim da iza

njega budu svi elementi manji od njega, pa zatim svi elementi koji

su veci od njega tj.

pivot < < < < > > > >

*/

/* last je pozicija poslednjeg elementa niza za koji znamo da je

manji od pivota. U pocetku takvih elemenata nema */

last = l;

for (i = l+1; i <= r; i++)

/* Ukoliko je element na poziciji i manji od pivota,

postavljamo ga iza niza elemenata manjih od pivota,

menjajuci mu mesto sa prvim sledecim */

if (a[i] < a[l])

swap(a, ++last, i);

84

Page 86: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 7.4 QuickSort algoritam

/* Zahtevanu konfiguraciju < < < < pivot > > > > dobijamo tako

sto zamenimo mesto pivotu i poslednjem elementu manjem od njega */

swap(a, l, last);

/* Pivot se sada nalazi na poziciji last */

return last;

}

/* Funkcija sortira deo niza brojeva a izmedju pozicija l i r*/

void quick_sort(int a[], int l, int r)

{

int i, last;

/* Ukoliko je interval [l, r] prazan nema nista da se radi */

if (l >= r)

return;

/* Particionisemo interval [l, r] */

int pivot = partition(a, l, r);

/* Rekurzivno sortiramo elemente manje od pivota */

quick_sort(a, l, pivot-1);

/* Rekurzivno sortiramo elemente vece pivota */

quick_sort(a, pivot+1, r);

}

main()

{

int a[] = {5, 8, 2, 4, 1, 9, 3, 7, 6};

int n = sizeof(a)/sizeof(int);

int i;

quick_sort(a, 0, n-1);

for (i = 0; i < n; i++)

printf("%d ", a[i]);

printf("\n");

}

85

Page 87: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 8

Pokazivaci na funkcije

Binarne instrukcije koje cine funkciju su takode negde u memoriji, i imaju svojuadresu. Zato je moguce je definisati pokazivac na funkciju. Na primer, deklaracija:

int (*fp)(int);

definise promenljivu f, koja je tipa ”pokazivac na funkciju koja prihvata argumenttipa int, i vraca vrednost tipa int”. Ova promenljiva sadrzi adresu neke funkcijetog tipa. Na primer, ako je deklarisana funkcija:

int f(int a);

Tada je moguce dodeliti:

fp = &f; /* Operator adrese nije neophodan. */

Promenljiva fp sadrzi adresu funkcije f. Funkcija f se sada moze pozivati derefer-enciranjem pokazivaca fp:

(*fp)(3);

Zagrade oko izraza *fp su neophodne zbog prioriteta operatora. Dereferenciranjempokazivaca na funkciju dobija se funkcija, koja se onda poziva na uobicajen nacin.

Primer 8.1 Program demonstrira upotrebu pokazivaca na funkcije.

#include <stdio.h>

int kvadrat(int n)

{

return n*n;

}

int kub(int n)

{

return n*n*n;

}

86

Page 88: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Pokazivaci na funkcije

int parni_broj(int n)

{

return 2*n;

}

/* Funkcija izracunava sumu brojeva f(i) za i iz intervala [1, n].

int (*f) (int) u argumentu funkcije sumiraj je pokazivac

na funkciju sa imenom f, koja kao argument prima promenljivu

tipa int i vraca kao rezultat vrednost tipa int

*/

int sumiraj(int (*f) (int), int n)

{

int i, suma=0;

for (i=1; i<=n; i++)

suma += (*f)(i);

return suma;

}

main()

{

/* U pozivu funkcije sumiraj, imena funkcija kvadrat, kub i parni_broj su

zapravo istovremneo i adrese funkcija pa operator & nije neophodan

ali nije greska ukoliko se se operator & ipak koristi ispred

imena funkcije. */

printf("Suma kvadrata brojeva od jedan do 3 je %d\n", sumiraj(kvadrat,3));

printf("Suma kubova brojeva od jedan do 3 je %d\n", sumiraj(kub,3));

printf("Suma prvih pet parnih brojeva je %d\n", sumiraj(parni_broj,5));

/* Ili:

printf("Suma kvadrata brojeva od jedan do 3 je %d\n", sumiraj(&kvadrat,3));

printf("Suma kubova brojeva od jedan do 3 je %d\n", sumiraj(&kub,3));

printf("Suma prvih pet parnih brojeva je %d\n", sumiraj(&parni_broj,5));

*/

}

/*Izlaz:

Suma kvadrata brojeva od jedan do 3 je 14

Suma kubova brojeva od jedan do 3 je 36

Suma prvih pet parnih brojeva je 30

*/

Primer 8.2 Program tabelarno ispisuje vrednosti raznih matematickih funkcija u

87

Page 89: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Pokazivaci na funkcije

tackama datog intervala sa datim korakom.

#include <stdio.h>

#include <math.h>

/*

Zaglavlje math.h sadrzi deklaracije razih matematickih funkcija. Izmedju

ostalog, to su sledece funkcije:

double sin(double x);

double cos(double x);

double tan(double x);

double asin(double x);

double acos(double x);

double atan(double x);

double atan2(double y, double x);

double sinh(double x);

double cosh(double x);

double tanh(double x);

double exp(double x);

double log(double x);

double log10(double x);

double pow(double x, double y);

double sqrt(double x);

double ceil(double x);

double floor(double x);

double fabs(double x);

*/

/* Funkcija tabeliraj() prihvata granice intervala a i b, korak h, kao i

pokazivac f koji pokazuje na funkciju koja prihvata double argument, i

vraca double rezultat. Za tako datu funkciju ispisuje njene vrednosti u

intervalu [a,b] sa korakom h */

void tabeliraj(double a, double b, double h, double (*f)(double))

{

double x;

printf("-----------------------\n");

for(x = a; x <= b; x+=h)

printf("| %8.3f | %8.3f |\n", x, (*f)(x));

printf("-----------------------\n");

}

88

Page 90: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.1 Funkcija qsort iz standardne biblioteke

/* Funkcija main */

int main()

{

double a, b, h;

/* Unosimo granice intervala */

printf("Uneti granice intervala: ");

scanf("%lf%lf", &a, &b);

/* Unosimo korak */

printf("Uneti korak: ");

scanf("%lf", &h);

/* Testiramo funkciju tabeliraj() za sin(), cos() i exp() */

printf("sin(x)\n");

tabeliraj(a, b, h, &sin);

printf("cos(x)\n");

tabeliraj(a, b, h, &cos);

printf("exp(x)\n");

tabeliraj(a, b, h, &exp);

return 0;

}

8.1 Funkcija qsort iz standardne biblioteke

Prototip funkcije qsort iz stdlib.h je:

void qsort(void *niz, int duzina_niza, int velicina_elementa_niza,

int (*poredi)(const void*, const void*) )

Ova funkcija sortira nizniz[0], niz[1], ..., niz[duzina_niza - 1]

elemenata velicine velicina_elementa_niza koriscenjem funkcije poredi koja uporedujedva elementa niza.

Posto je void* tip niza niz to obezbeduje mogucnost koriscenja ove funkcije zanizove razlicitih tipova podataka. Da bi funkcija qsort znala sa kakvim elementimabarata, potrebno joj je proslediti i velicinu jednog elementa u nizu (ovo je kljucnoza korak u kome se vrisi razmena elemenata niza — funkcija qsort mora da znakoliko bajtova zauzima jedan element niza kako bi mogla adekvatno da vrsi kopiranjememorije).

Funkcija poredi vrsi poredenje dva elementa niza. Kada ova funkcija vracapozitivnu vrednost ako je prvi element veci od drugog, 0 ako su jednaki i negativnuvrednost ako je prvi element manji, tada ce se niz sortirati u rastucem poretku.Modifikacijom ove funkcije niz se moze sortirati u opadajucem poretku (ukoliko

89

Page 91: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.1 Funkcija qsort iz standardne biblioteke

vraca pozitivnu vrednost ako je prvi manji, 0 ako su jednaki i negativnu vrednostako je prvi veci).

Kvalifikator const

Kljucna rec const u programskom jeziku C sluzi za definisanje konstanti. Akonapisemo:

const int a = 2;

tada se u daljem toku programa promenljivoj a ne moze dodeljivati vrednost. Zarazliku od konstanti definisanih define direktivom ova konstanta ima jasno definisantip koji prevodilac proverava prilikom upotrebe.

Treba naglasiti, medjutim, da ako se napise:

const int * p = &a;

tada se const ne odnosi na promenljivu p, vec na podatak tipa int na koji p

pokazuje. Dakle, nije pokazivac konstantan, vec on pokazuje na konstantan podatak.Na primer:

int b;

p = &b;

je dozvoljeno, ali:

*p = 4;

nije dozvoljeno, zato sto je p pokazivac na konstantan int (odnosno, kroz pokazivacp nije moguce menjati vrednost koja se nalazi na adresi na koju on ukazuje). Ovose moze razumeti tako sto se deklaracija procita sa desna u levo:

const int * p; /* p je pokazivac na int koji je konstantan*/

Slicno bi bilo i ako napisemo:

int const * p; /* p je pokazivac na konstantan int. */

Medutim, ako napisemo:

int * const p; /* p je konstantan pokazivac na int. */

Kombinacija ovoga je:

const int * const p; /* p je konstantan pokazivac na int

koji je konstantan.*/

Dakle, u ovom poslednjem slucaju konstantni su i pokazivac i ono na sta on pokazuje.

90

Page 92: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.1 Funkcija qsort iz standardne biblioteke

Upotreba qsort

Primer 8.3 Upotrebom qsort funkcije iz standardne biblioteke izvrsiti sortiranjeniza celih i niza realnih brojeva.

/* Ilustracija upotrebe funkcije qsort iz stdlib.h

Sortira se niz celih brojeva.

*/

#include <stdlib.h>

#include <stdio.h>

/* const znaci da ono na sta pokazuje a (odnosno b)

nece biti menjano u funkciji */

int poredi(const void* a, const void* b)

{

/* Skracen zapis za

int br_a = *(int*)a;

int br_b = *(int*)b;

return br_a-br_b;

*/

return *((int*)a)-*((int*)b);

}

int poredi_float(const void* a, const void* b)

{

float br_a = *(float*)a;

float br_b = *(float*)b;

if (br_a > br_b) return 1;

else if (br_a < br_b) return -1;

else return 0;

}

main()

{

int i;

int niz[]={3,8,7,1,2,3,5,6,9};

float nizf[]={3.0,8.7,7.8,1.9,2.1,3.3,6.6,9.9};

int n=sizeof(niz)/sizeof(int);

qsort((void*)niz, n, sizeof(int), poredi);

for(i=0; i<n; i++)

printf("%d",niz[i]);

91

Page 93: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.1 Funkcija qsort iz standardne biblioteke

n=sizeof(nizf)/sizeof(float);

qsort((void*)nizf, n, sizeof(float), poredi_float);

for(i=0; i<n; i++)

printf("%f",nizf[i]);

}

Primer 8.4 Sortiranje reci. Ako se sortira niz stringova, onda svaki element jesam po sebi pokazivac tipa char *, te funkcija poredenja tada prima podatke tipachar ** koji se konvertuju u svoj tip i derefenciraju radi dobijanja podataka tipachar *.

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

/* Funkcija koja vrsi leksikografsko poredjenje dve reci.

Vraca kao rezultat >0 ukoliko je prva rec veca, 0 ako su jednake

i <0 ako je prva rec manja. Sortiranje ce biti u rastucem poretku.*/

int poredi_leksikografski(const void* a, const void* b)

{

char *s1 = *(char **)a;

char *s2 = *(char **) b;

return strcmp(s1, s2);

/* Prethodno je ekvivalentno sa:

return strcmp(*(char**)a,*(char**)b); */

}

/* Funkcija koja vrsi poredjenje po duzini dve reci,

sortiranje koje koristiovu funkciju ca biti opadajuce!*/

int poredi_po_duzini(const void* a, const void* b)

{

char *s1 = *(char **) a;

char *s2 = *(char **) b;

return strlen(s1) - strlen(s2);

/* Prethodno je ekvivalentno sa:

return strlen(*(char**)b)-strlen(*(char**)a); */

}

main()

{

int i;

char* nizreci[]={"Jabuka","Kruska","Sljiva","Dinja","Lubenica"};

qsort((void*)nizreci, sizeof(nizreci)/sizeof(char *),

sizeof(char*), poredi_po_duzini);

92

Page 94: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.2 Funkcija bsearch iz standardne biblioteke

for (i=0; i<5; i++)

printf("%s\n",nizreci[i]);

qsort((void*)nizreci, sizeof(nizreci),

sizeof(char*), poredi_leksikografski);

printf("Sortirano:\n");

for (i=0; i<sizeof(nizreci); i++)

printf("%s\n",nizreci[i]);

}

/*

Izlaz:

Dinja Jabuka Kruska Lubenica Sljiva

Sortirano:

Lubenica Kruska Jabuka Sljiva Dinja

*/

8.2 Funkcija bsearch iz standardne biblioteke

Prototip funkcije bsearch iz stdlib.h je:

void *bsearch (const void *kljuc, const void *niz, int duzina_niza,

int velicina_elementa_niza,

int (*poredi)(const void*, const void*) )

Ova funkcija u nizuniz[0], niz[1], ..., niz[duzina_niza - 1]

elemenata velicine velicina_elementa_niza trazi element koji ima krakteristikuna koju ukazuje pokazivac kljuc.

Funkcija poredi moze da se razlikuje od funkcije poredi koju prima funkcijaqsort. Njen prvi argument je kljuc pretrage, a drugi argument je element niza. Toznaci da ova dva argumenta mogu ali i ne moraju da budu istog tipa. Na primer,ako u nizu studenata trazimo onog studenta koji ima indeks mm08123 tada je kljuctipa char* a element niza je tipa strukture student, znaci kljuc pretrage i elementniza su razlicitog tipa, pa u skladu sa tim funkcija poredenja prima dva argumentarazlicitog tipa. S druge strane, ako u nizu studenata trazimo onog studenta koji imaindeks mm08123, i ime Pera i prezime Peric, tada je kljuc istog tipa kao i elementniza, i tada funkcija poredenja prima argumente istog tipa (i analogna je funkcijiporedenja koja se pise za funkciju qsort). Dakle, poredenje se vrsi po kljucu kojimoze biti istog tipa kao i elementi niza ali i ne mora.

Funkcija poredi treba da vrati vrednost 0 ako je prvi argument (kljuc pretrage)veci (u smislu kljuca pretrage) od drugog argumenta (koji je element niza), 0 ako sujednaki (u smislu kljuca pretrage) i broj manji od 0 ako je prvi argument manji (usmislu kljuca pretrage) od drugog argumenta, ukoliko su elementi u nizu niz uredeni

93

Page 95: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.2 Funkcija bsearch iz standardne biblioteke

u rastucem redosledu u odnosu na dati kljuc. Funkcija bsearch vraca pokazivac napronadeni element ili NULL ukoliko element sa datom vrednoscu kljuca ne postoji.

Primer 8.5 Binarno pretrazivanje - koriscenje ugradene bsearch funkcije.

/* Funkcija ilustruje koriscenje ugradjene funkcije bsearch */

#include <stdlib.h>

int poredi(const void* a, const void *b)

{

return *(int*)a-*(int*)b;

}

main()

{

int x=-1;

int niz[]={1,2,3,4,5,6,7,8,9,10,11,12};

int* elem=(int*)bsearch((void*)&x,

(void*)niz,

sizeof(niz)/sizeof(int),

sizeof(int),

poredi);

if (elem==NULL)

printf("Element nije pronadjen\n");

else

printf("Element postoji na poziciji %d\n",elem-niz);

}

Primer 8.6 Sa ulaza se unose reci. Program broji pojavljivanja svake od kljucnihreci programskog jezika C. Na kraju se reci ispisuju opadajuce po broju pojavljivanja.

#include <stdio.h>

#include <stdlib.h>

/* Svaka kljucna rec se odlikuje imenom i brojem pojavljivanja */

typedef struct _keyword

{

char word[20];

int num;

} keyword;

/* Kreiramo niz struktura sortiranih leksikografski

po imenu kljucne reci, kako bismo ubrzali pronalazak reci */

94

Page 96: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.2 Funkcija bsearch iz standardne biblioteke

keyword keywords[]={ {"break",0},

{"continue",0},

{"float",0},

{"for",0},

{"if",0},

{"return",0},

{"struct",0},

{"while",0}

};

/* Funkcija cita sledecu rec sa standardnog ulaza */

int getword(char word[], int lim)

{

int c, i=0;

while(!isalpha(c=getchar()) && c!=EOF)

;

if (c==EOF)

return -1;

do

{

word[i++]=c;

} while(--lim>0 && isalpha(c=getchar()));

word[i]=’\0’;

return i;

}

/* Funkcija leksikografskog poredjenja za bsearch */

int cmp(const void* a, const void* b) {

/* Funkcija strcmp prima kao argumente dva

pokazivaca na karaktere. U ovom slucaju, prvi argument je

rec koju trazimo u nizu --- prilikom poziva funkcije

bsearch funkciji se kao prvi argument prosledjuje pokazivac

na kljuc pretrage - to znaci da ukoliko se funkciji prosledi

adresa podatka tipa char* tada bi bilo potrebno uraditi

dereferenciranje sa *(char **)a; medjutim, ukoliko se prosledi

samo promenljiva tipa char* tada je potrebno uraditi

dereferenciranje sa (char *)a. Drugi argument funkcije

je element niza sa kojim se vrsi poredjenje. Pokazivac

b konvertujemo u pokazivac na strukturu

keyword a zatim posmatramo rec koja se tu

cuva */

return strcmp((char*)a, (*(keyword*)b).word); }

95

Page 97: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.2 Funkcija bsearch iz standardne biblioteke

/* Funkcija numerickog poredjenja za qsort */

int numcmp(const void* a, const void* b)

{

return ((*(keyword*)b).num-(*(keyword*)a).num);

}

main()

{

char word[80];

int i;

/* Broj kljucnih reci */

int num_of_keywords=sizeof(keywords)/sizeof(keyword);

/* Citamo reci */

while (getword(word,80)!=-1)

{

/* Trazimo rec u spisku kljucnih reci binarnom pretragom */

/* Prvi argument funkcije bsearch treba da bude pokazivac

na rec koju trazimo. Posto je word tipa niza karaktera, tada

uzimanjem adrese od word dobijamo istu vrednost kao i word,

tj nema razlike izmedju word i &word. Zbog toga se u funkciji

poredjenja koristi dreferenciranje na sledeci nacin

strcmp((char*)a, (*(keyword*)b).word); }

Da je word tipa char*, tada bi poziv funkcije bsearch

sa argumentom &word povlacilo da funkcija poredjenja

koristi dereferenciranje na sledeci nacin

strcmp(*(char**)a, (*(keyword*)b).word); }

*/

keyword* k=(keyword*)bsearch((void*)word,

(void*)keywords,

num_of_keywords,

sizeof(keyword),

cmp);

/* Ukoliko je pronadjena uvecavamo broj pojavljivanja */

if (k!=NULL)

(*k).num++;

}

/* Sortiramo niz na osnovu broja pojavljivanja */

qsort((void*)keywords, num_of_keywords, sizeof(keyword), numcmp);

/* Vrsimo ispis */

for (i=0; i<num_of_keywords; i++)

96

Page 98: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.3 Funkcije lsearch i lfind

printf("%s %d\n", keywords[i].word, keywords[i].num);

}

8.3 Funkcije lsearch i lfind

Funkcije za linearnu pretragu standardne biblioteke su funkcije lsearch i lfind.Funkcija lsearch ima sledece argumente

void *lsearch(const void *kljuc, void *niz, int *duzina_niza,

int velicina_elementa_niza,

int (*comp)(const void *, const void *));

Ova funkcija ima iste argumente kao i funkcija bsearch() — jedina razlika je u tomesto se kao treci argument ne predaje duzina niza vec adresa celobrojne promenljiveu kojoj se nalazi duzina niza. Ovo je zato sto funkcija lsearch() ukoliko linearnompretragom ne pronade element koji se trazi, umece trazeni element na kraj niza,a duzinu niza uvecava za jedan (sto cini tako sto promenljivoj pozivajuce funkcijepristupa preko pokazivaca i menja je). Takode, posto postiji mogucnost umetanjaelementa u niz, kao kljuc pretrage potrebno je koristiti kljuc koji je istog tipa kao stosu to elementi niza. Funkcija za uporedjivanja elemenata na koju pokazuje pokazi-vac comp treba da zadovoljava ista pravila kao i kod funkcije bsearch. Medjutim,s obzirom da se kod linearne pretrage koristi iskljucivo poredenje na jednakost, do-voljno je da funkcija za uporedjivanje vraca 0 ako su objekti koji se uporedjujujednaki, a razlicito od nule u suprotnom.

Funkcija lfind:

void *lfind(const void *kljuc, void *niz, int *duzina_niza,

int velicina_elementa_niza,

int (*comp)(const void *, const void *));

funkcionise isto kao i lsearch, s tom razlikom sto ne umece novi element u slucajuneuspesne pretrage, vec vraca NULL (funkcija lsearch u slucaju neuspesne pretrageumece trazeni element na kraj i vraca njegovu adresu). Za funkciju lfind moguce jekoristiti kljuc pretrage koji nije istog tipa kao elementi niza, u tom slucaju potrebnoje adekvatno definisati funkciju poredenja.

8.4 Pokazivaci

Primer 8.7 Razlicite deklaracije:

int *p; pokazivac na int

int **p; pokazivac na pokazivac na tip int

int* p[10]; niz od 10 elemenata tipa pokazivaca na int

int *p[10]; niz od 10 elemenata tipa pokazivaca na int

97

Page 99: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 8.4 Pokazivaci

int p[5][10]; pet nizova od po 10 elemenata tipa int

int (*p)[10]; pokazivac na niz od deset celobrojnih vrednosti

int* p(); funkcija bez argumenata koja vraca pokazivac na int

int *p(); funkcija bez argumenata koja vraca pokazivac na int

int (*p)(); pokazivac na funkciju bez argumenata koja vraca int

int *(*p)(); pokazivac na funkciju bez argumenata koja vraca pokazivac na int

int* (*p)(); pokazivac na funkciju bez argumenata koja vraca pokazivac na int

Razlika izmedu int** t i int (*t)[5]

int x;

int (*t)[5];

int aa[4][5];

t = aa;

x=t[2][2]; /*Da je t tipa int** ova dodela ne bi bila moguca!*/

98

Page 100: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 9

Dinamicka alokacija memorije

Do sada smo memoriju za podatke rezervisali na dva nacina1:

Staticka alokacija — podaci se kreiraju staticki prilikom pokretanja programai ostaju u memoriji do kraja njegovog izvrsavanja. Ovakvu alokaciju imajuglobalne promenljive (promenljive definisane van svake funkcije ili bloka) kao istaticke lokalne promenljive (deklarisane kljucnom recju static unutar funkcijeili bloka).

Automatska alokacija — podaci se kreiraju automatski prilikom ulaska u funkcijuili blok, i nestaju kada se iz funkcije/bloka izade. Ovakvo ponasanje imajusve lokalne promenljive koje nisu deklarisane kao static. Prostor za ovepromenljive se rezervise na programskom steku.

Glavni nedostatak oba ova pristupa je to sto programer u vreme pisanja programamora da predvidi koliko ce mu prostora biti potrebno za podatke. Cesto tako nestonije moguce, jer dimenzije problema mogu varirati. Drugi nedostatak je to sto jezivotni vek svakog podatka jasno definisan gornjim pravilima. Programer moze zeletida nakon kreiranja nekog objekta u memoriji on ostane tu dokle god je potreban,te da ga kasnije ukloni kada vise za njim nema potrebe.

Svi ovi problemi resavaju se dinamickom alokacijom memorije. Pored statickezone i steka, u memoriji postoji i odredeni prostor koji se naziva hip (eng. heap). Uovom prostoru se po potrebi mogu kreirati objekti proizvoljne velicine. Taj prostorse kasnije moze (i treba) osloboditi, kada programer vise nema potrebu da koristitaj prostor.

U programskom jeziku C, ovo se moze ostvariti pozivom funkcije standardnebiblioteke malloc() koja je deklarisana u zaglavlju stdlib.h:

void * malloc(int n);

Ova funkcija alocira na hipu kontinualni prostor od n bajtova, i vraca adresu pocetkatog prostora ili vraca vrednost NULL ukoliko zahtev ne moze da se ispuni. Jedini nacinda se ovom prostoru pristupi je preko pokazivaca (jer ne postoji ime promenljive kaokod statickih i automatskih promenljivih).

Osim funkcije malloc() postoje i druge funkcije za alokaciju, na primer:1Tekst preuzet sa sajta www.matf.bg.ac.rs/~milan

99

Page 101: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Dinamicka alokacija memorije

void * calloc(int n, int s);

Ova funkcija alocira prostor za n susednih elemenata velicine s bajtova (zapravoalocira n*s bajtova). Ova funkcija jos i inicijalizuje rezervisani prostor na vrednost0, za razliku od malloc() funkcije koja prostor ostavlja neinicijalizovan. Funkcija

void * realloc(void *p, int n);

vrsi realokaciju prostora na koji pokazuje pokazivac p za koji je memorija prethodnobila dinamicki alocirana. Alocira se n bajtova, kopira se stari sadrzaj u novi prostor,a zatim se stari prostor oslobada. Ova funkcija se obicno koristi da bi se prosiriopostojeci dinamicki alocirani prostor.

Prostor alociran prethodnim funkcijama se moze dealocirati pozivom funkcije:

void free(void *p);

koja je takode deklarisana u zaglavlju stdlib.h, i koja za argument mora imatiadresu koju je funkcija (m|c|re)alloc() vratila u nekom od prethodnih poziva(drugim recima, ne moze se proizvoljan prostor oslobadati ovom funkcijom, vecsamo prostor na hipu alociran (m|c|re)alloc()-om). Kada se jednom dealocira,prostor vise ne sme da se koristi (vrlo je verovatno da ce ga kasnije funkcija kojavrsi alokaciju dodeliti nekom drugom).

Trenutak oslobadanja prostora na hipu odreduje programer. Nebitno je da lismo izasli iz funkcije ili bloka u kome je funkcija za alokaciju memorije pozvana,prostor ostaje rezervisan dokle god ne pozovemo free() sa argumentom adrese togprostora. Ova memorija se moze koristiti i u drugim funkcijama sve dok imamopokazivac kojim mozemo da joj pristupimo.

Tiha greska je ako ”izgubimo” pokazivac na dinamicki alocirani prostor, a da ganismo prethodno dealocirali. U ovom slucaju taj prostor ostaje rezervisan do krajaizvrsavanja programa, bez mogucnosti da ga koristimo ili da ga kasnije obrisemo(jer nemamo njegovu adresu). Ovaj fenomen se naziva ”curenje memorije” (eng.memory leak). Odgovornost je programera kao i odlika dobrog stila da se dinamickiprostor uvek oslobodi kada vise nije potreban. Takode, greska je i ukoliko programerpokusa dva puta da oslobodi istu memoriju ili da pristupi memoriji koja je vecoslobodena (ovo dovodi do greske prilikom izvrsavanja programa).

Primer 9.1 Dinamicka alokacija memorije za niz.

#include <stdio.h>

#include <stdlib.h>

main()

{

int n;

int i;

int *a;

/* Ranije smo alocirali konstantan prostor za niz,

100

Page 102: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Dinamicka alokacija memorije

sada cemo alocirati tacno onoliko koliko nam je

potrebno. */

printf("Unesi broj clanova niza : ");

scanf("%d", &n);

/* Kao da smo mogli da deklarisemo

int a[n];

Alocira se n mesta za podatke tipa int.

Funkcija malloc vraca vrednost tipa void*

tako da vrsimo kastovanje.

*/

a = (int*)malloc(n*sizeof(int));

/* Kad god se vrsi alokacija memorije mora se

proveriti da li je alokacija uspesno izvrsena!!! */

if (a == NULL)

{

printf("Nema slobodne memorije\n");

exit(1);

}

/* Od ovog trenutka a koristimo kao obican niz */

for (i = 0; i<n; i++)

scanf("%d",&a[i]);

/* Stampamo niz u obrnutom redosledu */

for(i = n-1; i>=0; i--)

printf("%d",a[i]);

/* Oslobadjamo memoriju*/

free(a);

}

Primer 9.2 Demonstracija funkcije calloc - funkcija inicijalizuje sadrzaj memo-rije na 0.

#include <stdio.h>

#include <stdlib.h>

main()

{

int *m, *c, i, n;

printf("Unesi broj clanova niza : ");

scanf("%d", &n);

101

Page 103: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Dinamicka alokacija memorije

/* Niz m NE MORA garantovano da ima sve nule */

m = (int*) malloc(n*sizeof(int));

if (m == NULL) {

printf("Greska prilikom alokacije memorije!\n");

exit(1);

}

/* Niz c MORA garantovano da ima sve nule */

c = (int*) calloc(n, sizeof(int));

if (c == NULL) {

printf("Greska prilikom alokacije memorije!\n");

free(m);

exit(1);

}

/*Odstampace se sadrzaj neinicijalizovane memorije*/

for (i = 0; i<n; i++)

printf("m[%d] = %d\n", i, m[i]);

/*Odstampace se nule*/

for (i = 0; i<n; i++)

printf("c[%d] = %d\n", i, c[i]);

/* Oslobadja se rezervisana memorija. */

free(m);

free(c);

}

Primer 9.3 Da bi funkcija omogucila pozivajucoj funkciji da koristi vrednosti nizakoji je kreirala, neophodno je da memorija kreiranog niza bude dinamicki alociranakako bi nastavila da postoji i nakon zavrsetka rada funkcije. U tom slucaju, pozi-vajuca funkcija ima odgovornost i da se brine o dealokaciji rezervisanog prostora.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MAX 1000

/* Funkcija dinamicki kreira niz karaktera u koji smesta rezultat

nadovezivanja stringova s i t. Adresa niza se vraca kao povratna

vrednost. */

char * nadovezi_stringove(char *s, char *t)

{

/* Dinamicki kreiramo prostor dovoljne velicine */

char *p = (char*) malloc((strlen(s) + strlen(t) + 1) * sizeof(char));

102

Page 104: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Dinamicka alokacija memorije

/* Proveravamo uspeh alokacije */

if(p == NULL)

{

fprintf(stderr, "malloc() greska!\n");

exit(1);

}

/* Kopiramo i nadovezujemo stringove */

strcpy(p,s);

strcat(p,t);

/* Vracamo p */

return p;

}

int main()

{

char * p = NULL;

char s[MAX], t[MAX];

/* Ucitavamo stringove */

scanf("%s", s);

scanf("%s", t);

/* Nadovezujemo stringove */

p = nadovezi_stringove(s,t);

/* Prikazujemo rezultat */

printf("%s\n", p);

/* Oslobadjamo memoriju*/

free(p);

return 0;

}

Primer 9.4 Niz pokazivaca na nizove razlicitih duzina.

#include <stdio.h>

#include <stdlib.h>

main()

{

/* Niz od tri elemenata tipa int*/

int nizi[3];

103

Page 105: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Dinamicka alokacija memorije

/* Niz od tri elemenata tipa int*, dakle

niz od tri pokazivaca na int*/

int* nizip[3];

/* Alociramo memoriju za prvi element niza*/

nizip[0] = (int*) malloc(sizeof(int));

if (nizip[0] == NULL)

{

printf("Nema slobodne memorije\n");

exit(1);

}

/* Upisujemo u prvi element niza broj 5*/

*nizip[0] = 5;

printf("%d", *nizip[0]);

/* Alociramo memoriju za drugi element niza.

Drugi element niza pokazuje na niz od dva

elementa*/

nizip[1] = (int*) malloc(2*sizeof(int));

if (nizip[1] == NULL) {

printf("Nema slobodne memorije\n");

free(nizip[0]);

exit(1);

}

/* Pristupamo prvom elementu na koji pokazuje

pokazivac nizip[1]*/

*(nizip[1]) = 1;

/* Pristupamo sledecem elementu u nizu na koji pokazuje

nizip[1].

*/

*(nizip[1] + 1) = 2;

printf("%d", nizip[1][1]);

/* Alociramo memoriju za treci element niza nizip. */

nizip[2] = (int*) malloc(sizeof(int));

if (nizip[2] == NULL) {

printf("Nema slobodne memorije\n");

free(nizip[0]);

free(nizip[1]);

exit(1);

}

104

Page 106: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

*(nizip[2]) = 2;

printf("%d", *(nizip[2]));

/* Oslobadjamo memoriju. */

free(nizip[0]);

free(nizip[1]);

free(nizip[2]);

}

9.1 Matrice

Primer 9.5 Staticka alokacija prostora za matricu.

#include <stdio.h>

main()

{

int i, j;

/* Deklaracija i inicijalizacija elemenata matrice */

int a[3][3] = {{0, 1, 2}, {10, 11, 12}, {20, 21, 22}};

/* Alternativni unos elemenata matrice

for(i=0; i<3; i++)

for(j=0; j<3; j++)

{

printf("a[%d][%d] = ", i, j);

scanf("%d", &a[i][j]);

}

*/

/*Pristup elementima matrice */

a[1][1] = a[0][0] + a[2][2];

/* a[1][1] = 0 + 22 = 22 */

printf("%d\n", a[1][1]); /* 22 */

/* Stampanje elemenata matrice*/

for(i=0; i<3; i++)

{

for(j=0; j<3; j++)

printf("%d\t", a[i][j]);

printf("\n");

105

Page 107: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

}

}

Ukoliko je potrebna veca fleksibilnost, tj. da se dimenzije matrice mogu uneti kaoparametri programa, tada je neophodno koristiti dinamicku alokaciju memorije.

Primer 9.6 Implementacija matrice preko niza.

#include <stdlib.h>

#include <stdio.h>

/* Makro pristupa clanu na poziciji i, j matrice koja ima

m vrsta i n kolona */

#define a(i,j) a[(i)*n+(j)]

main()

{

/* Dimenzije matrice */

int m, n;

/* Matrica */

int *a;

int i,j;

/* Suma elemenata matrice */

int s=0;

/* Unos i alokacija */

printf("Unesi broj vrsta matrice : ");

scanf("%d",&m);

printf("Unesi broj kolona matrice : ");

scanf("%d",&n);

/*Rezervise se prostor za niz a*/

a=(int*)malloc(m*n*sizeof(int));

if (a == NULL) {

printf("Greska prilikom alokacije memorije!\n");

exit(1);

}

for (i=0; i<m; i++)

for (j=0; j<n; j++)

{

printf("Unesi element na poziciji (%d,%d) : ",i,j);

106

Page 108: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

scanf("%d",&a(i,j));

}

/* Racunamo sumu elemenata matrice */

for (i=0; i<m; i++)

for (j=0; j<n; j++)

s+=a(i,j);

/* Ispis unete matrice */

printf("Uneli ste matricu : \n");

for (i=0; i<m; i++)

{ for (j=0; j<n; j++)

printf("%d ",a(i,j));

printf("\n");

}

printf("Suma elemenata matrice je %d\n", s);

/* Oslobadjamo memoriju */

free(a);

}

Primer 9.7 Druga opcija za implementaciju matrice je koristci pokazivac na pokazivac.

1. Definisemo pokazivac na pokazivac na int:

int **p;

Ovaj pokazivac ce cuvati adresu prvog u nizu dinamicki alociranih pokazivacana int-ove.

2. Dinamicki alociramo niz od m pokazivaca na int-ove, i adresu prvog u nizusmestamo u promenljivu p:

p = (int**) malloc(sizeof(int*) * m));

Ovi pokazivaci sluze da pokazuju na prve elemente nizova int-ova koji pred-stavljaju dinamicki alocirane vrste.

3. Svaku vrstu alociramo posebnim malloc-om, i povratnu adresu smestamo u i-tipokazivac u malopre alociranom nizu pokazivaca.

for(i = 0; i < m; i++)

p[i] = malloc(sizeof(int) * n);

Primetimo da nizovi int-ova koji se alociraju nisu morali biti jednakih dimen-zija (ne moraju svi biti duzine n, kao sto je ovde receno). Ovo moze da budekorisno, ako znamo da ce u nekoj vrsti biti korisceno samo nekoliko prvih ele-menata, tada mozemo alocirati manje za tu vrstu, i tako ustedeti prostor.

107

Page 109: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

4. U toku rada se elementima ovako alocirane matrice pristupa sa p[i][j] – p[i]

je i-ti pokazivac u nizu, a kako on pokazuje na pocetni element u i-toj vrsti,tada je p[i][j] upravo j-ti element u i-toj vrsti.

5. Dealokacija se sastoji u brisanju svih vrsta, nakon cega se obrise i niz pokazivaca(dakle, dealokacija ide u suprotnom redosledu od redosleda alokacije):

for(i = 0; i < m; i++)

free(p[i]);

free(p);

#include <stdio.h>

#include <stdlib.h>

int main()

{

int **p;

int m, n;

int i, j;

printf("Uneti broj vrsta matrice: ");

scanf("%d", &m);

printf("Unesite broj kolona matrice: ");

scanf("%d", &n);

/* Alociramo m pokazivaca na vrste matrice */

if((p = (int**)malloc(sizeof(int*) * m)) == NULL)

{

fprintf(stderr, "malloc() greska\n");

exit(1);

}

/* Alociramo m vrsta od po n int-ova */

for(i = 0; i < m; i++)

if((p[i] = (int*)malloc(sizeof(int) * n)) == NULL)

{

fprintf(stderr, "malloc() greska\n");

exit(1);

}

/* Postavljamo p[i][j] na vrednost abs(i - j) */

for(i = 0; i < m ; i++)

for(j = 0; j < n; j++)

p[i][j] = i - j >=0 ? i - j : j - i;

108

Page 110: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

/* Ispis matrice */

for(i = 0; i < m ; i++)

{

for(j = 0; j < n; j++)

printf("%3d ", p[i][j]);

putchar(’\n’);

}

/* Oslobadjanje vrsta matrice */

for(i = 0; i < m; i++)

free(p[i]);

/* Oslobadjanje niza pokazivaca */

free(p);

return 0;

}

Primer 9.8 Program ilustruje rad sa kvadratnim matricama i relacijama. Kod zaalokaciju i dealokaciju memorije je izdvojen u funkcije. Element i je u relaciji saelementom j ako je m[i][j] = 1, a nisu u relaciji ako je m[i][j] = 0.

#include <stdlib.h>

#include <stdio.h>

/* Dinamicka matrica je odredjena adresom

pocetka niza pokazivaca i dimenzijama tj.

int** a;

int m,n;

*/

/* Alokacija kvadratne matrice nxn */

int** alociraj(int n)

{

int** m;

int i;

m = (int**)malloc(n*sizeof(int*));

if (m == NULL) {

printf("Greska prilikom alokacije memorije!\n");

exit(1);

}

for (i=0; i<n; i++)

{

m[i]=(int*)malloc(n*sizeof(int));

if (m[i] == NULL)

109

Page 111: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

{

int k;

printf("Greska prilikom alokacije memorije!\n");

for(k=0;k<i;k++)

free(m[k]);

free(m);

exit(1);

}

}

return m;

}

/* Dealokacija matrice dimenzije nxn */

void obrisi(int** m, int n)

{

int i;

for (i=0; i<n; i++)

free(m[i]);

free(m);

}

/* Ispis matrice /

void ispisi_matricu(int** m, int n)

{

int i, j;

for (i=0; i<n; i++)

{

for (j=0; j<n; j++)

printf("%d ",m[i][j]);

printf("\n");

}

}

/* Provera da li je relacija predstavljena matricom refleksivna */

int refleksivna(int** m, int n)

{

int i;

for (i=0; i<n; i++)

if (m[i][i]==0)

return 0;

return 1;

}

110

Page 112: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

/* Provera da li je relacija predstavljena matricom simetricna */

int simetricna(int** m, int n)

{

int i,j;

for (i=0; i<n; i++)

for (j=i+1; j<n; j++)

if (m[i][j]!=m[j][i])

return 0;

return 1;

}

/* Provera da li je relacija predstavljena matricom tranzitivna*/

int tranzitivna(int** m, int n)

{

int i,j,k;

for (i=0; i<n; i++)

for (j=0; j<n; j++)

for (k=0; k<n; k++)

if ((m[i][j]==1)

&& (m[j][k]==1)

&& (m[i][k]!=1))

return 0;

return 1;

}

/* Pronalazi najmanju simetricnu relaciju koja sadrzi relaciju a */

void simetricno_zatvorenje(int** a, int n)

{

int i,j;

for (i=0; i<n; i++)

for (j=0; j<n; j++)

{

if (a[i][j]==1 && a[j][i]==0)

a[j][i]=1;

if (a[i][j]==0 && a[j][i]==1)

a[i][j]=1;

}

}

main()

{

int **m;

111

Page 113: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

int n;

int i,j;

printf("Unesi dimenziju matrice : ");

scanf("%d",&n);

m=alociraj(n);

for (i=0; i<n; i++)

for (j=0; j<n; j++)

scanf("%d",&m[i][j]);

printf("Uneli ste matricu : \n");

ispisi_matricu(m,n);

if (refleksivna(m,n))

printf("Relacija je refleksivna\n");

if (simetricna(m,n))

printf("Relacija je simetricna\n");

if (tranzitivna(m,n))

printf("Relacija je tranzitivna\n");

simetricno_zatvorenje(m,n);

ispisi_matricu(m,n);

obrisi(m,n);

}

Primer 9.9 Izracunati vrednost determinante matrice preko Laplasovog razvoja.

#include <stdio.h>

#include <stdlib.h>

/* Funkcija alocira matricu dimenzije nxn */

int** allocate(int n)

{

int **m;

int i;

m=(int**)malloc(n*sizeof(int*));

if (m == NULL) {

printf("Greska prilikom alokacije memorije!\n");

exit(1);

}

for (i=0; i<n; i++)

112

Page 114: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.1 Matrice

{

m[i]=malloc(n*sizeof(int));

if (m[i] == NULL)

{

int k;

for(k=0;k<i;k++)

free(m[k]);

printf("Greska prilikom alokacije memorije!\n");

free(m);

exit(1);

}

}

return m;

}

/* Funkcija vrsi dealociranje date matrice dimenzije n */

void deallocate(int** m, int n)

{

int i;

for (i=0; i<n; i++)

free(m[i]);

free(m);

}

/* Funkcija ucitava datu alociranu matricu sa standardnog ulaza */

void ucitaj_matricu(int** matrica, int n)

{

int i,j;

for (i=0; i<n; i++)

for (j=0; j<n; j++)

scanf("%d",&matrica[i][j]);

}

/* Rekurzivna funkcija koja vrsi Laplasov razvoj */

int determinanta(int** matrica, int n)

{

int i;

int** podmatrica;

int det=0,znak;

/* Izlaz iz rekurzije je matrica 1x1 */

if (n==1) return matrica[0][0];

113

Page 115: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.2 Realokacija memorije

/* Podmatrica ce da sadrzi minore polazne matrice */

podmatrica=allocate(n-1);

znak=1;

for (i=0; i<n; i++)

{

int vrsta,kolona;

for (kolona=0; kolona<i; kolona++)

for(vrsta=1; vrsta<n; vrsta++)

podmatrica[vrsta-1][kolona] = matrica[vrsta][kolona];

for (kolona=i+1; kolona<n; kolona++)

for(vrsta=1; vrsta<n; vrsta++)

podmatrica[vrsta-1][kolona-1] = matrica[vrsta][kolona];

det+= znak*matrica[0][i]*determinanta(podmatrica,n-1);

znak*=-1;

}

deallocate(podmatrica,n-1);

return det;

}

main()

{

int **matrica;

int n;

scanf("%d", &n);

matrica = allocate(n);

ucitaj_matricu(matrica, n);

printf("Determinanta je : %d\n",determinanta(matrica,n));

deallocate(matrica, n);

}

9.2 Realokacija memorije

Primer 9.10 Dinamicki niz.

/* Program za svaku rec unetu sa standardnog

ulaza ispisuje broj pojavljivanja.

Verzija sa dinamickim nizom i realokacijom.

*/

#include <ctype.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

114

Page 116: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.2 Realokacija memorije

/* Rec je opisana imenom i brojem pojavljivanja */

typedef struct _rec

{ char ime[80];

int br_pojavljivanja;

} rec;

/* Realokacija se vrsi sa datim korakom */

#define KORAK 10

/* Funkcija ucitava rec i vraca njenu duzinu ili

-1 ukoliko smo dosli do znaka EOF*/

int getword(char word[],int max)

{

int c, i=0;

while (isspace(c=getchar()))

;

while(!isspace(c) && c!=EOF && i<max-1)

{

word[i++]=c;

c = getchar();

}

word[i]=’\0’;

if (c==EOF) return -1;

else return i;

}

main()

{

/* Dinamicki niz reci je opisan pokazivacem na

pocetak, tekucim brojem upisanih elemenata i

tekucim brojem alociranih elemenata */

rec* niz_reci;

int duzina=0;

int alocirano=0;

char procitana_rec[80];

int i;

115

Page 117: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.2 Realokacija memorije

while (getword(procitana_rec,80)!=-1)

{

/* Proveravamo da li rec vec postoji u nizu */

for (i=0; i<duzina; i++)

/* Ako bismo uporedili procitana_rec == niz_reci[i].ime

bili bi uporedjeni pokazivaci a ne odgovarajuci sadrzaji.

Zato koristimo strcmp. */

if (strcmp(procitana_rec, niz_reci[i].ime)==0)

{

niz_reci[i].br_pojavljivanja++;

break;

}

/* Ukoliko rec ne postoji u nizu */

if (i==duzina)

{

rec nova_rec;

/* Ako bismo dodelili nova_rec.ime = procitana_rec

izvrsila bi se dodela pokazivaca a ne kopiranje niske

procitana_rec u nova_rec.ime. Zato koristimo strcpy. */

strcpy(nova_rec.ime,procitana_rec);

nova_rec.br_pojavljivanja=1;

/* Ukoliko je niz "kompletno popunjen" vrsimo realokaciju */

if (duzina==alocirano)

{

alocirano+=KORAK;

/* Sledeca linija zamenjuje blok koji sledi i moze se

koristiti alternativno. Blok je ostavljen samo da bi

demonstrirao korisnu tehniku */

/* niz_reci=realloc(niz_reci, (alocirano)*sizeof(rec)); */

{

/* alociramo novi niz, veci nego sto je bio prethodni */

rec* novi_niz=(rec *)malloc(alocirano*sizeof(rec));

if (novi_niz == NULL)

{

free(niz_reci);

printf("Greska prilikom alokacije memorije");

exit(1);

}

/* Kopiramo elemente starog niza u novi */

116

Page 118: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 9.2 Realokacija memorije

for (i=0; i<duzina; i++)

novi_niz[i]=niz_reci[i];

/* Uklanjamo stari niz */

free(niz_reci);

/* Stari niz postaje novi */

niz_reci=novi_niz;

}

}

/* Upisujemo rec u niz */

niz_reci[duzina]=nova_rec;

duzina++;

}

}

/* Ispisujemo elemente niza */

for(i=0; i<duzina; i++)

printf("%s - %d\n",niz_reci[i].ime, niz_reci[i].br_pojavljivanja);

free(niz_reci);

}

117

Page 119: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 10

Liste

Jednostruko povezana lista1 je struktura podataka koja se sastoji od sekvence cvorova.Svaki cvor sadrzi podatak (odredenog tipa) i pokazivac na sledeci cvor u sekvenci.Prvi cvor u sekvenci naziva se glava liste. Ostatak liste (bez glave) je takode lista,i naziva se rep liste. Lista koja ne sadrzi cvorove naziva se prazna lista. Prilikombaratanja listom mi cuvamo pokazivac na glavu liste. Kada pristupimo glavi liste, unjoj imamo zapisanu adresu sledeceg elementa, pa mu samim tim mozemo pristupiti.Kada mu pristupimo, u njemu je sadrzana adresa sledeceg elementa, pa preko togpokazivaca mozemo da mu pristupimo, itd. Poslednji element u listi nema sledecielement: u tom slucaju se njegov pokazivac na sledeci postavlja na NULL. Takode,prazna lista se predstavlja NULL pokazivacem.

Prednost koriscenja povezanih lista u odnosu na dinamicki niz je u tome sto seelementi mogu efikasno umetati i brisati sa bilo koje pozicije u nizu, bez potrebeza realokacijom ili premestanjem elemenata. Nedostatak ovakvog pristupa je to stone mozemo nasumicno pristupiti proizvoljnom elementu, vec se elementi morajuobradivati redom (iteracijom kroz listu).

10.1 Implementacija

Prilikom promene liste (dodavanje novog elementa, brisanje elementa, premestanjeelemenata, itd.) postoji mogucnost da glava liste bude promenjena, tj. da to postaneneki drugi cvor (sa drugom adresom). U tom slucaju se pokazivac na glavu liste moraazurirati. Kada promenu liste obavljamo u posebnoj funkciji (kao sto je bio slucaj uprethodnom primeru, gde smo za dodavanje i brisanje imali posebne funkcije) ondaje potrebno da se pozivajucoj funkciji vrati informacija o promeni adrese glave,kako bi pozivajuca funkcija mogla da azurira svoju pokazivacku promenljivu. Ovose moze uraditi na dva nacina:

1. Pozvana funkcija koja vrsi promenu na listi vraca kao povratnu vrednost adresuglave nakon promene. Ova adresa moze biti ista kao i pre promene (ako glavanije dirana) a moze se i razlikovati. U svakom slucaju, ta adresa treba da sedodeli pokazivackoj promenljivoj koja cuva adresu glave u pozivajucoj funkciji.

1Tekst preuzet sa sajta www.matf.bg.ac.rs/ milan

118

Page 120: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

Zbog toga se funkcija za promenu liste uvek poziva na sledeci nacin:pok = funkcija_za_promenu(pok, ...);

tj. promenljivoj cija se vrednost predaje kao adresa glave u pozivu mora se do-deliti povratna vrednost funkcije, za slucaj da je adresa glave interno promen-jena.

2. Pozvana funkcija koja vrsi promenu na listi prihvata kao argument pokazivacna pokazivacku promenljivu koja u pozivajucoj funkciji cuva adresu glave ikoju eventalno treba azurirati. Sada pozvana funkcija moze interno da prekodobijenog pokazivaca promeni promenljivu pozivajuce funkcije direktno. Naprimer:funkcija_za_promenu(&pok, ...);

Funkcija koja se poziva je po pravilu tipa void. Prednost drugog metoda jejednostavnije pozivanje, dok je prednost prvog metoda jednostavnija sintaksaunutar pozvane funkcije.

U prethodnom primeru je demonstriran prvi pristup. Naredni primer ilustruje drugipristup.

Primer 10.1 Osnovne funkcije za rad sa listama.

#include <stdio.h>

#include <stdlib.h>

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

int vrednost; /* podatak koji cvor sadrzi */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

/* Pomocna funkcija koja kreira cvor. Funkcija vrednost

novog cvora inicijalizuje na broj, dok pokazivac na

sledeci cvor u novom cvoru postavlja na NULL. Funkcija

ispisuje poruku o gresci i prekida program u slucaju

da malloc() funkcija ne uspe da alocira prostor.

Funkcija vraca pokazivac na novokreirani cvor */

Cvor* napravi_cvor(int broj)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

novi->vrednost = broj;

novi->sledeci = NULL;

119

Page 121: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

return novi;

}

/* Funkcija dodaje novi cvor na pocetak liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor().

Funkcija vraca pokazivac na novu glavu liste */

Cvor* dodaj_na_pocetak_liste(Cvor *glava, int broj)

{

Cvor * novi = napravi_cvor(broj);

novi->sledeci = glava;

return novi;

}

/*void dodaj_na_pocetak_liste(Cvor **glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

novi->sledeci = *glava;

*glava = novi;

}

*/

/* Funkcija dodaje novi cvor na kraj liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor().

Funkcija vraca pokazivac na glavu liste (koji moze

biti promenjen u slucaju da je lista inicijalno bila

prazna). */

Cvor* dodaj_na_kraj_liste(Cvor *glava, int broj)

{

Cvor * novi = napravi_cvor(broj);

Cvor *tekuci = glava;

/* slucaj prazne liste. U tom slucaju je glava nove liste

upravo novi cvor. */

if(glava == NULL) return novi;

/* Ako lista nije prazna, tada se krecemo duz liste sve dok

ne dodjemo do poslednjeg cvora (tj. do cvora ciji pokazivac

na sledeci pokazuje na NULL) */

while(tekuci->sledeci != NULL)

tekuci = tekuci->sledeci;

/* Dodajemo novi element na kraj preusmeravanjem pokazivaca */

tekuci->sledeci = novi;

120

Page 122: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

/* Vracamo pokazivac na glavu liste */

return glava;

}

/*void dodaj_na_kraj_liste(Cvor **glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

Cvor *tekuci = *glava;

/* slucaj prazne liste. U tom slucaju je glava nove liste

upravo novi cvor. */

if(*glava == NULL)

{

*glava = novi;

return;

}

/* Ako lista nije prazna, tada se krecemo duz liste sve dok

ne dodjemo do poslednjeg cvora (tj. do cvora ciji pokazivac

na sledeci pokazuje na NULL) */

while(tekuci->sledeci != NULL)

tekuci = tekuci->sledeci;

/* Dodajemo novi element na kraj preusmeravanjem pokazivaca */

tekuci->sledeci = novi;

}

*/

/* Funkcija trazi u listi element cija je vrednost jednaka

datom broju. Funkcija vraca pokazivac na cvor liste u

kome je sadrzan trazeni broj ili NULL u slucaju da takav

element ne postoji u listi */

Cvor* pretrazi_listu(Cvor *glava, int broj)

{

for(; glava != NULL ; glava = glava->sledeci)

if(glava->vrednost == broj)

return glava;

return NULL;

}

/* Funkcija prikazuje elemente liste pocev

od glave ka kraju liste */

void prikazi_listu(Cvor *glava)

121

Page 123: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

{

putchar(’[’);

for(;glava != NULL; glava = glava->sledeci)

printf("%d ", glava->vrednost);

putchar(’]’);

putchar(’\n’);

}

/* Funkcija oslobadja dinamicku memoriju zauzetu

od strane liste. Funkcija vraca NULL, tj.

vrednost koju treba dodeliti pokazivackoj

promenljivoj, s obzirom da je sada lista prazna. */

Cvor* oslobodi_listu(Cvor *glava)

{

Cvor *pomocni;

while(glava != NULL)

{

/* moramo najpre zapamtiti adresu sledeceg

elementa, a tek onda osloboditi glavu */

pomocni = glava->sledeci;

free(glava);

glava = pomocni;

}

return NULL;

}

/*void oslobodi_listu(Cvor **glava)

{

Cvor *pomocni;

while(*glava != NULL)

{

/* moramo najpre zapamtiti adresu sledeceg

elementa, a tek onda osloboditi glavu */

pomocni = (*glava)->sledeci;

free(*glava);

*glava = pomocni;

}

}

*/

/* Funkcija dodaje novi element u sortiranu listu tako da i nova

122

Page 124: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

lista ostane sortirana. Funkcija kreira novi cvor koriscenjem

funkcije napravi_cvor(). Funkcija vraca pokazivac na glavu liste

(koji moze biti promenjen u slucaju da je novi element dodat na

pocetak liste) */

Cvor* dodaj_sortirano(Cvor *glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

Cvor *tekuci = glava;

/* u slucaju prazne liste glava nove liste je

upravo novi element */

if(glava == NULL) return novi;

/* ako je novi element manji ili jednak od glave,

tada novi element mora da bude nova glava */

if(glava->vrednost >= novi->vrednost)

{

novi->sledeci = glava;

return novi;

}

/* u slucaju da je glava manja od novog elementa, tada se krecemo kroz

listu sve dok se ne dodje do elementa ciji je sledeci element veci ili

jednak od novog elementa, ili dok se ne dodje do poslednjeg elementa. */

while(tekuci->sledeci != NULL && tekuci->sledeci->vrednost < novi->vrednost)

tekuci = tekuci->sledeci;

/* U svakom slucaju novi element dodajemo IZA tekuceg elementa */

novi->sledeci = tekuci->sledeci;

tekuci->sledeci = novi;

/* vracamo pokazivac na glavu liste*/

return glava;

}

/* void dodaj_sortirano(Cvor **glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

Cvor *tekuci = *glava;

/* u slucaju prazne liste glava nove liste je

upravo novi element */

if(*glava == NULL)

{

123

Page 125: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

*glava = novi;

return;

}

/* ako je novi element manji ili jednak od glave,

tada novi element mora da bude nova glava */

if((*glava)->vrednost >= novi->vrednost)

{

novi->sledeci = *glava;

*glava = novi;

return;

}

/* u slucaju da je glava manja od novog elementa, tada se krecemo kroz

listu sve dok se ne dodje do elementa ciji je sledeci element veci ili

jednak od novog elementa, ili dok se ne dodje do poslednjeg elementa. */

while(tekuci->sledeci != NULL && tekuci->sledeci->vrednost < novi->vrednost)

tekuci = tekuci->sledeci;

/* U svakom slucaju novi element dodajemo IZA tekuceg elementa */

novi->sledeci = tekuci->sledeci;

tekuci->sledeci = novi;

}

*/

/* Funkcija brise iz liste sve cvorove koji sadrze dati broj.

Funkcija vraca pokazivac na glavu liste (koji moze biti

promenjen u slucaju da se obrise stara glava) */

Cvor* obrisi_element(Cvor *glava, int broj)

{

Cvor *tekuci;

Cvor *pomocni;

/* Brisemo sa pocetka liste sve eventualne cvorove

koji su jednaki datom broju, i azuriramo pokazivac

na glavu */

while(glava != NULL && glava->vrednost == broj)

{

pomocni = glava->sledeci;

free(glava);

glava = pomocni;

}

/* Ako je nakon toga lista ostala prazna vracamo

124

Page 126: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

NULL */

if(glava == NULL) return NULL;

/* Od ovog trenutka se u svakom koraku nalazimo

na tekucem cvoru koji je razlicit od trazenog

broja (kao i svi levo od njega). Poredimo

vrednost sledeceg cvora (ako postoji) sa trazenim

brojem i brisemo ga ako je jednak, a prelazimo na

sledeci cvor ako je razlicit. Ovaj postupak ponavljamo

dok ne dodjemo do poslednjeg cvora. */

tekuci = glava;

while(tekuci->sledeci != NULL)

if(tekuci->sledeci->vrednost == broj)

{

pomocni = tekuci->sledeci;

tekuci->sledeci = tekuci->sledeci->sledeci;

free(pomocni);

}

else tekuci = tekuci->sledeci;

/* vracamo novu glavu */

return glava;

}

/* void obrisi_element(Cvor **glava, int broj)

{

Cvor *tekuci;

Cvor *pomocni;

/* Brisemo sa pocetka liste sve eventualne cvorove

koji su jednaki datom broju, i azuriramo pokazivac

na glavu */

while(*glava != NULL && (*glava)->vrednost == broj)

{

pomocni = (*glava)->sledeci;

free(*glava);

*glava = pomocni;

}

/* Ako je nakon toga lista ostala prazna prekidamo

funkciju */

if(*glava == NULL) return;

/* Od ovog trenutka se u svakom koraku nalazimo

125

Page 127: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

na tekucem cvoru koji je razlicit od trazenog

broja (kao i svi levo od njega). Poredimo

vrednost sledeceg cvora (ako postoji) sa trazenim

brojem i brisemo ga ako je jednak, a prelazimo na

sledeci cvor ako je razlicit. Ovaj postupak ponavljamo

dok ne dodjemo do poslednjeg cvora. */

tekuci = *glava;

while(tekuci->sledeci != NULL)

if(tekuci->sledeci->vrednost == broj)

{

pomocni = tekuci->sledeci;

tekuci->sledeci = tekuci->sledeci->sledeci;

free(pomocni);

}

else tekuci = tekuci->sledeci;

return;

}

*/

/* test program */

int main()

{

Cvor *glava = NULL;

Cvor *pomocni = NULL;

int broj;

/* Testiranje dodavanja na pocetak */

printf("-------------------------------------------------------\n");

printf("---------- Testiranje dodavanja na pocetak ------------\n");

do {

printf("-------------------------------------------------------\n");

printf("Prikaz trenutnog sadrzaja liste:\n");

prikazi_listu(glava);

printf("-------------------------------------------------------\n");

printf("Dodati element na pocetak liste (ctrl-D za kraj unosa):\n");

} while(scanf("%d", &broj) > 0 &&

(glava = dodaj_na_pocetak_liste(glava, broj)) );

/* dodaj_na_pocetak_liste(&glava, broj);*/

printf("-------------------------------------------------------\n");

putchar(’\n’);

/* Testiranje pretrage elementa */

126

Page 128: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

printf("-------------------------------------------------------\n");

printf("---------------- Testiranje pretrage ------------------\n");

printf("-------------------------------------------------------\n");

printf("Prikaz trenutnog sadrzaja liste:\n");

prikazi_listu(glava);

printf("-------------------------------------------------------\n");

printf("Uneti broj koji se trazi: ");

scanf("%d",&broj);

if((pomocni = pretrazi_listu(glava, broj)) == NULL)

printf("Trazeni broj nije u listi\n");

else

printf("Trazeni element %d je u listi\n", pomocni->vrednost);

printf("-------------------------------------------------------\n");

putchar(’\n’);

glava = oslobodi_listu(glava);

/*oslobodi_listu(&glava);*/

/* Testiranje dodavanja na kraj */

printf("-------------------------------------------------------\n");

printf("------------ Testiranje dodavanja na kraj -------------\n");

do{

printf("-------------------------------------------------------\n");

printf("Prikaz liste:\n");

prikazi_listu(glava);

printf("-------------------------------------------------------\n");

printf("Dodati element na kraj liste (ctrl-D za kraj unosa):\n");

} while(scanf("%d",&broj) > 0 &&

(glava = dodaj_na_kraj_liste(glava, broj)));

/*(dodaj_na_kraj_liste(&glava,broj)));*/

printf("-------------------------------------------------------\n");

putchar(’\n’);

/* Testiranje brisanja elemenata */

printf("-------------------------------------------------------\n");

printf("--------------- Testiranje brisanja -------------------\n");

printf("-------------------------------------------------------\n");

printf("Prikaz trenutnog sadrzaja liste:\n");

prikazi_listu(glava);

printf("-------------------------------------------------------\n");

printf("Uneti broj koji se brise: ");

scanf("%d", &broj);

127

Page 129: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

glava = obrisi_element(glava, broj);

/* obrisi_element(&glava, broj); */

printf("-------------------------------------------------------\n");

printf("Lista nakon izbacivanja:\n");

prikazi_listu(glava);

printf("-------------------------------------------------------\n");

putchar(’\n’);

glava = oslobodi_listu(glava);

/*oslobodi_listu(&glava);*/

/* Testiranje sortiranog dodavanja */

printf("-------------------------------------------------------\n");

printf("----------- Testiranje sortiranog dodavanja -----------\n");

do{

printf("-------------------------------------------------------\n");

printf("Prikaz liste:\n");

prikazi_listu(glava);

printf("-------------------------------------------------------\n");

printf("Dodati element sortirano (ctrl-D za kraj unosa):\n");

} while(scanf("%d",&broj) > 0 &&

(glava = dodaj_sortirano(glava, broj)));

/*(dodaj_sortirano(&glava, broj)));*/

printf("-------------------------------------------------------\n");

putchar(’\n’);

glava = oslobodi_listu(glava);

/*oslobodi_listu(&glava);*/

return 0;

}

Primer 10.2 Sve funkcije iz prethodnih primera, za koje to ima smisla, definisanesu rekurzivno. Primetno je da su ove rekurzivne varijante mnogo jednostavnije.Razlog je to sto su liste po svojoj prirodi rekurzivne strukture. Naime, liste se mogurekurzivno definisati na sledeci nacin:

• objekat oznacen sa [] je lista (prazna lista)

• ako je L lista, i G neki element, tada je uredeni par (G,L) takode lista. ElementG nazivamo glavom liste, a listu L repom liste.

Na primer, uredjeni par (2, []) je lista, i po dogovoru cemo je zapisivati kao [2].Takodje par (3,[2]) je lista koju mozemo zapisati kao [3 2], itd.

U opstem slucaju, ako je L=[a1 a2 a3 ... an] lista, i ako je b neki element,tada je uredjeni par (b,L) takode lista, koju mozemo zapisati kao [b a1 a2 ... an].

128

Page 130: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

Glava liste je b, a rep liste je lista L. Sada se obrada liste moze razdvojiti na dvadela:

• Izvrsiti operaciju nad glavom liste

• Izvrsiti rekurzivni postupak nad repom liste (koji je takode lista).

Za testiranje narednog programa moze se koristiti main funkcija iz primera 10.1.

#include <stdio.h>

#include <stdlib.h>

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

int vrednost; /* podatak koji cvor sadrzi */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

/* Pomocna funkcija koja kreira cvor. Funkcija vrednost

novog cvora inicijalizuje na broj, dok pokazivac na

sledeci cvor u novom cvoru postavlja na NULL. Funkcija

ispisuje poruku o gresci i prekida program u slucaju

da malloc() funkcija ne uspe da alocira prostor.

Funkcija vraca pokazivac na novokreirani cvor */

Cvor* napravi_cvor(int broj)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

novi->vrednost = broj;

novi->sledeci = NULL;

return novi;

}

/* Funkcija dodaje novi cvor na pocetak liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor().

Funkcija vraca pokazivac na novu glavu liste */

Cvor* dodaj_na_pocetak_liste(Cvor *glava, int broj)

{

Cvor * novi = napravi_cvor(broj);

novi->sledeci = glava;

return novi;

129

Page 131: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

}

/*

void dodaj_na_pocetak_liste(Cvor **glava, int broj)

{

Cvor * novi = napravi_cvor(broj);

novi->sledeci = *glava;

*glava = novi;

}

*/

/* Funkcija dodaje novi cvor na kraj liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor().

Funkcija vraca pokazivac na glavu liste (koji moze

biti promenjen u slucaju da je lista inicijalno bila

prazna). Funkcija koristi rekurziju. */

Cvor* dodaj_na_kraj_liste(Cvor *glava, int broj)

{

/* Izlaz iz rekurzije: slucaj prazne liste.

U tom slucaju je glava nove liste upravo novi cvor. */

if(glava == NULL) return napravi_cvor(broj);

/* U slucaju da je lista neprazna, tada ona ima glavu i rep,

pri cemu je rep opet lista. Tada je dodavanje na kraj

liste ekvivalentno dodavanju na kraj repa liste, sto

radimo rekurzivnim pozivom. Povratna vrednost rekurzivnog

poziva je adresa glave modifikovanog repa liste koja se

treba sacuvati u pokazivacu na sledeci u glavi */

glava->sledeci = dodaj_na_kraj_liste(glava->sledeci, broj);

/* Vracamo glavu liste, koja je mozda izmenjena */

return glava;

}

/*

void dodaj_na_kraj_liste(Cvor **glava, int broj)

{

/* Izlaz iz rekurzije: slucaj prazne liste.

U tom slucaju je glava nove liste upravo novi cvor. */

if(*glava == NULL)

*glava = napravi_cvor(broj);

else

/* U slucaju da je lista neprazna, tada ona ima glavu i rep,

pri cemu je rep opet lista. Tada je dodavanje na kraj

liste ekvivalentno dodavanju na kraj repa liste, sto

130

Page 132: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

radimo rekurzivnim pozivom. */

dodaj_na_kraj_liste(&(*glava->sledeci), broj);

}

*/

/* Funkcija dodaje novi element u sortiranu listu tako da i nova

lista ostane sortirana. Funkcija kreira novi cvor koriscenjem

funkcije napravi_cvor(). Funkcija vraca pokazivac na glavu liste

(koji moze biti promenjen u slucaju da je novi element dodat na

pocetak liste). Funkcija koristi rekurziju. */

Cvor* dodaj_sortirano(Cvor *glava, int broj)

{

/* u slucaju da je lista prazna, ili da je vrednost

glave veca ili jednaka od vrednosti broja koji

umecemo, tada se novi cvor umece na pocetak liste. */

if(glava == NULL || glava->vrednost >= broj)

{

Cvor *novi = napravi_cvor(broj);

novi->sledeci = glava;

return novi;

}

/* U slucaju da je lista neprazna, i da je vrednost

glave manja od vrednosti broja koji umecemo u listu,

tada je sigurno da se novi element umece DESNO od glave,

tj. u rep liste. Dakle, mozemo rekurzivno pozvati istu

funkciju za rep liste. S obzirom da po induktivnoj

pretpostavci rep nakon toga ostaje sortiran, a svi

elementi u repu ukljucujuci i element koji je dodat

su jednaki ili veci od glave, tada ce i cela lista

biti sortirana. Povratna vrednost rekurzivnog poziva

predstavlja adresu glave modifikovanog repa liste,

pa se ta adresa mora sacuvati u pokazivacu na sledeci

u glavi liste. */

glava->sledeci = dodaj_sortirano(glava->sledeci, broj);

/* vracamo staru - novu glavu */

return glava;

}

/*

void dodaj_sortirano(Cvor **glava, int broj)

{

131

Page 133: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

/* u slucaju da je lista prazna, ili da je vrednost

glave veca ili jednaka od vrednosti broja koji

umecemo, tada se novi cvor umece na pocetak liste. */

if(*glava == NULL || *glava->vrednost >= broj)

{

Cvor *novi = napravi_cvor(broj);

novi->sledeci = *glava;

*glava = novi;

}

else

/* U slucaju da je lista neprazna, i da je vrednost

glave manja od vrednosti broja koji umecemo u listu,

tada je sigurno da se novi element umece DESNO od glave,

tj. u rep liste. Dakle, mozemo rekurzivno pozvati istu

funkciju za rep liste. S obzirom da po induktivnoj

pretpostavci rep nakon toga ostaje sortiran, a svi

elementi u repu ukljucujuci i element koji je dodat

su jednaki ili veci od glave, tada ce i cela lista

biti sortirana.*/

dodaj_sortirano(&(*glava->sledeci), broj);

}

*/

/* Funkcija trazi u listi element cija je vrednost jednaka

datom broju. Funkcija vraca pokazivac na cvor liste u

kome je sadrzan trazeni broj ili NULL u slucaju da takav

element ne postoji u listi. Funkcija koristi rekurziju. */

Cvor* pretrazi_listu(Cvor *glava, int broj)

{

/* Izlaz iz rekurzije: prazna lista */

if(glava == NULL) return NULL;

/* ako je glava jednaka datom broju, vracamo adresu glave */

if(glava->vrednost == broj)

return glava;

/* u suprotnom pretrazujemo rep rekurzivnim pozivom i vracamo

ono sto taj rekurzivni poziv vrati */

else

return pretrazi_listu(glava->sledeci, broj);

}

/* Funkcija brise iz liste sve cvorove koji sadrze dati broj.

Funkcija vraca pokazivac na glavu liste (koji moze biti

132

Page 134: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

promenjen u slucaju da se obrise stara glava). Funkcija

koristi rekurziju. */

Cvor* obrisi_element(Cvor *glava, int broj)

{

/* Izlaz iz rekurzije: prazna lista ostaje prazna nakon

brisanja elemenata koji su jednaki sa brojem */

if(glava == NULL) return NULL;

/* Rekurzivno iz repa uklanjamo sve pojave datog broja.

Rekurzivni poziv vraca adresu glave tako modifikovanog

repa, koja se cuva u pokazivacu na sledeci u okviru glave */

glava->sledeci = obrisi_element(glava->sledeci, broj);

/* Nakon ovoga znamo da u repu nema pojava datog broja. Jedino

ostaje da proverimo jos i glavu, i da je po potrebi obrisemo */

if(glava->vrednost == broj)

{

Cvor *pomocni = glava;

glava = glava->sledeci;

free(pomocni);

}

/* vracamo novu glavu */

return glava;

}

/*

void obrisi_element(Cvor **glava, int broj)

{

/* Izlaz iz rekurzije: prazna lista ostaje prazna nakon

brisanja elemenata koji su jednaki sa brojem */

if(*glava == NULL) return;

/* Rekurzivno iz repa uklanjamo sve pojave datog broja.

Rekurzivni poziv vraca adresu glave tako modifikovanog

repa, koja se cuva u pokazivacu na sledeci u okviru glave */

obrisi_element(&(*glava->sledeci), broj);

/* Nakon ovoga znamo da u repu nema pojava datog broja. Jedino

ostaje da proverimo jos i glavu, i da je po potrebi obrisemo */

133

Page 135: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

if(*glava->vrednost == broj)

{

Cvor *pomocni = *glava;

*glava = *glava->sledeci;

free(pomocni);

}

}

*/

/* Pomocna rekurzivna funkcija koja prikazuje

listu. Definisana je kao static, cime se

sprecava njeno koriscenje od strane funkcija

koje nisu definisane u ovom fajlu */

static void prikazi_listu_r(Cvor *glava)

{

/* Izlaz iz rekurzije */

if(glava == NULL) return;

/* Prikaz glave */

printf("%d ", glava->vrednost);

/* Prikaz repa (rekurzivnim pozivom) */

prikazi_listu_r(glava->sledeci);

}

/* Funkcija prikazuje elemente liste pocev od glave

ka kraju liste. Koristi rekurzivnu funkciju

prikazi_listu_r(). Ova funkcija prosto dodaje

uglaste zagrade i novi red na kraju ispisa. */

void prikazi_listu(Cvor *glava)

{

putchar(’[’);

prikazi_listu_r(glava);

putchar(’]’);

putchar(’\n’);

}

/* Funkcija oslobadja dinamicku memoriju zauzetu

od strane liste. Funkcija koristi rekurziju. */

134

Page 136: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

Cvor* oslobodi_listu(Cvor *glava)

{

/* Izlaz iz rekurzije */

if(glava == NULL) return NULL;

/* Oslobadjamo rep liste */

oslobodi_listu(glava->sledeci);

/* Oslobadjamo glavu liste */

free(glava);

return NULL;

}

/*

void oslobodi_listu(Cvor **glava)

{

/* Izlaz iz rekurzije */

if(*glava == NULL) return;

/* Oslobadjamo rep liste */

oslobodi_listu(&(*glava->sledeci));

/* Oslobadjamo glavu liste */

free(*glava);

*glava = NULL;

}

*/

Primer 10.3 Program ispisuje broj pojavljivanja za svaku od reci koja se pojavila utekstu unetom sa standardnog ulaza. Verzija sa (sortiranom) listom.

#include <stdlib.h>

#include <stdio.h>

/* Definicija cvora liste */

typedef struct _cvor

{

char ime[80];

int br_pojavljivanja;

struct _cvor* sledeci;

} cvor;

/* Pomocna funkcija koja kreira cvor.

Funkcija vraca pokazivac na novokreirani cvor */

135

Page 137: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

cvor* napravi_cvor(char* rec)

{

cvor *novi = NULL;

if((novi = (cvor *) malloc(sizeof(cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

strcpy(novi->ime, rec);

novi->br_pojavljivanja = 1;

novi->sledeci = NULL;

return novi;

}

/* Funkcija ispisuje listu rekurzivno, pocevsi od poslednjeg

elementa */

void ispisi_listu(cvor* pocetak)

{

if (pocetak!=NULL)

{

ispisi_listu(pocetak->sledeci);

printf("%s %d\n",pocetak->ime,pocetak->br_pojavljivanja);

}

}

/* Funkcija koja brise listu */

void obrisi_listu(cvor* pocetak)

{

if (pocetak!=NULL)

{

obrisi_listu(pocetak->sledeci);

free(pocetak);

}

}

/* Funkcija ubacuje rekurzivno datu rec u listu koja je

leksikografski sortirana, na odgovarajuce mesto i vraca

pokazivac na novi pocetak liste */

cvor* ubaci_sortirano(cvor* pocetak, char* rec)

{

int cmp;

/* Ukoliko je lista prazna ubacujemo na pocetak liste*/

if (pocetak==NULL)

136

Page 138: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

return napravi_cvor(rec);

/* Ukoliko lista nije prazna poredimo rec sa elementom u glavi */

cmp=strcmp(pocetak->ime,rec);

/* Ukoliko je rec pronadjena samo uvecavamo njen broj

pojavljivanja */

if (cmp==0)

{ pocetak->br_pojavljivanja++;

return pocetak;

}

/* Ukoliko je rec koju ubacujemo veca od tekuce reci, ubacujemo je

rekurzivno u rep */

else if (cmp>0)

{ pocetak->sledeci=ubaci_sortirano(pocetak->sledeci,rec);

return pocetak;

}

/* Ukoliko je rec koju ubacujemo manja od tekuce reci, gradimo novi

cvor i ubacujemo ga ispred pocetka */

else

{

cvor* novi=napravi_cvor(rec);

novi->sledeci=pocetak;

return novi;

}

}

/* Pomocna funkcija koja cita rec sa standardnog ulaza i vraca

njenu duzinu, odnosno -1 ukoliko se naidje na EOF */

int getword(char word[], int lim) {

int c, i=0;

while (!isalpha(c=getchar()) && c!=EOF)

;

if (c==EOF)

return -1;

do

{ word[i++]=c;

}while (i<lim-1 && isalpha(c=getchar()));

word[i]=’\0’;

return i;

}

/* Funkcija koja rekurzivno pronalazi datu rec u datoj listi.

137

Page 139: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

Funkcija vraca pokazivac na cvor u kome je nadjena rec, ili

NULL ukoliko rec nije nadjena. */

cvor* nadji_rec(cvor* lista, char rec[])

{

if (lista==NULL)

return NULL;

if (strcmp(lista->ime,rec)==0)

return lista;

/* Da bi pretrazivanje u sortiranoj listi

bilo efikasnije:

if (strcmp(lista->ime,rec)<0)

return NULL;

*/

return nadji_rec(lista->sledeci,rec);

}

main()

{

cvor* lista=NULL;

char procitana_rec[80];

cvor* pronadjen;

while(getword(procitana_rec,80)!=-1)

lista=ubaci_sortirano(lista,procitana_rec);

pronadjen=nadji_rec(lista,"programiranje");

if (pronadjen!=NULL)

printf("Rec programiranje se javlja u listi!\n");

else

printf("Rec programiranje se ne javlja u listi!\n");

ispisi_listu(lista);

obrisi_listu(lista);

}

Primer 10.4 Primer ilustruje sortiranje liste. Lista moze biti sortirana na dvanacina. Prvi nacin je da se premestaju cvorovi u listi (sto zahteva prevezivanjepokazivaca), a drugi je da se premestaju vrednosti koje cvorovi sadrze. Prvi nacinje komplikovaniji, ali je cesto efikasniji, narocito ako su objekti koji se cuvaju kaopodaci u cvorovima veliki.

U oba slucaja se moze koristiti rekurzija, a moze se realizovati i iterativno. Uovom primeru ilustrujemo oba nacina sortiranja lista (prvi rekurzivno, a drugi iter-ativno).

#include <stdio.h>

#include <stdlib.h>

138

Page 140: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

#include <time.h>

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

int vrednost; /* podatak koji cvor sadrzi */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

/* Pomocna funkcija koja kreira cvor. Funkcija vrednost

novog cvora inicijalizuje na broj, dok pokazivac na

sledeci cvor u novom cvoru postavlja na NULL. Funkcija

ispisuje poruku o gresci i prekida program u slucaju

da malloc() funkcija ne uspe da alocira prostor.

Funkcija vraca pokazivac na novokreirani cvor */

Cvor* napravi_cvor(int broj)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

novi->vrednost = broj;

novi->sledeci = NULL;

return novi;

}

/* Funkcija dodaje novi cvor na pocetak liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor().

Funkcija vraca pokazivac na novu glavu liste */

Cvor * dodaj_na_pocetak_liste(Cvor *glava, int broj)

{

Cvor * novi = napravi_cvor(broj);

novi->sledeci = glava;

return novi;

}

/* Pomocna rekurzivna funkcija koja prikazuje

listu. Definisana je kao static, cime se

sprecava njeno koriscenje od strane funkcija

koje nisu definisane u ovom fajlu */

static void prikazi_listu_r(Cvor *glava)

139

Page 141: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

{

/* Izlaz iz rekurzije */

if(glava == NULL) return;

/* Prikaz glave */

printf("%d ", glava->vrednost);

/* Prikaz repa (rekurzivnim pozivom) */

prikazi_listu_r(glava->sledeci);

}

/* Funkcija prikazuje elemente liste pocev od glave

ka kraju liste. Koristi rekurzivnu funkciju

prikazi_listu_r(). Ova funkcija prosto dodaje

uglaste zagrade i novi red na kraju ispisa. */

void prikazi_listu(Cvor *glava)

{

putchar(’[’);

prikazi_listu_r(glava);

putchar(’]’);

putchar(’\n’);

}

/* Funkcija oslobadja dinamicku memoriju zauzetu

od strane liste. Funkcija koristi rekurziju. */

Cvor* oslobodi_listu(Cvor *glava)

{

/* Izlaz iz rekurzije */

if(glava == NULL) return NULL;

/* Oslobadjamo rep liste */

oslobodi_listu(glava->sledeci);

/* Oslobadjamo glavu liste */

free(glava);

return NULL;

}

Cvor* dodaj_cvor_sortirano(Cvor * glava, Cvor * novi)

140

Page 142: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

{

/* u slucaju da je lista prazna, ili da je vrednost

glave veca ili jednaka od vrednosti broja koji

umecemo, tada se novi cvor umece na pocetak liste. */

if(glava == NULL || glava->vrednost >= novi->vrednost)

{

novi->sledeci = glava;

return novi;

}

/* U slucaju da je lista neprazna, i da je vrednost

glave manja od vrednosti broja koji umecemo u listu,

tada je sigurno da se novi element umece DESNO od glave,

tj. u rep liste. Dakle, mozemo rekurzivno pozvati istu

funkciju za rep liste. S obzirom da po induktivnoj

pretpostavci rep nakon toga ostaje sortiran, a svi

elementi u repu ukljucujuci i element koji je dodat

su jednaki ili veci od glave, tada ce i cela lista

biti sortirana. Povratna vrednost rekurzivnog poziva

predstavlja adresu glave modifikovanog repa liste,

pa se ta adresa mora sacuvati u pokazivacu na sledeci

u glavi liste. */

glava->sledeci = dodaj_cvor_sortirano(glava->sledeci, novi);

/* vracamo glavu */

return glava;

}

/* Sortira listu i vraca adresu glave liste. Lista se sortira

rekurzivno, prevezivanjem cvorova. */

Cvor* sortiraj_listu(Cvor * glava)

{

Cvor * pomocni;

/* Izlaz iz rekurzije */

if(glava == NULL)

return NULL;

/* Odvajamo cvor glave iz liste, a za glavu proglasavamo

sledeci cvor */

pomocni = glava;

glava = glava->sledeci;

pomocni->sledeci = NULL;

141

Page 143: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.1 Implementacija

/* Sortiramo ostatak liste */

glava = sortiraj_listu(glava);

glava = dodaj_cvor_sortirano(glava, pomocni);

/* Vracamo novu glavu */

return glava;

}

/* Funkcija sortira listu i vraca adresu glave liste. Lista se

sortira iterativno, zamenom vrednosti u cvorovima. */

Cvor* sortiraj_listu2(Cvor * glava)

{

int t;

Cvor * pi, * pj, * min;

/* Slucaj prazne liste */

if(glava == NULL)

return glava;

/* Simuliramo selection sort */

for(pi = glava; pi->sledeci != NULL ; pi = pi->sledeci)

{

min = pi;

for(pj = pi->sledeci; pj != NULL; pj = pj->sledeci)

{

if(pj->vrednost < min->vrednost)

min = pj;

}

t = min->vrednost;

min->vrednost = pi->vrednost;

pi->vrednost = t;

}

/* Vracamo glavu */

return glava;

}

/* test program */

int main()

{

Cvor *glava = NULL;

int n = 10;

/* Inicijalizujemo sekvencu slucajnih brojeva. */

142

Page 144: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.2 Kruzna (ciklicna) lista

srand(time(NULL));

/* Generisemo n slucajnih brojeva i dodajemo ih na pocetak liste */

while(n--)

glava = dodaj_na_pocetak_liste(glava, (int) (100 * (rand()/(double)RAND_MAX)));

/* Prikazujemo generisanu listu */

printf("Generisana lista:\n");

prikazi_listu(glava);

/* Sortiramo listu */

glava = sortiraj_listu(glava);

/* glava = sortiraj_listu2(glava); */

/* Prikazujemo listu nakon sortiranja */

printf("Lista nakon sortiranja:\n");

prikazi_listu(glava);

/* Oslobadjamo listu */

glava = oslobodi_listu(glava);

return 0;

}

10.2 Kruzna (ciklicna) lista

A

pocetak ciklicne liste

B C ... Z

Slika 10.1: Kruzna lista

Primer 10.5 (februar 2006.) Grupa od n plesaca (na cijim kostimima su u smerukazaljke na satu redom brojevi od 1 do n) izvodi svoju plesnu tacku tako sto formirajukrug iz kog najpre izlazi k-ti plesac (odbrojava se pocev od plesaca oznacenog brojem1 u smeru kretanja kazaljke na satu). Preostali plesaci obrazuju manji krug iz kogopet izlazi k-ti plesac (odbrojava se pocev od sledeceg suseda prethodno izbacenog,opet u smeru kazaljke na satu). Izlasci iz kruga se nastavljaju sve dok svi plesaci

143

Page 145: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.3 Red

ne budu iskljuceni. Celi brojevi n, k (k < n) se ucitavaju sa standardnog ulaza.Napisati program koji ce na standardni izlaz ispisati redne brojeve plesaca u redosledunapustanja kruga.

PRIMER: za n = 5, k = 3 redosled izlaska je 3 1 5 2 4.

10.3 Red

A

pocetak reda (brisanje — get)

B ... X NULL

kraj reda (dodavanje — add)

Y

novi element

A

pocetak reda

B ... X Y

novi kraj reda

NULL

B ...

pocetak reda nakon brisanja

X Y

kraj reda

NULL

Slika 10.2: Red

Red (eng. queue) je struktura podataka nad kojom su definisane sledece operacije:

• Dodavanje elementa – kazemo da je element dodat na kraj reda (eng. enqueue()operacija)

• Uklanjanje elementa koji je prvi dodat – kazemo da je element skinut sa pocetkareda (eng. dequeue() operacija)

• Ocitavanje vrednosti elementa koji je na pocetku reda (eng. front() operacija)

Red spada u FIFO strukture (eng. First In First Out). Moze se implementiratina vise nacina. Najjednostavniji nacin je da se definise kao niz. Medjutim, tadaje ogranicen max. broj elemenata u redu dimenzijom niza. Zbog toga se obicnopribegava koriscenju lista za implementaciju reda, gde se enqueue() operacija svodina dodavanje na kraj liste, a dequeue() operacija se svodi na uklanjanje glave liste.

144

Page 146: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.3 Red

Obe operacije se izvode u konstantnom vremenu, pod uslovom da se osim pokazivacana glavu liste cuva i pokazivac na poslednji element u listi.

Primer 10.6 Skupovi brojeva su predstavljeni tekstualnim fajlovima koji sadrze bro-jeve i imena drugih fajlova. Ako se broj x nalazi u fajlu S, tada broj x pripada skupuS. Ako se ime fajla S1 nalazi u fajlu S2, tada je skup S1 podskup skupa S2. Programza dati skup S i dati broj x proverava da li x pripada S.

U ovom zadatku ce red biti koriscen na sledeci nacin: inicijalno se u red stavljaime fajla koji predstavlja dati skup S. Iz reda se zatim u petlji uzima sledeci fajl iotvara za citanje. Prilikom citanja, svaki put kada se u fajlu koji se trenutno citanaidje na ime drugog fajla, to ime ce biti dodato u red. Kada se zavrsi sa citanjemtekuceg fajla, isti se zatvara, skida se sa reda sledeci fajl i citanje se nastavlja unjemu. Ovaj proces se ponavlja dok se ne naidje na trazeni broj, ili dok se neisprazni red.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MAX 100

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

char ime_fajla[MAX]; /* Sadrzi ime fajla */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

/* Funkcija kreira novi cvor, upisuje u njega etiketu

i vraca njegovu adresu */

Cvor * napravi_cvor(char * ime_fajla)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

strcpy(novi->ime_fajla, ime_fajla);

novi->sledeci = NULL;

return novi;

}

/* Funkcija dodaje na kraj reda novi fajl */

145

Page 147: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.3 Red

void dodaj_u_red(Cvor **pocetak, Cvor **kraj, char *ime_fajla)

{

Cvor * novi = napravi_cvor(ime_fajla);

if(*kraj != NULL)

{

(*kraj)->sledeci = novi;

*kraj = novi;

}

else /* ako je red prazan */

{

*pocetak = novi;

*kraj = novi;

}

}

/* Funkcija skida sa pocetka reda fajl. Ako je poslednji argument

pokazivac razlicit od NULL, tada u niz karaktera na koji on

pokazuje upisuje ime fajla koji je upravo skinut sa reda

dok u suprotnom ne radi nista. Funkcija vraca 0 ako je red

prazan (pa samim tim nije bilo moguce skinuti vrednost sa

reda) ili 1 u suprotnom. */

int skini_sa_reda(Cvor **pocetak, Cvor ** kraj, char * ime_fajla)

{

Cvor * pomocni;

if(*pocetak == NULL)

return 0;

if(ime_fajla != NULL)

strcpy(ime_fajla, (*pocetak)->ime_fajla);

pomocni = *pocetak;

*pocetak = (*pocetak)->sledeci;

free(pomocni);

if(*pocetak == NULL)

*kraj = NULL;

return 1;

}

/* Funkcija vraca pokazivac na string koji sadrzi ime fajla

146

Page 148: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.3 Red

na pocetku reda. Ukoliko je red prazan, vraca NULL */

char * pocetak_reda(Cvor *pocetak)

{

if(pocetak == NULL)

return NULL;

else

return pocetak->ime_fajla;

}

/* Funkcija prazni red */

void oslobodi_red(Cvor **pocetak, Cvor **kraj)

{

Cvor *pomocni;

while(*pocetak != NULL)

{

pomocni = *pocetak;

*pocetak = (*pocetak)->sledeci;

free(pomocni);

}

*kraj = NULL;

}

/* Glavni program */

int main(int argc, char ** argv)

{

Cvor * pocetak = NULL, * kraj = NULL;

FILE *in = NULL;

char ime_fajla[MAX];

int x, y;

int pronadjen = 0;

/* NAPOMENA: Pretpostavlja se da nema cirkularnih zavisnosti, tj. da ne moze

da se desi slucaj: S2 podskup od S1, S3 podskup S2,...,S1 podskup od Sn,

u kom slucaju bismo imali beskonacnu petlju. */

/* Ime fajla i broj se zadaju sa komandne linije */

if(argc < 3)

{

fprintf(stderr, "koriscenje: %s fajl broj\n", argv[0]);

exit(1);

}

/* Dodajemo skup S u red */

147

Page 149: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.3 Red

dodaj_u_red(&pocetak, &kraj, argv[1]);

/* Trazeni broj x */

x = atoi(argv[2]);

/* Dokle god broj nije pronadjen, a ima jos fajlova u redu */

while(!pronadjen && skini_sa_reda(&pocetak, &kraj, ime_fajla))

{

/* Otvaramo sledeci fajl sa reda */

if((in = fopen(ime_fajla, "r")) == NULL)

{

fprintf(stderr, "Greska prilikom otvaranja fajla: %s\n", ime_fajla);

exit(1);

}

/* Dokle god nismo stigli do kraja fajla i nismo pronasli broj */

while(!feof(in) && !pronadjen)

{

/* Ako je broj sledeci podatak u fajlu */

if(fscanf(in, "%d", &y) == 1)

{

/* Ako je nadjen trazeni broj */

if(x == y)

{

printf("Broj x=%d pripada skupu!\n", x);

pronadjen = 1;

}

}

/* Ako je ime fajla sledeci podatak u fajlu */

else if(fscanf(in, "%s", ime_fajla) == 1)

{

/* Dodajemo ga u red */

dodaj_u_red(&pocetak, &kraj, ime_fajla);

}

}

/* Zatvaramo fajl */

fclose(in);

}

/* Ako do kraja nije pronadjen */

if(!pronadjen)

printf("Broj x=%d ne pripada skupu!\n", x);

148

Page 150: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.3 Red

/* Oslobadjamo red */

oslobodi_red(&pocetak, &kraj);

return 0;

}

Primer 10.7 Program formira celobrojni red i ispisuje sadrzaj reda na standardniizlaz.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MAX 100

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

int broj; /* broj */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

/* Funkcija kreira novi cvor, upisuje u njega broj

i vraca njegovu adresu */

Cvor * napravi_cvor(int broj)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

novi->broj = broj;

novi->sledeci = NULL;

return novi;

}

/* Funkcija dodaje na kraj reda broj */

void dodaj_u_red(Cvor **pocetak, Cvor **kraj, int broj)

{

Cvor * novi = napravi_cvor(broj);

if(*kraj != NULL)

{

149

Page 151: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.3 Red

(*kraj)->sledeci = novi;

*kraj = novi;

}

else /* ako je red prazan */

{

*pocetak = novi;

*kraj = novi;

}

}

/* Funkcija skida sa pocetka reda broj */

int skini_sa_reda(Cvor **pocetak, Cvor ** kraj, int *broj)

{

Cvor * pomocni;

if(*pocetak == NULL)

return 0;

if(broj != NULL)

*broj = (*pocetak)->broj);

pomocni = *pocetak;

*pocetak = (*pocetak)->sledeci;

free(pomocni);

if(*pocetak == NULL)

*kraj = NULL;

return 1;

}

main()

{

Cvor * pocetak = NULL, * kraj = NULL;

int i, broj;

/*Formiranje jednostavnog reda*/

for(i=0; i<100; i++)

dodaj_u_red(&pocetak, &kraj, i);

/*Ispis elemenata reda koristeci operacije nad redom*/

while(skini_sa_reda(&pocetak, &kraj, &broj))

printf("Element uzet sa pocetka reda je %d\n", broj);

150

Page 152: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

/*Nema potrebe da se oslobadja memorija jer je memorija koju

je red zauzimao oslobodjena prilikom ispisa elemenata

reda - nakon ispisa red je ostao prazan!*/

}

10.4 Stek

Stek (eng. stack) je struktura podataka nad kojom su definisane sledece operacije:

• Dodavanje elementa – kazemo da je element potisnut na vrh steka (eng. push()operacija).

• Uklanjanje elementa koji je poslednji dodat – kazemo da je element skinut savrha steka (eng. pop() operacija).

• Ocitavanje vrednosti elementa koji je poslednji dodat (eng. top() operacija).

Stek spada u LIFO strukture (eng. Last In First Out). Moze se implementiratina vise nacina. Najjednostavniji nacin je da se definise kao niz. Medjutim, tadaje ogranicen max. broj elemenata na steku dimenzijom niza. Zbog toga se obicnopribegava koriscenju lista za implementaciju steka, gde se push() operacija svodina dodavanje na pocetak, a pop() operacija se svodi na uklanjanje glave liste. Obeoperacije se izvode u konstantnom vremenu.

Primer 10.8 Program proverava da li su zagrade (, [, {, }, ] i ) dobro uparene —staticka implementacija steka (pretpostavka da se na ulazu nece naci vise od 100otvorenih zagrada za redom).

#include <stdio.h>

#include <stdlib.h>

#define MAX_ZAGRADA 100

int odgovarajuce(char z1, char z2) {

return (z1 == ’(’ && z2 == ’)’) ||

(z1 == ’{’ && z2 == ’}’) ||

(z1 == ’[’ && z2 == ’]’);

}

main()

{

int c;

char otv_zagrade[MAX_ZAGRADA];

int br_otv = 0;

while((c=getchar()) != EOF)

{

switch(c)

151

Page 153: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

{

case ’(’:

case ’{’:

case ’[’:

{

otv_zagrade[br_otv] = c;

br_otv++;

break;

}

case ’]’:

case ’}’:

case ’)’:

if (br_otv>0 && odgovarajuce(otv_zagrade[br_otv-1], c))

{

br_otv--;

}

else

{

printf("Visak zatvorenih zagrada: %c u liniji %d\n", c, br_linija);

exit(1);

}

}

}

if (br_otv == 0)

printf("Zagrade su u redu\n");

else

printf("Visak otvorenih zagrada\n");

}

Primer 10.9 Program proverava da li su etikete u datom HTML fajlu dobro up-arene.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <ctype.h>

#define MAX 100

#define OTVORENA 1

#define ZATVORENA 2

#define VAN_ETIKETE 0

#define PROCITANO_MANJE 1

152

Page 154: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

X

vrh steka (i dodavanje i brisanje — push i pop)

Y ... A NULL

V

novi element

Y

novi vrh steka

X V ... A NULL

X

vrh steka nakon brisanja

V ... A NULL

Slika 10.3: Stek

#define U_ETIKETI 2

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

char etiketa[MAX]; /* Sadrzi ime etikete */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

/* Funkcija kreira novi cvor, upisuje u njega etiketu

i vraca njegovu adresu */

Cvor * napravi_cvor(char * etiketa)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

strcpy(novi->etiketa, etiketa);

novi->sledeci = NULL;

153

Page 155: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

return novi;

}

/* Funkcija postavlja na vrh steka novu etiketu */

void potisni_na_stek(Cvor **vrh, char *etiketa)

{

Cvor * novi = napravi_cvor(etiketa);

novi->sledeci = *vrh;

*vrh = novi;

}

/* Funkcija skida sa vrha steka etiketu. Ako je drugi argument

pokazivac razlicit od NULL, tada u niz karaktera na koji on

pokazuje upisuje ime etikete koja je upravo skinuta sa steka

dok u suprotnom ne radi nista. Funkcija vraca 0 ako je stek

prazan (pa samim tim nije bilo moguce skinuti vrednost sa

steka) ili 1 u suprotnom. */

int skini_sa_steka(Cvor **vrh, char * etiketa)

{

Cvor * pomocni;

if(*vrh == NULL)

return 0;

if(etiketa != NULL)

strcpy(etiketa, (*vrh)->etiketa);

pomocni = *vrh;

*vrh = (*vrh)->sledeci;

free(pomocni);

return 1;

}

/* Funkcija vraca pokazivac na string koji sadrzi etiketu

na vrhu steka. Ukoliko je stek prazan, vraca NULL */

char * vrh_steka(Cvor *vrh)

{

if(vrh == NULL)

return NULL;

else

return vrh->etiketa;

}

154

Page 156: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

/* Funkcija prazni stek */

void oslobodi_stek(Cvor **vrh)

{

Cvor *pomocni;

while(*vrh != NULL)

{

pomocni = *vrh;

*vrh = (*vrh)->sledeci;

free(pomocni);

}

}

/* Funkcija iz fajla na koji pokazuje f cita sledecu

etiketu, i njeno ime upisuje u niz na koji pokazuje

pokazivac etiketa. Funkcija vraca EOF u slucaju da

se dodje do kraja fajla pre nego sto se procita

etiketa, vraca OTVORENA ako je procitana otvorena

etiketa, odnosno ZATVORENA ako je procitana zatvorena

etiketa. */

int uzmi_etiketu(FILE *f, char * etiketa)

{

int c;

int stanje = VAN_ETIKETE;

int i = 0;

int tip;

while((c = fgetc(f)) != EOF)

{

switch(stanje)

{

case VAN_ETIKETE:

if(c == ’<’)

{

stanje = PROCITANO_MANJE;

}

break;

case PROCITANO_MANJE:

if(c == ’/’)

{

tip = ZATVORENA;

}

else if(isalpha(c))

155

Page 157: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

{

tip = OTVORENA;

etiketa[i++] = tolower(c);

}

stanje = U_ETIKETI;

break;

case U_ETIKETI:

if(isalpha(c) && i < MAX - 1)

etiketa[i++] = tolower(c);

else {

stanje = VAN_ETIKETE;

etiketa[i]=’\0’;

return tip;

}

break;

}

}

return EOF;

}

/* Test program */

int main(int argc, char **argv)

{

Cvor * vrh = NULL;

char etiketa[MAX];

int tip;

int u_redu = 1;

FILE * f;

/* Ime datoteke zadajemo na komandnoj liniji */

if(argc < 2)

{

fprintf(stderr, "Koriscenje: %s ime_html_datoteke\n", argv[0]);

exit(0);

}

/* Otvaramo datoteku */

if((f = fopen(argv[1], "r")) == NULL)

{

fprintf(stderr, "fopen() greska!\n");

exit(1);

}

156

Page 158: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

/* Dokle god ima etiketa, uzimamo ih jednu po jednu sa ulaza */

while((tip = uzmi_etiketu(f, etiketa)) != EOF)

{

/* Ako je otvorena etiketa, dodajemo je na stek. Izuzetak su

etikete <br>, <hr> i <meta> koje nemaju sadrzaj, tako da

ih nije potrebno zatvoriti. NAPOMENA: U html-u postoje

jos neke etikete koje koje nemaju sadzaj (npr link).

Pretpostavimo da njih nema u dokumentu, zbog jednostavnosti */

if(tip == OTVORENA)

{

if(strcmp(etiketa, "br") != 0

&& strcmp(etiketa, "hr") != 0

&& strcmp(etiketa, "meta") != 0)

potisni_na_stek(&vrh, etiketa);

}

/* Ako je zatvorena etiketa, tada je uslov dobre uparenosti da je u pitanju

zatvaranje etikete koja je poslednja otvorena, a jos uvek nije zatvorena.

Ova etiketa se mora nalaziti na vrhu steka. Ako je taj uslov ispunjen,

tada je skidamo sa steka, jer je zatvorena. U suprotnom, obavestavamo

korisnika da etikete nisu pravilno uparene. */

else if(tip == ZATVORENA)

{

if(vrh_steka(vrh) != NULL && strcmp(vrh_steka(vrh), etiketa) == 0)

skini_sa_steka(&vrh, NULL);

else

{

printf(vrh_steka(vrh) != NULL ?

"Etikete nisu pravilno uparene\n(nadjena etiketa

</%s> a poslednja otvorena etiketa je <%s>)\n" :

"Etikete nisu pravilno uparene\n(nadjena etiketa

</%s> koja nije otvorena)\n",

etiketa, vrh_steka(vrh));

u_redu = 0;

break;

}

}

}

/* Zatvaramo fajl */

fclose(f);

/* Ako nismo pronasli pogresno uparivanje...*/

if(u_redu)

{

157

Page 159: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

/* Nakon zavrsetka citanja etiketa uslov je da stek mora biti prazan.

Ako nije, tada znaci da postoje jos neke etikete koje su otvorene

ali nisu zatvorene. */

if(vrh_steka(vrh) == NULL)

printf("Etikete su pravilno uparene!\n");

else

printf("Etikete nisu pravilno uparene\n(etiketa <%s> nije zatvorena)\n",

vrh_steka(vrh));

}

/* Oslobadjamo stek */

oslobodi_stek(&vrh);

return 0;

}

Primer 10.10 Program formira celobrojni stek i ispisuje sadrzaj steks na standardniizlaz.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MAX 100

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

int broj; /* broj */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

} Cvor;

/* Funkcija kreira novi cvor, upisuje u njega broj

i vraca njegovu adresu */

Cvor * napravi_cvor(int broj)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

novi->broj = broj;

novi->sledeci = NULL;

return novi;

}

158

Page 160: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.4 Stek

/* Funkcija dodaje broj na stek*/

void potisni_na_stek(Cvor **vrh, int broj)

{

Cvor * novi = napravi_cvor(broj);

novi->sledeci = *vrh;

*vrh = novi;

}

/* Funkcija skida broj sa steka */

int skini_sa_steka(Cvor **vrh, int *broj)

{

Cvor * pomocni;

if(*vrh == NULL)

return 0;

if(broj != NULL)

*broj = (*vrh)->broj);

pomocni = *vrh;

*vrh = (*vrh)->sledeci;

free(pomocni);

return 1;

}

main()

{

Cvor * vrh = NULL;

int i, broj;

/*Formiranje steka*/

for(i=0; i<100; i++)

potisni_na_stek(&vrh, i);

/*Ispis elemenata steka: 99, 98 ... 0*/

while(skini_sa_steka(&pocetak, &kraj, &broj))

printf("Broj skinut sa vrha steka je %d\n", broj);

/*Nema potrebe da se oslobadja memorija jer je memorija koju

je stek zauzimao oslobodjena prilikom ispisa elemenata

steka - nakon ispisa stek je ostao prazan!*/

159

Page 161: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.5 Dvostruko povezane liste

}

10.5 Dvostruko povezane liste

NULL A

pocetak dvostruko povezane liste

B C...... Z NULL

kraj liste

Slika 10.4: Dvostruko povezana lista

Dvostruko povezana lista je struktura podataka koja se sastoji od sekvence cvorova.Svaki cvor sadrzi podatak (odredenog tipa) i pokazivace na prethodni i sledeci cvoru sekvenci. Za razliku od obicne liste, u svakom cvoru imamo zapisanu i adresuprethodnog elementa, pa se kroz listu mozemo kretati i unazad. Poslednji element ulisti nema sledeci element: u tom slucaju se njegov pokazivac na sledeci postavlja naNULL. Slicno, glava liste nema prethodni element, pa je njen pokazivac na prethodnipostavljen na NULL. Takode, prazna lista se predstavlja NULL pokazivacem.

Prednost dvostruko povezane liste u odnosu na jednostruko povezanu je u tomesto se mozemo kretati u oba smera kroz listu. Nedostatak je to sto se velicinamemorije potrebne za cuvanje cvora povecava za velicinu jednog pokazivaca.

Primer 10.11 Program demonstrira rad sa dvostruko povezanim listama. Za testi-ranje narednih funkcija moze se koristiti main funkcija iz primera 10.1.

#include <stdio.h>

#include <stdlib.h>

/* Struktura koja predstavlja cvor liste */

typedef struct cvor {

int vrednost; /* podatak koji cvor sadrzi */

struct cvor *sledeci; /* pokazivac na sledeci cvor */

struct cvor *prethodni; /* pokazivac na prethodni */

} Cvor;

/* Pomocna funkcija koja kreira cvor. Funkcija vrednost

novog cvora inicijalizuje na broj, dok pokazivace na

prethodni i sledeci cvor u novom cvoru postavlja na NULL.

Funkcija ispisuje poruku o gresci i prekida program u

slucaju da malloc() funkcija ne uspe da alocira prostor.

160

Page 162: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.5 Dvostruko povezane liste

Funkcija vraca pokazivac na novokreirani cvor */

Cvor* napravi_cvor(int broj)

{

Cvor *novi = NULL;

if((novi = (Cvor *) malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska!\n");

exit(1);

}

novi->vrednost = broj;

novi->prethodni = NULL;

novi->sledeci = NULL;

return novi;

}

/* Funkcija dodaje novi cvor na pocetak liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor().

Funkcija vraca pokazivac na novu glavu liste */

Cvor* dodaj_na_pocetak_liste(Cvor *glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

novi->sledeci = glava;

if(glava != NULL) glava->prethodni = novi;

return novi;

}

/* Funkcija dodaje novi cvor na kraj liste. Funkcija

kreira novi cvor koriscenjem funkcije napravi_cvor().

Funkcija vraca pokazivac na glavu liste (koji moze

biti promenjen u slucaju da je lista inicijalno bila

prazna). */

Cvor* dodaj_na_kraj_liste(Cvor *glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

Cvor *tekuci = glava;

/* slucaj prazne liste. U tom slucaju je glava nove liste

upravo novi cvor. */

if(glava == NULL) return novi;

/* Ako lista nije prazna, tada se krecemo duz liste sve dok

ne dodjemo do poslednjeg cvora (tj. do cvora ciji pokazivac

161

Page 163: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.5 Dvostruko povezane liste

na sledeci pokazuje na NULL) */

while(tekuci->sledeci != NULL)

tekuci = tekuci->sledeci;

/* Dodajemo novi element na kraj preusmeravanjem pokazivaca */

tekuci->sledeci = novi;

novi->prethodni = tekuci;

/* Vracamo glavu liste */

return glava;

}

/* Funkcija dodaje novi element u sortiranu listu tako da i nova

lista ostane sortirana. Funkcija kreira novi cvor koriscenjem

funkcije napravi_cvor(). Funkcija vraca pokazivac na glavu liste

(koji moze biti promenjen u slucaju da je novi element dodat na

pocetak liste) */

Cvor* dodaj_sortirano(Cvor *glava, int broj)

{

Cvor *novi = napravi_cvor(broj);

Cvor *tekuci = glava;

/* u slucaju prazne liste glava nove liste je

upravo novi element */

if(glava == NULL) return novi;

/* ako je novi element manji ili jednak od glave,

tada novi element mora da bude nova glava */

if(glava->vrednost >= novi->vrednost)

{

novi->sledeci = glava;

glava->prethodni = novi;

return novi;

}

/* u slucaju da je glava manja od novog elementa, tada se krecemo kroz

listu sve dok se ne dodje do elementa ciji je sledeci element veci ili

jednak od novog elementa, ili dok se ne dodje do poslednjeg elementa. */

while(tekuci->sledeci != NULL && tekuci->sledeci->vrednost < novi->vrednost)

tekuci = tekuci->sledeci;

/* U svakom slucaju novi element dodajemo IZA tekuceg elementa */

novi->sledeci = tekuci->sledeci;

novi->prethodni = tekuci;

162

Page 164: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.5 Dvostruko povezane liste

if(tekuci->sledeci != NULL)

tekuci->sledeci->prethodni = novi;

tekuci->sledeci = novi;

/* vracamo vrednost glave */

return glava;

}

/* Funkcija trazi u listi element cija je vrednost jednaka

datom broju. Funkcija vraca pokazivac na cvor liste u

kome je sadrzan trazeni broj ili NULL u slucaju da takav

element ne postoji u listi */

Cvor* pretrazi_listu(Cvor *glava, int broj)

{

/* Pretrazivanje se vrsi bez pretpostavke o sortiranosti

liste.*/

for(; glava != NULL ; glava = glava->sledeci)

if(glava->vrednost == broj)

return glava;

return NULL;

}

/* Funkcija brise tekuci element liste, tj. element liste

na koji pokazuje pokazivac tekuci. Funkcija vraca pokazivac

na glavu liste, koja moze biti promenjena ako je upravo

glava obrisani cvor */

Cvor* obrisi_tekuci(Cvor * glava, Cvor * tekuci)

{

if(tekuci == NULL)

return glava;

/* Preusmeravamo pokazivace prethodnog i sledeceg

ako oni postoje */

if(tekuci->prethodni != NULL)

tekuci->prethodni->sledeci = tekuci->sledeci;

if(tekuci->sledeci != NULL)

tekuci->sledeci->prethodni = tekuci->prethodni;

/* Ako je cvor koji brisemo glava, tada moramo da

promenimo glavu (sledeci element postaje glava) */

if(tekuci == glava)

glava = tekuci->sledeci;

163

Page 165: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.5 Dvostruko povezane liste

/* Brisemo tekuci cvor */

free(tekuci);

return glava;

}

/* Funkcija brise iz liste sve cvorove koji sadrze dati broj.

Funkcija vraca pokazivac na glavu liste (koji moze biti

promenjen u slucaju da se obrise stara glava) */

Cvor* obrisi_element(Cvor *glava, int broj)

{

Cvor *tekuci = glava;

Cvor *pomocni;

/* Pretrazujemo listu pocev od tekuceg elementa trazeci

datu vrednost. Ako je pronadjemo, brisemo nadjeni cvor

i nastavljamo pretragu od elementa koji sledi nakon

upravo obrisanog */

while((tekuci = pretrazi_listu(tekuci, broj)) != NULL)

{

pomocni = tekuci->sledeci;

glava = obrisi_tekuci(glava, tekuci);

tekuci = pomocni;

}

/* vracamo novu glavu */

return glava;

}

/* Funkcija prikazuje elemente liste pocev

od glave ka kraju liste */

void prikazi_listu(Cvor *glava)

{

putchar(’[’);

for(;glava != NULL; glava = glava->sledeci)

printf("%d ", glava->vrednost);

putchar(’]’);

putchar(’\n’);

}

/* Funkcija prikazuje elemente liste u obrnutom poretku */

void prikazi_listu_obrnuto(Cvor * glava)

{

/* Ako je lista prazna... */

if(glava == NULL)

164

Page 166: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 10.5 Dvostruko povezane liste

{

printf("[]\n");

return;

}

/* Prolazimo kroz listu dok ne stignemo do poslednjeg

elementa. */

while(glava->sledeci != NULL)

glava = glava->sledeci;

/* Ispisujemo elemente liste iteracijom u suprotnom smeru */

putchar(’[’);

for(;glava != NULL; glava = glava->prethodni)

printf("%d ", glava->vrednost);

putchar(’]’);

putchar(’\n’);

}

/* Funkcija oslobadja dinamicku memoriju zauzetu

od strane liste. Funkcija vraca NULL, tj.

vrednost koju treba dodeliti pokazivackoj

promenljivoj, s obzirom da je sada lista prazna. */

Cvor* oslobodi_listu(Cvor *glava)

{

Cvor *pomocni;

while(glava != NULL)

{

/* moramo najpre zapamtiti adresu sledeceg

elementa, a tek onda osloboditi glavu */

pomocni = glava->sledeci;

free(glava);

glava = pomocni;

}

return NULL;

}

165

Page 167: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 11

Stabla

Binarno stablo1 je skup cvorova koji su povezani na sledeci nacin:

1. Svaki cvor moze imati levog i desnog SINA (pri tom svaki od sinova moze bitii izostavljen, ili oba). Kazemo da je dati cvor RODITELJ svojim sinovima. Akoje cvor U roditelj cvoru V, tada pisemo da je U < V . Cvor koji nema ni levogni desnog sina naziva se LIST.

2. Postoji jedan jedinstveni cvor takav da nema roditelja. Ovaj cvor nazivamoKOREN stabla.

3. U stablu nema ciklusa, tj. ne postoji niz cvorova x1,x2,...,xn, takav da jex1 < x2 < ... < xn < x1.

Inace, niz cvorova x1 < x2 < ... < xn nazivamo put u stablu. Kazemo da jecvor U predak cvora V ako u stablu postoji put od U do V. Specijalno, svaki cvorje predak samom sebi. Lako se moze pokazati da je koren predak svih cvorova ustablu. Rastojanje od korena do nekog cvora naziva se visina cvora (koren je visine0). Maksimalna visina cvora u stablu naziva se visina stabla.

Stablo koje nema cvorova naziva se prazno stablo.Za svaki cvor V u stablu mozemo posmatrati stablo koje se sastoji od svih njegovih

potomaka (svih cvorova kojima je on predak). Ovo stablo se naziva podstablo sadatim korenom V. Podstablo sa njegovim levim sinom kao korenom nazivamo levimpodstablom cvora V, dok podstablo sa njegovim desnim sinom kao korenom nazivamodesnim podstablom cvora V. Ako je neki od njegovih sinova izostavljen, tada kazemoda je odgovarajuce podstablo cvora V prazno stablo. Specijalno, ako je V koren, tadanjegovo levo i desno podstablo nazivamo levim i desnim podstablom datog (citavog)stabla.

Stablo se moze definisati i rekurzivno na sledeci nacin:

1. Prazno stablo je stablo

2. Ako su data dva stabla t1 i t2, i cvor r, tada je i (t1,r,t2) takodje stablo.t1 je tada levo podstablo, t2 je desno podstablo dok je cvor r koren takoformiranog stabla.

166

Page 168: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

NULL NULL

NULL NULL NULL

NULL NULL

Slika 11.1: Binarno stablo

U programiranju se stabla obicno koriste kao strukture podataka, tako sto svakicvor sadrzi po jedan podatak odredenog tipa. Nacin rasporedivanja podataka ustablu zavisi od konkretne primene stabla. S obzirom na rekurzivnu prirodu stabla,uobicajeno je da se u programiranju stabla obraduju rekurzivno.

11.1 Binarno pretrazivacko stablo

Posebna vrsta stabla su tzv. binarna pretrazivacka stabla. Ova stabla imaju osobinuda za svaki cvor vazi sledece: svi cvorovi njegovog levog podstabla sadrze podatkesa manjom vrednoscu od vrednosti podatka u tom cvoru, dok svi cvorovi njegovogdesnog podstabla sadrze podatke sa vecom vrednoscu od vrednosti podatka u tomcvoru. Ovakva organizacija omogu ’cava efikasno pretrazivanje.

Primer 11.1 Program demonstrira rad sa binarnim pretrazivackim drvetima ciji supodaci celi brojevi. Drvo sadrzi cele brojeve sortirane po velicini. Za svaki cvor, levopodstabla sadrzi manje elemente, dok desno podstablo sadrzi vece.

#include <stdlib.h>1Tekst preuzet sa sajta http://www.matf.bg.ac.rs/~milan

167

Page 169: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

17, 12, 21, 15, 5, 14

infix : 5, 12, 14, 15, 17, 21

prefix : 17, 12, 5, 15, 14, 21

postfix : 5, 14, 15, 12, 21, 17

17

21

NULL NULL

12

NULL NULL

5

NULL

15

NULL NULL

14

Slika 11.2: Uredeno stablo

#include <stdio.h>

/* Struktura koja predstavlja cvor drveta */

typedef struct _cvor

{

int broj;

struct _cvor *l, *d;

} cvor;

/* Pomocna funkcija za kreiranje cvora. */

cvor* napravi_cvor(int b) {

cvor* novi = (cvor*)malloc(sizeof(cvor));

if (novi == NULL)

{

fprintf(stderr, "Greska prilikom

alokacije memorije");

exit(1);

}

168

Page 170: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

novi->broj = b;

novi->l = NULL;

novi->d = NULL;

return novi;

}

/* Funkcija umece broj b u drvo ciji je koren dat preko

pokazivaca koren. Funkcija vraca pokazivac na koren

novog drveta */

cvor* ubaci_u_drvo(cvor* koren, int b)

{

if (koren == NULL)

return napravi_cvor(b);

if (b < koren->broj)

koren->l = ubaci_u_drvo(koren->l, b);

else

koren->d = ubaci_u_drvo(koren->d, b);

return koren;

}

/* Funkcija proverava da li dati broj postoji u drvetu */

int pronadji(cvor* koren, int b)

{

if (koren == NULL)

return 0;

if (koren->broj == b)

return 1;

if (b < koren->broj)

return pronadji(koren->l, b);

else

return pronadji(koren->d, b);

}

/* Funkcija ispisuje sve cvorove drveta u infiksnom redosledu */

void ispisi_drvo(cvor* koren) {

if (koren != NULL)

{

ispisi_drvo(koren->l);

printf("%d ", koren->broj);

ispisi_drvo(koren->d);

169

Page 171: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

}

}

/* Funkcija oslobadja memoriju koju je drvo zauzimalo */

void obrisi_drvo(cvor* koren) {

if (koren != NULL)

{

/* Oslobadja se memorija za levo poddrvo */

obrisi_drvo(koren->l);

/* Oslobadja se memorija za desno poddrvo */

obrisi_drvo(koren->d);

/* Oslobadja se memorija koju zauzima koren */

free(koren);

}

}

/* Funkcija sumira sve vrednosti binarnog stabla */

int suma_cvorova(cvor* koren)

{

if (koren == NULL)

return 0;

return suma_cvorova(koren->l) +

koren->broj +

suma_cvorova(koren->d);

}

/* Funkcija prebrojava broj cvorova binarnog stabla */

int broj_cvorova(cvor* koren)

{

if (koren == NULL)

return 0;

return broj_cvorova(koren->l) +

1 +

broj_cvorova(koren->d);

}

/* Funkcija prebrojava broj listova binarnog stabla */

int broj_listova(cvor* koren)

{

if (koren == NULL)

return 0;

if (koren->l == NULL && koren->d == NULL)

return 1;

return broj_listova(koren->l) +

170

Page 172: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

broj_listova(koren->d);

}

/* Funkcija izracunava sumu listova binarnog stabla */

int suma_listova(cvor* koren)

{

if (koren == NULL)

return 0;

if (koren->l == NULL && koren->d == NULL)

return koren->broj;

return suma_listova(koren->l) +

suma_listova(koren->d);

}

/* Funkcija ispisuje sadrzaj listova binarnog stabla */

void ispisi_listove(cvor* koren)

{

if (koren == NULL)

return;

ispisi_listove(koren->l);

if (koren->l == NULL && koren->d == NULL)

printf("%d ", koren->broj);

ispisi_listove(koren->d);

}

/* Funkcija pronalazi maksimalnu vrednost u drvetu

Koristi se cinjenica da je ova vrednost

smestena u najdesnjem listu */

int max_vrednost(cvor* koren)

{

if (koren==NULL)

return 0;

if (koren->d==NULL)

return koren->broj;

return max_vrednost(koren->d);

}

171

Page 173: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Iterativna funkcija za pronalazenje maksimalne vrednosti. */

int max_vrednost_nerekurzivno(cvor* koren)

{

if (koren==NULL)

return 0;

else

{

cvor* tekuci;

for (tekuci=koren; tekuci->d!=NULL; tekuci=tekuci->d)

;

return tekuci->broj;

}

}

#define max(a,b) (((a)>(b))?(a):(b))

/* Funkcija racuna "dubinu" binarnog stabla */

int dubina(cvor* koren)

{

if (koren==NULL)

return 0;

else

{ int dl=dubina(koren->l);

int dd=dubina(koren->d);

return 1+max(dl,dd);

}

}

/* Program koji testira rad prethodnih funkcija */

main()

{

cvor* koren = NULL;

koren = ubaci_u_drvo(koren, 1);

koren = ubaci_u_drvo(koren, 8);

koren = ubaci_u_drvo(koren, 5);

koren = ubaci_u_drvo(koren, 3);

koren = ubaci_u_drvo(koren, 7);

koren = ubaci_u_drvo(koren, 6);

koren = ubaci_u_drvo(koren, 9);

if (pronadji(koren, 3))

printf("Pronadjeno 3\n");

if (pronadji(koren, 2))

172

Page 174: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

printf("Pronadjeno 2\n");

if (pronadji(koren, 7))

printf("Pronadjeno 7\n");

ispisi_drvo(koren);

putchar(’\n’);

printf("Suma cvorova : %d\n", suma_cvorova(koren));

printf("Broj cvorova : %d\n", broj_cvorova(koren));

printf("Broj listova : %d\n", broj_listova(koren));

printf("Suma listova : %d\n", suma_listova(koren));

printf("Dubina drveta : %d\n", dubina(koren));

printf("Maximalna vrednost : %d\n", max_vrednost(koren));

ispisi_listove(koren);

obrisi_drvo(koren);

}

/*

Pronadjeno 3

Pronadjeno 7

1 3 5 6 7 8 9

Suma cvorova : 39

Broj cvorova : 7

Broj listova : 3

Suma listova : 18

Dubina drveta : 5

Maximalna vrednost : 9

3 6 9

*/

Primer 11.2 Binarno pretrazivacko drvo - funkcije za izracunavanje kopije, unije,preseka i razlike dva drveta, funkcija za izbacivanje cvora iz drveta.

/* Struktura koja predstavlja cvor drveta */

typedef struct cvor {

int vrednost; /* Vrednost koja se cuva */

struct cvor * levi; /* Pokazivac na levo podstablo */

struct cvor * desni; /* Pokazivac na desno podstablo */

} Cvor;

/* NAPOMENA: Prazno stablo se predstavlja NULL pokazivacem. */

173

Page 175: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Pomocna funkcija za kreiranje cvora. Cvor se kreira

dinamicki, funkcijom malloc(). U slucaju greske program

se prekida i ispisuje se poruka o gresci. U slucaju

uspeha inicijalizuje se vrednost datim brojem, a pokazivaci

na podstabla se inicijalizuju na NULL. Funkcija vraca

adresu novokreiranog cvora */

Cvor * napravi_cvor (int broj)

{

/* dinamicki kreiramo cvor */

Cvor * novi = (Cvor *) malloc (sizeof(Cvor));

/* u slucaju greske ... */

if (novi == NULL)

{

fprintf (stderr,"malloc() greska\n");

exit (1);

}

/* inicijalizacija */

novi->vrednost = broj;

novi->levi = NULL;

novi->desni = NULL;

/* vracamo adresu novog cvora */

return novi;

}

/* Funkcija dodaje novi cvor u stablo sa datim korenom.

Ukoliko broj vec postoji u stablu, ne radi nista.

Cvor se kreira funkcijom napravi_cvor(). Funkcija

vraca koren stabla nakon ubacivanja novog cvora. */

Cvor * dodaj_u_stablo (Cvor *koren, int broj)

{

/* izlaz iz rekurzije: ako je stablo bilo prazno,

novi koren je upravo novi cvor */

if (koren == NULL)

return napravi_cvor (broj);

/* Ako je stablo neprazno, i koren sadrzi manju vrednost

od datog broja, broj se umece u desno podstablo,

rekurzivnim pozivom */

174

Page 176: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

if (koren->vrednost < broj)

koren->desni = dodaj_u_stablo (koren->desni, broj);

/* Ako je stablo neprazno, i koren sadrzi vecu vrednost

od datog broja, broj se umece u levo podstablo,

rekurzivnim pozivom */

else if (koren->vrednost > broj)

koren->levi = dodaj_u_stablo (koren->levi, broj);

/* U slucaju da je koren jednak datom broju, tada

broj vec postoji u stablu, i ne radimo nista */

/* Vracamo koren stabla */

return koren;

}

/* Funkcija pretrazuje binarno stablo. Ukoliko

pronadje cvor sa vrednoscu koja je jednaka

datom broju, vraca adresu tog cvora. U

suprotnom vraca NULL */

Cvor * pretrazi_stablo (Cvor * koren, int broj)

{

/* Izlaz iz rekurzije: ako je stablo prazno,

tada trazeni broj nije u stablu */

if (koren == NULL)

return NULL;

/* Ako je stablo neprazno, tada se pretrazivanje

nastavlja u levom ili desnom podstablu, u

zavisnosti od toga da li je trazeni broj

respektivno manji ili veci od vrednosti

korena. Ukoliko je pak trazeni broj jednak

korenu, tada se vraca adresa korena. */

if (koren->vrednost < broj)

return pretrazi_stablo (koren->desni, broj);

else if (koren->vrednost > broj)

return pretrazi_stablo (koren->levi, broj);

else

return koren;

}

/* Funkcija vraca adresu cvora sa najmanjom vrednoscu

u stablu, ili NULL ako je stablo prazno */

175

Page 177: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

Cvor * pronadji_najmanji (Cvor * koren)

{

/* Slucaj praznog stabla */

if (koren == NULL)

return NULL;

/* Izlaz iz rekurzije: ako koren nema levog sina,

tada je upravo koren po vrednosti najmanji cvor.*/

if (koren->levi == NULL)

return koren;

/* U suprotnom je najmanja vrednost u stablu upravo

najmanja vrednost u levom podstablu, pa se pretraga

nastavlja rekurzivno u levom podstablu. */

else

return pronadji_najmanji (koren->levi);

}

/* Funkcija vraca adresu cvora sa najvecom vrednoscu

u stablu, ili NULL ako je stablo prazno */

Cvor * pronadji_najveci (Cvor * koren)

{

/* Slucaj praznog stabla */

if (koren == NULL)

return NULL;

/* Izlaz iz rekurzije: ako koren nema desnog sina,

tada je upravo koren po vrednosti najveci cvor. */

if (koren->desni == NULL)

return koren;

/* U suprotnom je najveca vrednost u stablu upravo

najveca vrednost u desnom podstablu, pa se pretraga

nastavlja rekurzivno u desnom podstablu. */

else

return pronadji_najveci (koren->desni);

}

/* Funkcija brise cvor sa datom vrednoscu iz stabla,

ukoliko postoji. U suprotnom ne radi nista. Funkcija

vraca koren stabla (koji moze biti promenjen nakon

brisanja) */

Cvor * obrisi_element (Cvor * koren, int broj)

176

Page 178: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

{

Cvor * pomocni=NULL;

/* Izlaz iz rekurzije: ako je stablo prazno, ono ostaje

prazno i nakon brisanja */

if (koren == NULL)

return NULL;

/* Ako je vrednost broja veca od vrednosti korena,

tada se broj eventualno nalazi u desnom podstablu,

pa treba rekurzivno primeniti postupak na desno

podstablo. Koren ovako modifikovanog stabla je

nepromenjen, pa vracamo stari koren */

if (koren->vrednost < broj)

{

koren->desni = obrisi_element (koren->desni, broj);

return koren;

}

/* Ako je vrednost broja manja od vrednosti korena,

tada se broj eventualno nalazi u levom podstablu,

pa treba rekurzivno primeniti postupak na levo

podstablo. Koren ovako modifikovanog stabla je

nepromenjen, pa vracamo stari koren */

if (koren->vrednost > broj)

{

koren->levi = obrisi_element (koren->levi, broj);

return koren;

}

/* Slede podslucajevi vezani za slucaj kada je vrednost

korena jednaka broju koji se brise (tj. slucaj kada

treba obrisati koren) */

/* Ako koren nema sinova, tada se on prosto brise, i

rezultat je prazno stablo (vracamo NULL) */

if (koren->levi == NULL && koren->desni == NULL)

{

free (koren);

return NULL;

}

/* Ako koren ima samo levog sina, tada se brisanje

177

Page 179: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

vrsi tako sto obrisemo koren, a novi koren postaje

levi sin */

if (koren->levi != NULL && koren->desni == NULL)

{

pomocni = koren->levi;

free (koren);

return pomocni;

}

/* Ako koren ima samo desnog sina, tada se brisanje

vrsi tako sto obrisemo koren, a novi koren postaje

desni sin */

if (koren->desni != NULL && koren->levi == NULL)

{

pomocni = koren->desni;

free (koren);

return pomocni;

}

/* Slucaj kada koren ima oba sina. U tom slucaju se

brisanje vrsi na sledeci nacin: najpre se potrazi

sledbenik korena (u smislu poretka) u stablu. To

je upravo po vrednosti najmanji cvor u desnom podstablu.

On se moze pronaci npr. funkcijom pronadji_najmanji().

Nakon toga se u koren smesti vrednost tog cvora, a u taj

cvor se smesti vrednost korena (tj. broj koji se brise).

Onda se prosto rekurzivno pozove funkcija za brisanje

na desno podstablo. S obzirom da u njemu treba obrisati

najmanji element, a on definitivno ima najvise jednog

potomka, jasno je da ce to brisanje biti obavljeno na

jedan od nacina koji je gore opisan. */

pomocni = pronadji_najmanji (koren->desni);

koren->vrednost = pomocni->vrednost;

pomocni->vrednost = broj;

koren->desni = obrisi_element (koren->desni, broj);

return koren;

}

/* Funkcija prikazuje stablo s leva u desno (tj.

prikazuje elemente u rastucem poretku) */

void prikazi_stablo (Cvor * koren)

{

/* izlaz iz rekurzije */

178

Page 180: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

if(koren == NULL)

return;

prikazi_stablo (koren->levi);

printf("%d ", koren->vrednost);

prikazi_stablo (koren->desni);

}

/* Funkcija oslobadja prostor koji je alociran za

cvorove stabla. Funkcija vraca NULL, zato sto je

nakon oslobadjanja stablo prazno. */

Cvor * oslobodi_stablo (Cvor *koren)

{

/* Izlaz iz rekurzije */

if(koren == NULL)

return NULL;

koren->levi = oslobodi_stablo (koren->levi);

koren->desni = oslobodi_stablo (koren->desni);

free(koren);

return NULL;

}

/* Funkcija kreira novo stablo identicno stablu koje je

dato korenom. Funkcija vraca pokazivac na koren

novog stabla. */

Cvor * kopiraj_stablo (Cvor * koren)

{

Cvor * duplikat = NULL;

/* Izlaz iz rekurzije: ako je stablo prazno,

vracamo NULL */

if(koren == NULL)

return NULL;

/* Dupliramo koren stabla i postavljamo ga

da bude koren novog stabla */

duplikat = napravi_cvor (koren->vrednost);

/* Rekurzivno dupliramo levo podstablo i njegovu

adresu cuvamo u pokazivacu na levo podstablo

korena duplikata. */

179

Page 181: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

duplikat->levi = kopiraj_stablo (koren->levi);

/* Rekurzivno dupliramo desno podstablo i njegovu

adresu cuvamo u pokazivacu na desno podstablo

korena duplikata. */

duplikat->desni = kopiraj_stablo (koren->desni);

/* Vracamo adresu korena duplikata */

return duplikat;

}

/* Funkcija modifikuje stablo dato korenom koren_1

tako da sadrzi i sve elemente drugog stabla datog

korenom koren_2 (drugim recima funkcija kreira uniju

dva stabla, i rezultat se smesta u prvo stablo).

Funkcija vraca pokazivac na koren tako modifikovanog

prvog stabla. */

Cvor * kreiraj_uniju (Cvor * koren_1, Cvor * koren_2)

{

/* Ako je drugo stablo neprazno */

if(koren_2 != NULL)

{

/* dodajemo koren drugog stabla u prvo stablo */

koren_1 = dodaj_u_stablo (koren_1, koren_2->vrednost);

/* rekurzivno racunamo uniju levog i desnog podstabla

drugog stabla sa prvim stablom */

koren_1 = kreiraj_uniju (koren_1, koren_2->levi);

koren_1 = kreiraj_uniju (koren_1, koren_2->desni);

}

/* vracamo pokazivac na modifikovano prvo stablo */

return koren_1;

}

/* Funkcija modifikuje stablo dato korenom koren_1

tako da sadrzi samo one elemente koji su i elementi

stabla datog korenom koren_2 (drugim recima funkcija

kreira presek dva stabla, i rezultat se smesta u prvo

stablo). Funkcija vraca pokazivac na koren tako

modifikovanog prvog stabla. */

Cvor * kreiraj_presek (Cvor * koren_1, Cvor * koren_2)

180

Page 182: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

{

/* Ako je prvo stablo prazno, tada je i rezultat prazno

stablo */

if(koren_1 == NULL)

return NULL;

/* Kreiramo presek levog i desnog podstabla sa drugim

stablom, tj. iz levog i desnog podstabla prvog stabla

brisemo sve one elemente koji ne postoje u drugom

stablu */

koren_1->levi = kreiraj_presek (koren_1->levi, koren_2);

koren_1->desni = kreiraj_presek (koren_1->desni, koren_2);

/* Ako se koren prvog stabla ne nalazi u drugom stablu

tada ga uklanjamo iz prvog stabla */

if(pretrazi_stablo (koren_2, koren_1->vrednost) == NULL)

koren_1 = obrisi_element (koren_1, koren_1->vrednost);

/* Vracamo koren tako modifikovanog prvog stabla */

return koren_1;

}

/* Funkcija modifikuje stablo dato korenom koren_1

tako da sadrzi samo one elemente koji nisu i elementi

stabla datog korenom koren_2 (drugim recima funkcija

kreira razliku dva stabla, i rezultat se smesta u prvo

stablo). Funkcija vraca pokazivac na koren tako

modifikovanog prvog stabla. */

Cvor * kreiraj_razliku (Cvor * koren_1, Cvor * koren_2)

{

/* Ako je prvo stablo prazno, tada je i rezultat prazno

stablo */

if(koren_1 == NULL)

return NULL;

/* Kreiramo razliku levog i desnog podstabla sa drugim

stablom, tj. iz levog i desnog podstabla prvog stabla

brisemo sve one elemente koji postoje i u drugom

stablu */

koren_1->levi = kreiraj_razliku (koren_1->levi, koren_2);

koren_1->desni = kreiraj_razliku (koren_1->desni, koren_2);

181

Page 183: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Ako se koren prvog stabla nalazi i u drugom stablu

tada ga uklanjamo iz prvog stabla */

if(pretrazi_stablo (koren_2, koren_1->vrednost) != NULL)

koren_1 = obrisi_element (koren_1, koren_1->vrednost);

/* Vracamo koren tako modifikovanog prvog stabla */

return koren_1;

}

/* test program */

int main()

{

Cvor * koren = NULL, * koren_2 = NULL;

Cvor * pomocni = NULL;

int broj;

/* Testiranje dodavanja u stablo */

printf("-------------------------------------------------------\n");

printf("---------- Testiranje dodavanja u stablo --------------\n");

do {

printf("-------------------------------------------------------\n");

printf("Prikaz trenutnog sadrzaja stabla:\n");

prikazi_stablo (koren);

putchar(’\n’);

printf("-------------------------------------------------------\n");

printf("Dodati element u stablo (ctrl-D za kraj unosa):\n");

} while(scanf("%d", &broj) > 0 &&

(koren = dodaj_u_stablo (koren, broj)) );

printf("-------------------------------------------------------\n");

putchar(’\n’);

/* Testiranje pretrage elementa */

printf("-------------------------------------------------------\n");

printf("---------------- Testiranje pretrage ------------------\n");

printf("-------------------------------------------------------\n");

printf("Prikaz trenutnog sadrzaja stabla:\n");

prikazi_stablo (koren);

putchar(’\n’);

printf("-------------------------------------------------------\n");

182

Page 184: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

printf("Uneti broj koji se trazi: ");

scanf("%d", &broj);

if((pomocni = pretrazi_stablo (koren, broj)) == NULL)

printf("Trazeni broj nije u stablu\n");

else

printf("Trazeni element %d je u stablu\n", pomocni->vrednost);

printf("-------------------------------------------------------\n");

putchar(’\n’);

/* Testiranje brisanja elemenata */

printf("-------------------------------------------------------\n");

printf("--------------- Testiranje brisanja -------------------\n");

printf("-------------------------------------------------------\n");

printf("Prikaz trenutnog sadrzaja stabla:\n");

prikazi_stablo (koren);

putchar(’\n’);

printf("-------------------------------------------------------\n");

printf("Uneti broj koji se brise: ");

scanf("%d", &broj);

koren = obrisi_element (koren, broj);

printf("-------------------------------------------------------\n");

printf("Stablo nakon izbacivanja:\n");

prikazi_stablo (koren);

putchar(’\n’);

printf("-------------------------------------------------------\n");

putchar(’\n’);

/* Testiranje dupliranja, unije, preseka i razlike */

printf("-------------------------------------------------------\n");

printf("----- Testiranje dupliranja i skupovnih operacija -----\n");

do {

printf("-------------------------------------------------------\n");

printf("Prikaz trenutnog sadrzaja 2. stabla:\n");

prikazi_stablo (koren_2);

putchar(’\n’);

printf("-------------------------------------------------------\n");

printf("Dodati element u 2. stablo (ctrl-D za kraj unosa):\n");

} while(scanf("%d", &broj) > 0 &&

(koren_2 = dodaj_u_stablo (koren_2, broj)) );

183

Page 185: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

printf("-------------------------------------------------------\n");

putchar(’\n’);

pomocni = kopiraj_stablo(koren);

pomocni = kreiraj_uniju(pomocni, koren_2);

printf("-------------------------------------------------------\n");

printf("Prikaz 1. stabla:\n");

prikazi_stablo (koren);

putchar(’\n’);

printf("-------------------------------------------------------\n");

printf("Prikaz 2. stabla:\n");

prikazi_stablo (koren_2);

putchar(’\n’);

printf("-------------------------------------------------------\n");

printf("Unija ova dva stabla:\n");

prikazi_stablo (pomocni);

putchar(’\n’);

printf("-------------------------------------------------------\n");

pomocni = oslobodi_stablo(pomocni);

pomocni = kopiraj_stablo(koren);

pomocni = kreiraj_presek(pomocni, koren_2);

printf("Presek ova dva stabla:\n");

prikazi_stablo (pomocni);

putchar(’\n’);

printf("-------------------------------------------------------\n");

pomocni = oslobodi_stablo(pomocni);

pomocni = kopiraj_stablo(koren);

pomocni = kreiraj_razliku(pomocni, koren_2);

printf("Razlika ova dva stabla:\n");

prikazi_stablo (pomocni);

putchar(’\n’);

printf("-------------------------------------------------------\n");

pomocni = oslobodi_stablo (pomocni);

koren_2 = oslobodi_stablo (koren_2);

koren = oslobodi_stablo (koren);

184

Page 186: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

return 0;

}

Primer 11.3 Primer demonstrira sortiranje niza pomocu binarnog stabla. Elementiniza se redom prvo smeste u binarno stablo pretrage, a zatim se elementi ”pokupe”iz stabla obilazeci ga sa leva u desno (tj. u rastucem poretku). Vremenska slozenostovakvog algoritma je u ekvivalentna quick_sort()-u. Prostorna slozenost je nestoveca, jer sortiranje nije ”u mestu”, vec se koristi pomocna struktura – binarnostablo.

#include <stdio.h>

#include <stdlib.h>

#define MAX 1000

/* Struktura koja predstavlja cvor stabla */

typedef struct cvor {

int vrednost;

struct cvor * levi;

struct cvor * desni;

} Cvor;

/* Funkcija kreira novi cvor */

Cvor * napravi_cvor (int broj)

{

Cvor * novi = (Cvor *) malloc (sizeof(Cvor));

if (novi == NULL)

{

fprintf (stderr,"malloc() greska\n");

exit (1);

}

novi->vrednost = broj;

novi->levi = NULL;

novi->desni = NULL;

return novi;

}

185

Page 187: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Funkcija dodaje novu vrednost u stablo. Dozvoljava

ponavljanje vrednosti u stablu -- levo se dodaju

manje vrednosti, a desno vece ili jednake. */

Cvor * dodaj_u_stablo (Cvor * koren, int broj)

{

if (koren == NULL)

return napravi_cvor (broj);

if (koren->vrednost <= broj)

koren->desni = dodaj_u_stablo (koren->desni, broj);

else if (koren->vrednost > broj)

koren->levi = dodaj_u_stablo (koren->levi, broj);

return koren;

}

/* Funkcija oslobadja stablo */

void oslobodi_stablo (Cvor *koren)

{

if(koren == NULL)

return;

oslobodi_stablo (koren->levi);

oslobodi_stablo (koren->desni);

free(koren);

}

/* Funkcija obilazi stablo sa leva u desno i

vrednosti cvorova smesta u niz. Funkcija

vraca broj vrednosti koje su smestene u niz. */

int kreiraj_niz(Cvor * koren, int a[])

{

int r, s;

if(koren == NULL)

return 0;

r = kreiraj_niz(koren->levi, a);

186

Page 188: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

a[r] = koren->vrednost;

s = kreiraj_niz(koren->desni, a + r + 1);

return r + s + 1;

}

/* Funkcija sortira niz tako sto najpre elemente

niza smesti u stablo, a zatim kreira novi niz

prolazeci kroz stablo sa leva u desno. */

void sortiraj(int a[], int n)

{

int i;

Cvor * koren = NULL;

for(i = 0; i < n; i++)

koren = dodaj_u_stablo(koren, a[i]);

kreiraj_niz(koren, a);

oslobodi_stablo(koren);

}

/* Test program */

int main()

{

int a[MAX];

int n, i;

printf("Uneti dimenziju niza manju od %d: ", MAX);

scanf("%d", &n);

printf("Uneti elemente niza: ");

for(i = 0; i < n; i++)

scanf("%d", &a[i]);

printf("Niz pre sortiranja:\n");

for(i = 0; i < n; i++)

printf("%d ", a[i]);

printf("\n");

187

Page 189: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

sortiraj(a, n);

printf("Niz nakon sortiranja:\n");

for(i = 0; i < n; i++)

printf("%d ", a[i]);

printf("\n");

return 0;

}

Primer 11.4 Program sa ulaza cita tekst i ispisuje broj pojavljivanja svake od recikoje su se javljale u tekstu. Radi poboljsanja efikasnosti, prilikom brojanja reci koristise struktura podataka pogodna za leksikografsku pretragu — u ovom slucaju binarnopretrazivacko drvo.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <ctype.h>

#define MAX 1024

typedef struct cvor {

char rec[MAX]; /* rec */

int brojac; /* broj pojavljivanja reci */

struct cvor * levi;

struct cvor * desni;

} Cvor;

/* Pomocna funkcija za kreiranje cvora. Funkcija vraca

adresu novokreiranog cvora */

Cvor * napravi_cvor (char * rec)

{

/* dinamicki kreiramo cvor */

Cvor * novi = (Cvor *) malloc (sizeof(Cvor));

/* u slucaju greske ... */

if (novi == NULL)

{

fprintf (stderr,"malloc() greska\n");

exit (1);

}

/* inicijalizacija */

188

Page 190: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

strcpy(novi->rec, rec);

novi->brojac = 1;

novi->levi = NULL;

novi->desni = NULL;

/* vracamo adresu novog cvora */

return novi;

}

/* Funkcija dodaje novi cvor u stablo sa datim korenom.

Ukoliko rec vec postoji u stablu, uvecava dati brojac.

Cvor se kreira funkcijom napravi_cvor(). Funkcija

vraca koren stabla nakon ubacivanja novog cvora. */

Cvor * dodaj_u_stablo (Cvor * koren, char *rec)

{

/* izlaz iz rekurzije: ako je stablo bilo prazno,

novi koren je upravo novi cvor */

if (koren == NULL)

return napravi_cvor (rec);

/* Ako je stablo neprazno, i koren sadrzi leksikografski

manju rec od date reci, broj se umece u desno podstablo,

rekurzivnim pozivom */

if (strcmp(koren->rec, rec) < 0)

koren->desni = dodaj_u_stablo (koren->desni, rec);

/* Ako je stablo neprazno, i koren sadrzi vecu vrednost

od datog broja, broj se umece u levo podstablo,

rekurzivnim pozivom */

else if (strcmp(koren->rec, rec) > 0)

koren->levi = dodaj_u_stablo (koren->levi, rec);

else

/* Ako je data rec vec u stablu, samo uvecavamo brojac. */

koren->brojac++;

/* Vracamo koren stabla */

return koren;

}

/* Funkcija pronalazi najfrekventniju rec, tj. cvor ciji

brojac ima najvecu vrednost. Funkcija vraca NULL, ako

je stablo prazno, odnosno adresu cvora koji sadrzi

najfrekventniju rec u suprotnom. */

189

Page 191: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

Cvor * nadji_najfrekventniju (Cvor * koren)

{

Cvor *max, *max_levi, *max_desni;

/* Izlaz iz rekurzije */

if (koren == NULL)

return NULL;

/* Odredjujemo najfrekventnije reci u levom i desnom podstablu */

max_levi = nadji_najfrekventniju(koren->levi);

max_desni = nadji_najfrekventniju(koren->desni);

/* Odredjujemo MAX(koren, max_levi, max_desni) */

max = koren;

if(max_levi != NULL && max_levi->brojac > max->brojac)

max = max_levi;

if(max_desni != NULL && max_desni->brojac > max->brojac)

max = max_desni;

/* Vracamo adresu cvora sa najvecim brojacem */

return max;

}

/* Prikazuje reci u leksikografskom poretku, kao i broj pojava svake

od reci */

void prikazi_stablo(Cvor *koren)

{

if(koren == NULL)

return;

prikazi_stablo(koren->levi);

printf("%s: %d\n", koren->rec, koren->brojac);

prikazi_stablo(koren->desni);

}

/* Funkcija oslobadja prostor koji je alociran za

cvorove stabla. Funkcija vraca NULL, zato sto je

nakon oslobadjanja stablo prazno. */

void oslobodi_stablo (Cvor *koren)

{

/* Izlaz iz rekurzije */

190

Page 192: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

if(koren == NULL)

return;

oslobodi_stablo (koren->levi);

oslobodi_stablo (koren->desni);

free(koren);

}

/* Funkcija ucitava sledecu rec iz fajla i upisuje je

u niz na koji pokazuje rec, maksimalne duzine max.

Funkcija vraca EOF ako nema vise reci, 0 u suprotnom.

Rec je niz alfabetskih karaktera.*/

int sledeca_rec(FILE *f, char * rec, int max)

{

int c;

int i = 0;

/* Dokle god ima mesta za jos jedan karakter u stringu,

i dokle god nismo stigli do kraja fajla... */

while(i < max - 1 && (c = fgetc(f)) != EOF)

{

/* Ako je slovo, ubacujemo ga u rec */

if(isalpha(c))

rec[i++] = tolower(c);

/* U suprotnom, ako smo procitali bar jedno slovo

prethodno, onda imamo rec. Inace idemo na sledecu

iteraciju */

else if(i > 0)

break;

}

/* Zatvaramo string */

rec[i] = ’\0’;

/* Vracamo 0 ako imamo rec, EOF u suprotnom */

return i > 0 ? 0 : EOF;

}

/* test program */

int main(int argc, char **argv)

{

191

Page 193: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

Cvor *koren = NULL, *max;

FILE *f;

char rec[MAX];

/* Proveravamo da li je navedeno ime datoteke */

if(argc < 2)

{

fprintf(stderr, "Morate uneti ime datoteke sa tekstom!\n");

exit(0);

}

/* Otvaramo datoteku */

if((f = fopen(argv[1], "r")) == NULL)

{

fprintf(stderr, "fopen() greska\n");

exit(1);

}

/* Ucitavamo reci iz datoteke. Rec je niz alfabetskih karaktera. */

while(sledeca_rec(f, rec, MAX) != EOF)

{

koren = dodaj_u_stablo(koren, rec);

}

/* Zatvaramo datoteku */

fclose(f);

/* Prikazujemo sve reci i brojeve njihovih pojavljivanja */

prikazi_stablo(koren);

/* Pronalazimo najfrekventniju rec */

if((max = nadji_najfrekventniju(koren)) == NULL)

printf("U tekstu nema reci!\n");

else

printf("Najcesca rec %s (pojavljuje se %d puta)\n", max->rec, max->brojac);

/* Oslobadjamo stablo */

oslobodi_stablo(koren);

return 0;

}

Primer 11.5 Mapa je apstraktna struktura podataka koja u sebi sadrzi parove oblika(kljuc, vrednost). Pri tom su i kljuc i vrednost unapred odredenog (ne obaveznoistog) tipa (na primer, kljuc nam moze biti string koji predstavlja ime studenta, avrednost je npr. broj koji predstavlja njegov prosek). Operacije koje mapa mora

192

Page 194: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

efikasno da podrzava su:

• dodavanje para (kljuc, vrednost) u mapu

• brisanje para (kljuc, vrednost) iz mape

• pronalazenje vrednosti za dati kljuc

• promena vrednosti za dati kljuc

Jedan od najcescih nacina implementacije mape je preko binarnog stabla pretrage.Svaki cvor ovog stabla sadrzace odgovarajuci par (kljuc, vrednost), tj. svaki cvorce sadrzati dva podatka. Pri tom ce poredak u stablu biti odreden poretkom koji jedefinisan nad kljucevima. Ovim se postize da se pretraga po kljucu moze obaviti nauobicajen nacin, kao i sve ostale navedene operacije.

Jasno je da ovako implementirana mapa nece efikasno podrzavati operaciju pre-trage po vrednosti (npr. naci sve kljuceve koji imaju datu vrednost), zato sto poredaku stablu nije ni na koji nacin u vezi sa poretkom medu vrednostima. Medutim, umapi se najcesce i ne zahteva takva operacija.

Sledi primer koji demonstrira implementaciju mapa pomocu binarnog stabla.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MAX 1024

/* Struktura koja predstavlja cvor stabla */

typedef struct cvor {

char naziv[MAX];

int cena;

struct cvor * levi;

struct cvor * desni;

} Cvor;

/* Funkcija kreira novi cvor i vraca njegovu adresu */

Cvor * napravi_cvor (char * naziv, int cena)

{

/* dinamicki kreiramo cvor */

Cvor * novi = (Cvor *) malloc (sizeof(Cvor));

/* u slucaju greske ... */

if (novi == NULL)

{

fprintf (stderr,"malloc() greska\n");

exit (1);

}

193

Page 195: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* inicijalizacija */

strcpy(novi->naziv, naziv);

novi->cena = cena;

novi->levi = NULL;

novi->desni = NULL;

/* vracamo adresu novog cvora */

return novi;

}

/* Funkcija dodaje novi cvor u stablo sa datim korenom.

U cvor se upisuje vrednost (naziv, cena). Ukoliko naziv

vec postoji u stablu, tada se azurira njegova cena.

Cvor se kreira funkcijom napravi_cvor(). Funkcija

vraca koren stabla nakon ubacivanja novog cvora. */

Cvor * dodaj_u_stablo (Cvor * koren, char * naziv, int cena)

{

/* izlaz iz rekurzije: ako je stablo bilo prazno,

novi koren je upravo novi cvor */

if (koren == NULL)

return napravi_cvor (naziv, cena);

/* Ako je stablo neprazno, i koren sadrzi naziv koji je

leksikografski manji od datog naziva, vrednost se umece

u desno podstablo, rekurzivnim pozivom */

if (strcmp(koren->naziv, naziv) < 0)

koren->desni = dodaj_u_stablo (koren->desni, naziv, cena);

/* Ako je stablo neprazno, i koren sadrzi naziv koji je

leksikografski veci od datog naziva, vrednost se umece

u levo podstablo, rekurzivnim pozivom */

else if (strcmp(koren->naziv, naziv) > 0)

koren->levi = dodaj_u_stablo (koren->levi, naziv, cena);

/* Ako je naziv korena jednak nazivu koja se umece, tada se samo

azurira cena. */

else

koren->cena = cena;

/* Vracamo koren stabla */

return koren;

}

194

Page 196: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Funkcija pretrazuje binarno stablo. Ukoliko

pronadje cvor sa vrednoscu naziva koja je jednaka

datom nazivu, vraca adresu tog cvora. U

suprotnom vraca NULL */

Cvor * pretrazi_stablo (Cvor * koren, char * naziv)

{

/* Izlaz iz rekurzije: ako je stablo prazno,

tada trazeni broj nije u stablu */

if (koren == NULL)

return NULL;

/* Ako je stablo neprazno, tada se pretrazivanje

nastavlja u levom ili desnom podstablu, u

zavisnosti od toga da li je trazeni naziv

respektivno manji ili veci od vrednosti naziva

korena. Ukoliko je pak trazeni naziv jednak

nazivu korena, tada se vraca adresa korena. */

if (strcmp(koren->naziv, naziv) < 0)

return pretrazi_stablo (koren->desni, naziv);

else if (strcmp(koren->naziv, naziv) > 0)

return pretrazi_stablo (koren->levi, naziv);

else

return koren;

}

/* cvor liste */

typedef struct cvor_liste {

char naziv[MAX];

int cena;

struct cvor_liste * sledeci;

} Cvor_liste;

/* Pomocna funkcija koja kreira cvor liste */

Cvor_liste * napravi_cvor_liste(char * naziv, int cena)

{

Cvor_liste * novi;

if((novi = malloc(sizeof(Cvor_liste))) == NULL)

{

fprintf(stderr, "malloc() greska\n");

exit(1);

195

Page 197: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

}

strcpy(novi->naziv, naziv);

novi->cena = cena;

novi->sledeci = NULL;

return novi;

}

/* Funkcija pronalazi sve nazive cija je cena manja ili jednaka od date,

i formira listu koja sadrzi nadjene parove (naziv, cena) u leksikogra-

fskom poretku po nazivima. Prilikom pocetnog poziva, treci argument

treba da bude NULL. Funkcija obilazi stablo sa desna u levo, kako bi

se prilikom dodavanja na pocetak liste poslednji dodao onaj koji je

leksikografski najmanji (tj. on ce biti na pocetku). */

Cvor_liste * pronadji_manje (Cvor * koren, int cena, Cvor_liste * glava)

{

if(koren == NULL)

return glava;

/* Dodajemo na pocetak liste sve cvorove desnog podstabla cija je cena

manja od date. */

glava = pronadji_manje(koren->desni, cena, glava);

/* Dodajemo koren u listu, ako mu je cena manja od date */

if(koren->cena <= cena)

{

Cvor_liste * novi = napravi_cvor_liste(koren->naziv, koren->cena);

novi->sledeci = glava;

glava = novi;

}

/* Dodajemo na pocetak liste sve cvorove levog podstabla cija je cena

manja od date */

glava = pronadji_manje(koren->levi, cena, glava);

/* Vracamo glavu liste nakon svih modifikacija */

return glava;

}

/* Funkcija prikazuje listu */

void prikazi_listu(Cvor_liste * glava)

{

if(glava == NULL)

196

Page 198: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

return;

printf("%s = %d\n", glava->naziv, glava->cena);

prikazi_listu(glava->sledeci);

}

/* Funkcija oslobadja listu */

Cvor_liste * oslobodi_listu(Cvor_liste * glava)

{

if(glava == NULL)

return NULL;

glava->sledeci = oslobodi_listu(glava->sledeci);

free(glava);

return NULL;

}

/* Funkcija oslobadja stablo. */

Cvor * oslobodi_stablo (Cvor *koren)

{

if(koren == NULL)

return NULL;

koren->levi = oslobodi_stablo (koren->levi);

koren->desni = oslobodi_stablo (koren->desni);

free(koren);

return NULL;

}

/* test program */

int main(int argc, char ** argv)

{

Cvor * koren = NULL, * pomocni;

Cvor_liste * glava = NULL;

FILE *f;

char naziv[MAX];

int cena;

/* Proveravamo da li je navedeno ime datoteke */

197

Page 199: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

if(argc < 2)

{

fprintf(stderr, "Morate navesti ime datoteke!\n");

exit(0);

}

/* Otvaramo datoteku */

if((f = fopen(argv[1], "r")) == NULL)

{

fprintf(stderr, "fopen() greska\n");

exit(1);

}

/* Ubacujemo proizvode u stablo */

while(fscanf(f, "%s%d", naziv, &cena) == 2)

{

koren = dodaj_u_stablo(koren, naziv, cena);

}

fclose(f);

/* Testiranje pretrage po nazivu (efikasna operacija) */

printf("Uneti naziv proizvoda koji vas zanima: ");

scanf("%s", naziv);

if((pomocni = pretrazi_stablo(koren, naziv)) == NULL)

printf("Trazeni proizvod ne postoji\n");

else

printf("Cena trazenog proizvoda je: %d\n", pomocni->cena);

/* Testiranje pretrage po ceni (neefikasno) */

printf("Unesite maksimalnu cenu: ");

scanf("%d", &cena);

glava = pronadji_manje(koren, cena, NULL);

prikazi_listu(glava);

/* Oslobadjanje memorije */

glava = oslobodi_listu(glava);

koren = oslobodi_stablo(koren);

return 0;

}

Primer 11.6 Program broji pojavljivanje svake etikete u tekstu i ispisuje etikete injihove frekvencije u opadajucem poretku po frekvencijama.

Radi poboljsanja efikasnosti, prilikom brojanja pojavljivanja etiketa koristi se

198

Page 200: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

struktura podataka pogodna za leksikografsku pretragu - u ovom slucaju binarno pre-trazivacko drvo.

Na kraju rada, cvorovi drveta se ”presortiraju” na osnovu broja pojavljivanja.

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <ctype.h>

/* Makimalna duzina imena etikete */

#define MAX_ETIKETA 32

/* Definisemo stanja prilikom citanja html datoteke */

#define VAN_ETIKETE 1

#define U_ETIKETI 2

/* Struktura koja predstavlja cvor stabla */

typedef struct cvor {

char etiketa[MAX_ETIKETA]; /* Ime etikete */

int brojac; /* Brojac pojavljivanja etkete u tekstu */

struct cvor *levi; /* Pokazivaci na levo */

struct cvor *desni; /* i desno podstablo */

} Cvor;

/* Funkcija dodaje ime html etikete u binarno stablo,

u rastucem leksikografskom poretku. Funkcija vraca

koren stabla nakon modifikacije. U slucaju da je

etiketa sa istim imenom vec u stablu, uvecava se

brojac pojavljivanja. */

Cvor * dodaj_leksikografski(Cvor *koren, char *etiketa)

{

int cmp;

/* Izlaz iz rekurzije */

if(koren == NULL)

{

if((koren = malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska\n");

exit(1);

}

/* Ime etikete se kopira u novi cvor, a brojac

se inicijalizuje na 1 (prvo pojavljivanje) */

199

Page 201: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

strcpy(koren->etiketa, etiketa);

koren->brojac = 1;

koren->levi = NULL;

koren->desni = NULL;

return koren;

}

/* Rekurzivni pozivi */

if((cmp = strcmp(koren->etiketa,etiketa)) < 0)

koren->desni = dodaj_leksikografski(koren->desni, etiketa);

else if(cmp > 0)

koren->levi = dodaj_leksikografski(koren->levi, etiketa);

else

koren->brojac++; /* uvecanje brojaca za vec prisutne etikete */

return koren;

}

/* Funkcija cita ulazni fajl i iz njega izdvaja sve

otvorene html etikete (npr. <html>, <br> itd, ali ne

i </html>, </body> itd.) i njihova imena (bez znakova

(< i > kao i bez eventualnih atributa) ubacuje u stablo

u leksikografskom poretku. Tom prilikom ce se prebrojati

i pojavljivanja svake od etiketa. Funkcija vraca pokazivac

na koren kreiranog stabla. */

Cvor * ucitaj(FILE *f)

{

Cvor * koren = NULL;

int c;

int stanje = VAN_ETIKETE;

int i;

char etiketa[MAX_ETIKETA];

/* NAPOMENA: prilikom izdvajanja etiketa pretpostavlja se da je

html datoteka sintaksno ispravna, kao i da nakon znaka ’<’

nema belina. Zato se otvorene etikete mogu lako prepoznati

tako sto pronadjemo znak ’<’ i nakon toga izdvojimo sva

slova koja slede. Ako postoji neprazan niz slova nakon znaka

’<’, tada je to upravo ime etikete. Ako ne, tada je verovatno

u pitanju zatvorena etiketa, npr. </html>, pa je ignorisemo. */

while((c = fgetc(f)) != EOF)

{

switch(stanje)

200

Page 202: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

{

/* u ovom stanju trazimo prvu sledecu pojavu znaka ’<’ */

case VAN_ETIKETE:

if(c == ’<’)

{

stanje = U_ETIKETI; /* sada smo u etiketi */

i = 0;

}

break;

/* u ovom stanju citamo slova koja slede, i nakon toga

ubacujemo procitanu etiketu u stablo */

case U_ETIKETI:

/* Ako je slovo, i nismo prekoracili duzinu niza

dodajemo slovo u niz */

if(isalpha(c) && i < MAX_ETIKETA - 1)

etiketa[i++] = c;

/* u suprotnom se vracamo u stanje VAN_ETIKETE */

else {

stanje = VAN_ETIKETE;

/* Ako je niz slova nakon ’<’ bio neprazan... */

if(i > 0)

{

etiketa[i]=’\0’;

/* ubacujemo procitanu etiketu u stablo */

koren = dodaj_leksikografski(koren, etiketa);

}

}

break;

}

}

return koren;

}

/* Funkcija dodaje u stablo etiketu ciji je broj pojavljivanja

poznat (broj), i to u opadajucem poretku po broju pojavljivanja.

Funkcija vraca pokazivac na koren tako modifikovanog stabla */

Cvor * dodaj_po_broju(Cvor * koren, char * etiketa, int broj)

{

/* Izlaz iz rekurzije */

if(koren == NULL)

201

Page 203: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

{

if((koren = malloc(sizeof(Cvor))) == NULL)

{

fprintf(stderr,"malloc() greska\n");

exit(1);

}

/* Kopiramo u novi cvor ime i broj pojavljivanja etikete */

strcpy(koren->etiketa, etiketa);

koren->brojac = broj;

koren->levi = NULL;

koren->desni = NULL;

return koren;

}

/* NAPOMENA: s obzirom da dve ili vise etiketa mogu imati isti broj

pojavljivanja, etiketa se mora dodavati u stablo, cak i ako ima

isti broj pojavljivanja sa korenom. Zato cemo u levo podstablo

dodavati etikete koje imaju veci broj pojavljivanja od korena,

dok cemo u desno podstablo dodavati etikete koje imaju manji

ili jednak broj pojavljivanja od korena. */

/* Rekurzivni pozivi */

if(koren->brojac >= broj)

koren->desni = dodaj_po_broju(koren->desni, etiketa, broj);

else if(koren->brojac < broj)

koren->levi = dodaj_po_broju(koren->levi, etiketa, broj);

return koren;

}

/* Funkcija pretpostavlja da je novo stablo ili prazno, ili

uredjeno prema opadajucem poretku broja pojavljavanja

etiketa. Funkcija u takvo stablo rekurzivno dodaje sve

etikete iz starog stabla, i vraca koren na tako modifikovano

novo stablo. */

Cvor * resortiraj_stablo(Cvor * staro, Cvor * novo)

{

/* Izlaz iz rekurzije - nemamo sta da dodamo u novo stablo */

if(staro == NULL)

return novo;

202

Page 204: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Dodajemo etiketu iz korena starog stabla u novo stablo,

sortirano opadajuce prema broju pojavljivanja */

novo = dodaj_po_broju(novo, staro->etiketa, staro->brojac);

/* Rekurzivno dodajemo u novo stablo sve cvorove iz levog

i desnog podstabla starog stabla. */

novo = resortiraj_stablo(staro->levi, novo);

novo = resortiraj_stablo(staro->desni, novo);

return novo;

}

/* Ispis stabla - s leva na desno */

void ispisi_stablo(Cvor * koren)

{

if(koren == NULL)

return;

ispisi_stablo(koren->levi);

printf("%s: %d\n", koren->etiketa, koren->brojac);

ispisi_stablo(koren->desni);

}

/* Oslobadjanje dinamicki alociranog prostora */

void oslobodi_stablo(Cvor *koren)

{

if(koren == NULL)

return;

oslobodi_stablo(koren->levi);

oslobodi_stablo(koren->desni);

free(koren);

}

/* glavni program */

int main(int argc, char **argv)

{

FILE *in = NULL; /* Ulazni fajl */

Cvor * koren = NULL; /* Stablo u leksikografskom poretku */

Cvor * resortirano = NULL; /* stablo u poretku po broju pojavljivanja */

203

Page 205: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Mora se zadati makar ime ulaznog fajla na komandnoj liniji.

Opciono se kao drugi argument moze zadati opcija "-b"

sto dovodi do ispisa po broju pojavljivanja (umesto leksikografski) */

if(argc < 2)

{

fprintf(stderr, "koriscenje: %s ime_fajla [-b]\n", argv[0]);

exit(1);

}

/* Otvaramo fajl */

if((in = fopen(argv[1], "r")) == NULL)

{

fprintf(stderr, "fopen() greska\n");

exit(1);

}

/* Formiramo leksikografsko stablo etiketa */

koren = ucitaj(in);

/* Ako je korisnok zadao "-b" kao drugi argument, tada

se vrsi resortiranje, i ispisuje izvestaj sortiran

opadajuce po broju pojavljivanja */

if(argc == 3 && strcmp(argv[2], "-b") == 0)

{

resortirano = resortiraj_stablo(koren, resortirano);

ispisi_stablo(resortirano);

}

/* U suprotnom se samo ispisuje izvestaj u leksikografskom poretku */

else

ispisi_stablo(koren);

/* Oslobadjamo stabla */

oslobodi_stablo(koren);

oslobodi_stablo(resortirano);

return 0;

}

Primer 11.7 Efikasnije sortiranje corova stabla — formira se niz pokazivaca nacvorove stabla koji se u skladu sa funkcijom poredenja sortiraju pomocu biblioteckeqsort funkcije.

#include <stdlib.h>

#include <stdio.h>

204

Page 206: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Cvor drveta sadrzi ime reci i

broj njenih pojavljivanja */

typedef struct _cvor {

char ime[80];

int br_pojavljivanja;

struct _cvor* levo, *desno;

} cvor;

/* Gradimo niz pokazivaca na cvorove drveta koji ce

nam sluziti da po prihvatanju svih reci izvrsimo

sortiranje po broju pojavljivanja */

#define MAX_BROJ_RAZLICITIH_RECI 1000

cvor* razlicite_reci[MAX_BROJ_RAZLICITIH_RECI];

/* Tekuci broj cvorova drveta */

int broj_razlicitih_reci=0;

/* Funkcija uklanja binarno drvo iz memorije */

void obrisi_drvo(cvor* drvo)

{

if (drvo!=NULL)

{ obrisi_drvo(drvo->levo);

obrisi_drvo(drvo->desno);

free(drvo);

}

}

cvor* napravi_cvor(char rec[])

{

cvor* novi_cvor=(cvor*)malloc(sizeof(cvor));

if (novi_cvor==NULL)

{

printf("Greska prilikom alokacije memorije\n");

exit(1);

}

strcpy(novi_cvor->ime, rec);

novi_cvor->br_pojavljivanja=1;

return novi_cvor;

}

205

Page 207: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

/* Funkcija ubacuje datu rec u dato drvo i vraca pokazivac na

koren drveta */

cvor* ubaci(cvor* drvo, char rec[])

{

/* Ukoliko je drvo prazno gradimo novi cvor */

if (drvo==NULL)

{

cvor* novi_cvor = napravi_cvor(rec);

/* pokazivac na novo napravljeni cvor smestamo u niz

pokazivaca na sve cvorove drveta */

razlicite_reci[broj_razlicitih_reci++] = novi_cvor;

return novi_cvor;

}

int cmp = strcmp(rec, drvo->ime);

/* Ukoliko rec vec postoji u drvetu

uvecavamo njen broj pojavljivanja */

if (cmp==0)

{ drvo->br_pojavljivanja++;

return drvo;

}

/* Ukoliko je rec koju ubacujemo leksikografski ispred

reci koja je u korenu drveta, rec ubacujemo

u levo podstablo */

if (cmp<0)

{ drvo->levo=ubaci(drvo->levo, rec);

return drvo;

}

/* Ukoliko je rec koju ubacujemo

leksikografski iza reci koja je u

korenu drveta, rec ubacujemo

u desno podstablo */

if (cmp>0)

{ drvo->desno=ubaci(drvo->desno, rec);

return drvo;

}

}

/* Pomocna funkcija koja cita rec iz date datoteke i vraca

206

Page 208: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

njenu duzinu, odnosno -1 ukoliko se naidje na EOF */

int getword(char word[], int lim, FILE* ulaz)

{

int c, i=0;

/* Umesto funkcije getchar koristimo fgetc

za rad sa datotekama */

while (!isalpha(c=fgetc(ulaz)) && c!=EOF)

;

if (c==EOF)

return -1;

do

{ word[i++]=c;

}while (i<lim-1 && isalpha(c=fgetc(ulaz)));

word[i]=’\0’;

return i;

}

/* Funkcija poredjenja za funkciju qsort. */

int poredi_br_pojavljivanja(const void* a, const void* b)

{

return

/* Konverzija pokazivaca b iz pokazivaca

na tip void u pokazivac na cvor jer

nam je svaki element niza koji sortiramo

tipa pokazivaca na cvor */

(*(cvor**)b)->br_pojavljivanja

-

(*(cvor**)a)->br_pojavljivanja;

}

main(int argc, char* argv[])

{

int i;

/* Drvo je na pocetku prazno */

cvor* drvo=NULL;

char procitana_rec[80];

FILE* ulaz;

if (argc!=2)

{ fprintf(stderr,"Greska :

Ocekivano ime datoteke\n");

207

Page 209: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic 11.1 Binarno pretrazivacko stablo

exit(1);

}

if ((ulaz=fopen(argv[1],"r"))==NULL)

{

fprintf(stderr,"Greska : nisam uspeo da otvorim datoteku %s\n");

exit(1);

}

/* Citamo rec po rec dok ne naidjemo na kraj datoteke i

ubacujemo ih u drvo */

while(getword(procitana_rec,80,ulaz)!=-1)

drvo=ubaci(drvo,procitana_rec);

/* Sortiramo niz pokazivaca na cvorove

drveta po broju pojavljivanja */

qsort(razlicite_reci,

broj_razlicitih_reci,

sizeof(cvor*),

poredi_br_pojavljivanja);

/* Ispisujemo prvih 10 (ukoliko ih ima)

reci i njihov broj pojavljivanja */

for (i=0; i<10 && i<broj_razlicitih_reci; i++)

printf("%s - %d\n",razlicite_reci[i]->ime,

razlicite_reci[i]->br_pojavljivanja);

/* Uklanjamo drvo iz memorije */

obrisi_drvo(drvo);

fclose(ulaz);

}

208

Page 210: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Glava 12

Grafovi

Graf1 G=(V,E) sastoji se od skupa V cvorova i skupa E grana. Grane predstavljajurelacije izmedu cvorova i odgovara paru cvorova. Graf moze biti usmeren (orijen-tisan), ako su mu grane uredeni parovi i neusmeren (neorjentisan) ako su graneneuredeni parovi.

Put u grafu je niz cvorova v1,v2,...,vn, pri cemu u grafu postoje grane (v1,v2),(v2,v3), ..., (vn-1, vn). Ako jos vazi da je v1 = vn, tada se ovakav put naziva ciklus.

Iako su liste i stabla specijalni oblici grafova, u programiranju postoji bitna raz-lika: liste i stabala se prevashodno koriste kao kontejneri za podatke (u svaki cvorse smesta neki podatak odredenog tipa), grafovi se pre svega koriste za modeliranjenekih problema iz prakse, kako bi se ti problemi predstavili na apstraktan nacini resili primenom algoritama nad odgovarajucim grafom. Na primer, cvorovima ugrafu se mogu predstaviti raskrsnice u gradu, a granama ulice koji ih povezuju. Tadase zadatak nalazenja najkraceg puta od tacke A do tacke B u gradu moze svesti nanalazenje najkraceg puta od cvora u do cvora v u grafu. Slicno se mogu resavatiproblemi sa racunarskim mrezama (racunari se predstave cvorovima, a veze medunjima granama), itd.

Grafovi se mogu predstavljati na vise nacina. Uobicajena su dva nacina pred-stavljanja grafova. To su matrica povezanosti grafa i lista povezanosti.

Matrica povezanosti je kvadratna matrica dimenzije n, pri cemu je n broj cvorovau grafu, takva da je element na preseku i-te vrste i j-te kolone jednak jedinici uko-liko postoji grana u grafu od i-tog do j-tog cvora, inace je nula. Jasno je dace neusmerenim grafovima odgovarati simetricne matrice. Prednost ovakvog pred-stavljanja je jednostavnost. Nedostatak ovakvog pristupa je neracionalno koriscenjememorije u slucaju ”retkih” grafova (grafova sa malo grana), gde ce najveci brojelemenata matrice biti 0.

Umesto da se i sve nepostojece grane eksplicitno predstavljaju u matrici povezanosti,mogu se formirati povezane liste od jedinica iz i-te vrste za i=1,2,...,n. To jelista povezanosti. Svakom cvoru se pridruzuje povezana lista, koja sadrzi sve granesusedne tom cvoru. Graf je predstavljen vektorom lista. Svaki elemenat vektora

1Tekst i primeri preuzeti od Jelene Tomasevic, www.matf.bg.ac.yu/~jtomasevic, i MilanaBankovica www.matf.bg.ac.yu/~milan zasnovano na materijalu Algoritmi, Miodrag Zivkovic ihttp://www.matf.bg.ac.yu/~filip

209

Page 211: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

sadrzi ime (indeks) cvora i pokazivac na njegovu listu cvorova. Ovakav pristup stedimemoriju, a omogucava i efikasno dodavanje novih cvorova i grana.

Osnovni problem koji se javlja kod grafova jeste kako polazeci od nekog cvora, kre-tanjem kroz puteva grafa posetiti sve cvorove. Ovakav postupak se naziva obilazakgrafa. Postoje dva osnovna algoritma za obilazak grafa: pretraga u dubinu (DFS,skracenica od depth-first-search) i pretraga u sirinu (BFS, skracenica od breadth-first-search).

Kod DFS algoritma, obilazak zapocinje iz proizvoljnog zadatog cvora r koji senaziva koren pretrage u dubinu. Koren se oznacava kao posecen. Zatim se biraproizvoljan neoznacen cvor r1, susedan sa r, pa se iz cvora r1 rekurzivno startujepretraga u dubinu. Iz nekog nivoa rekurzije izlazi se kad se naide na cvor v kome susvi susedi vec oznaceni.

Primer 12.1 Primer reprezentovanja grafa preko matrice povezanosti. U programuse unosi neorijentisan graf i DFS algoritmom se utvrduju cvrovi koji su dostizni izcvora 0.

#include <stdlib.h>

#include <stdio.h>

int** alociraj_matricu(int n)

{ int **matrica;

int i;

matrica=malloc(n*sizeof(int*));

if (matrica==NULL)

{

printf("Greska prilikom alokacije memorije\n");

exit(1);

}

for (i=0; i<n; i++)

{

/* Funkcija calloc popunjava rezervisan

prostor u memoriji nulama. */

matrica[i]=calloc(n,sizeof(int));

if (matrica[i]==NULL)

{

printf("Greska prilikom alokacije memorije\n");

exit(1);

}

}

return matrica;

}

void oslobodi_matricu(int** matrica, int n)

{ int i;

210

Page 212: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

for (i=0; i<n; i++)

free(matrica[i]);

free(matrica);

}

int* alociraj_niz(int n)

{ int* niz;

niz=calloc(n,sizeof(int));

if (niz==NULL)

{

printf("Greska prilikom alokacije memorije\n");

exit(1);

}

return niz;

}

void oslobodi_niz(int* niz)

{ free(niz);

}

void unesi_graf(int** graf, int n)

{ int i,j;

for (i=0; i<n; i++)

for (j=i; j<n; j++)

{ printf("Da li su element %d i %d povezani : ",i,j);

do

{ scanf("%d",&graf[i][j]);

/* Radimo sa neusmerenim grafom */

graf[j][i]=graf[i][j];

} /* Obezbedjujemo ispravan unos */

while (graf[i][j]!=0 && graf[i][j]!=1);

}

}

void ispisi_graf(int** graf, int n)

{ int i,j;

for (i=0; i<n; i++)

{ for (j=0; j<n; j++)

printf("%d",graf[i][j]);

printf("\n");

}

}

211

Page 213: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

/* Rekurzivna implementacija DFS algoritma */

void poseti(int i, int** graf, int n, int* posecen)

{ int j;

posecen[i]=1;

printf("Posecujem cvor %d\n",i);

for (j=0; j<n; j++)

if (graf[i][j] && !posecen[j])

poseti(j);

}

main()

{

/* Broj cvorova grafa (dimenzija matrice) */

int n;

/* Matrica povezanosti */

int **graf;

/* Pomocni vektor koji govori o tome koji su cvorovi posecivani

tokom DFS obilaska */

int *posecen;

int i, j;

printf("Unesi broj cvorova : ");

scanf("%d",&n);

graf=alociraj_matricu(n);

unesi_graf(graf,n);

ispisi_graf(graf,n);

posecen=alociraj_niz(n);

poseti(0, graf, n);

oslobodi_niz(posecen);

oslobodi_matricu(graf,n);

}

Primer 12.2 Primer demonstrira osnovne algoritme nad grafovima. U ovom primergrafovi se predstavljaju matricama povezanosti. Zbog jednostavnosti, pretpostavlja seda je broj cvorova ogranicen nekom fiksiranom konstantom, tako da se odgovarajucematrice alociraju staticki. Takode, da bi se pojednostavile liste argumenata pojedinihfunkcija, vecina promenljivih u ovom primeru se deklarisu kao globalne, kako bi kaotakve bile dostupne svim funkcijama.

#include <stdio.h>

#include <stdlib.h>

212

Page 214: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

#define MAX_CVOROVA 100

/* Matrica povezanosti grafa. Pretpostavka je da broj

cvorova grafa nece biti veca od MAX_CVOROVA */

int graf[MAX_CVOROVA][MAX_CVOROVA];

int broj_cvorova;

/* Niz 0/1 vrednosti kojim se obelezavaju poseceni cvorovi */

int posecen[MAX_CVOROVA];

/* Pomocna funkcija koja utvrdjuje da li postoji neposecen cvor.

Funkcija vraca indeks prvog takvog cvora, ili -1 u slucaju da

su svi cvorovi vec poseceni */

int postoji_neposecen()

{

int i;

for(i = 0; i < broj_cvorova; i++)

if(!posecen[i]) return i;

return -1;

}

/* Pomocna funkcija koja sluzi za inicijalizaciju raznih nizova

(poput posecen[], dolazna_numeracija[] itd.) Za sve nizove

se pretpostavlja da su duzine broj_cvorova. */

void inicijalizuj(int niz[])

{

int i;

for(i = 0 ; i < broj_cvorova ; i++)

niz[i] = 0;

}

/* NAPOMENA: za potrebu demonstracije obilaska grafa, obrada koja

se prilikom obilaska vrsi bice numerisanje cvorova grafa u

poretku posecivanja. */

/* Staticki podaci koji se koriste za smestanje rezultata

prilikom numeracije cvorova (i za DFS i za BFS). */

int dolazna_numeracija[MAX_CVOROVA];

213

Page 215: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

int odlazna_numeracija[MAX_CVOROVA];

/* Donji brojaci treba da se postave na nulu pre pozivanja

funkcija za numeraciju */

int brojac_dolazna=0;

int brojac_odlazna=0;

/* Funkcija obilazi graf DFS algoritmom, i tom prilikom

vrsi dolaznu i odlaznu numeraciju cvorova. Pre poziva

ove funkcije obavezno je postaviti brojace dolazne i

odlazne numeracije na 0 */

void DFS_numeracija(int polazni_cvor)

{

int i;

/* Obelezavanje */

posecen[polazni_cvor] = 1;

/* Ulazna obrada */

dolazna_numeracija[polazni_cvor] = ++brojac_dolazna;

/* Rekurzija (za sve cvorove ka kojima postoji grana

i koji jos nisu obelezeni) */

for(i = 0 ; i < broj_cvorova; i++)

if(graf[polazni_cvor][i] && !posecen[i])

DFS_numeracija(i);

/* Izlazna obrada */

odlazna_numeracija[polazni_cvor] = ++brojac_odlazna;

}

/* Funkcija vrsi BFS obilazak i numerise cvorove u poretku

obilaska. Pre poziva ove funkcije treba inicijalizovati

posecen[] i brojac_dolazna */

void BFS_numeracija(int polazni_cvor)

{

/* Improvizovani red koji koristimo za smestanje

cvorova koji cekaju da budu obradjeni. Cvorovi

se smestaju na kraj reda (kraj niza), a uzimaju

sa pocetka reda (niza). Zato imamo dva indeksa

koji pokazuju na pocetak i kraj reda. Duzina

ovog reda je MAX_CVOROVA sto je dovoljno jer

ce svaki cvor na red biti postavljen tacno jednom */

214

Page 216: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

int red[MAX_CVOROVA];

int sledeci_na_redu = 0;

int poslednji_na_redu = -1;

int i;

/* Inicijalno se na redu nalazi samo polazni cvor */

posecen[polazni_cvor] = 1;

red[++poslednji_na_redu] = polazni_cvor;

/* Dokle god imamo cvorove u redu...*/

while(sledeci_na_redu <= poslednji_na_redu)

{

/* Vrsimo obradu cvora (numeracija) */

dolazna_numeracija[red[sledeci_na_redu]] = ++brojac_dolazna;

/* Za sve cvorove ka kojima postoji grana i koji nisu

poseceni vrsimo obelezavanje i dodavanje u red */

for(i = 0; i < broj_cvorova; i++)

if(graf[red[sledeci_na_redu]][i] && !posecen[i])

{

red[++poslednji_na_redu] = i;

posecen[i] = 1;

}

/* Prelazimo na sledeci u redu */

sledeci_na_redu++;

}

}

/* Test program */

int main()

{

int i,j;

int aciklican;

/* Unos matrice povezanosti grafa */

printf("Unesite broj cvorova grafa (<=%d): ", MAX_CVOROVA);

scanf("%d",&broj_cvorova);

for(i = 0; i < broj_cvorova; i++)

for(j = 0; j < broj_cvorova; j++)

{

if(i == j) continue;

printf("Da li postoji grana (%d,%d) (0/1): ", i, j);

215

Page 217: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

scanf("%d", &graf[i][j]);

}

/* Testiramo DFS numeraciju */

inicijalizuj(posecen);

brojac_dolazna = 0;

brojac_odlazna = 0;

while((i = postoji_neposecen()) >= 0)

DFS_numeracija(i);

printf("Dolazna DFS numeracija: ");

for(i = 0; i < broj_cvorova; i++)

printf("%d ",dolazna_numeracija[i]);

putchar(’\n’);

printf("Odlazna DFS numeracija: ");

for(i = 0; i < broj_cvorova; i++)

printf("%d ", odlazna_numeracija[i]);

putchar(’\n’);

/* Testiramo BFS numeraciju */

inicijalizuj(posecen);

brojac_dolazna = 0;

while((i = postoji_neposecen()) >= 0)

BFS_numeracija(i);

printf("Dolazna BFS numeracija: ");

for(i = 0; i < broj_cvorova; i++)

printf("%d ",dolazna_numeracija[i]);

putchar(’\n’);

return 0;

}

Primer 12.3 Primer predstavljanja grafa preko niza listi suseda svakog od cvorovagrafa. U programu se unosi graf i DFS algoritmom se utvrdjuje koji su cvorovidostizni iz cvora 0.

#include <stdlib.h>

#include <stdio.h>

/* Cvor liste suseda */

216

Page 218: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

typedef struct _cvor_liste

{ int broj; /* Indeks suseda */

struct _cvor_liste* sledeci;

} cvor_liste;

/* Ubacivanje na pocetak liste */

cvor_liste* ubaci_u_listu(cvor_liste* lista, int broj)

{ cvor_liste* novi=malloc(sizeof(cvor_liste));

if (novi==NULL)

{

printf("Greska prilikom alokacije memorije\n");

exit(1);

}

novi->broj=broj;

novi->sledeci=lista;

return novi;

}

/* Rekurzivno brisanje liste */

void obrisi_listu(cvor_liste* lista)

{ if (lista)

{ obrisi_listu(lista->sledeci);

free(lista);

}

}

/* Ispis liste */

void ispisi_listu(cvor_liste* lista)

{ if (lista)

{ printf("%d ",lista->broj);

ispisi_listu(lista->sledeci);

}

}

#define MAX_BROJ_CVOROVA 100

/* Graf predstavlja niz pokazivaca na pocetke listi suseda */

cvor_liste* graf[MAX_BROJ_CVOROVA];

int broj_cvorova;

int posecen[MAX_BROJ_CVOROVA];

/* Rekurzivna implementacija DFS algoritma */

void poseti(int i)

217

Page 219: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

{ cvor_liste* sused;

printf("Posecujem cvor %d\n",i);

posecen[i]=1;

for( sused=graf[i]; sused!=NULL; sused=sused->sledeci)

if (!posecen[sused->broj])

poseti(sused->broj);

}

void oslobodi_graf(cvor_liste* graf[], int broj_cvorova)

{

int i;

for(i=0; i<broj_cvorova; i++)

obrisi_listu(graf[i]);

}

main()

{

int i;

printf("Unesi broj cvorova grafa : ");

scanf("%d",&broj_cvorova);

/*Unos grafa*/

for (i=0; i<broj_cvorova; i++)

{ int br_suseda,j;

graf[i]=NULL;

printf("Koliko cvor %d ima suseda : ",i);

scanf("%d",&br_suseda);

for (j=0; j<br_suseda; j++)

{ int sused;

do

{

printf("Unesi broj %d.-tog suseda cvora %d : ",j,i);

scanf("%d",&sused);

} while (sused<1 && sused>broj_cvorova);

graf[i]=ubaci_u_listu(graf[i],sused-1);

}

}

/*Ispis grafa*/

for (i=0; i<broj_cvorova; i++)

{ printf("%d - ",i);

ispisi_listu(graf[i]);

printf("\n");

218

Page 220: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

}

/*Koji su sve cvorovi dostupni iz cvora 0*/

poseti(0);

/*Oslobadjanje memorije*/

oslobodi_graf(graf, broj_cvorova);

}

Primer 12.4 MINESWEEPER - primer jednostavne igrice.

#include <stdlib.h>

#include <stdio.h>

#include <time.h>

/* Dimenzija table */

int n;

/* Tabla koja sadrzi 0 i 1 u zavisnosti

od toga da li na polju postoji bomba */

int** bombe;

#define PRAZNO (-1)

#define ZATVORENO 0

#define ZASTAVICA 9

/* Tabla koja opisuje tekuce stanje igre.

Moze da sadrzi sledece vrednosti :

ZATVORENO - opisuje polje

koje jos nije bilo otvarano

PRAZNO - polje na kome ne

postoji ni jedna bomba

BROJ od 1-8 - polje koje je

otvoreno i na kome pise

koliko bombi postoji u okolini

ZASTAVICA - polje koje je korisnik

oznacio zastavicom

*/

int** stanje;

/* Ukupan broj bombi */

int broj_bombi;

/* Ukupan broj postavljenih zastavica */

int broj_zastavica = 0;

/* Pomocne funkcije za rad sa matricama */

219

Page 221: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

int** alociraj(int n)

{

int i;

int** m=(int**)malloc(n*sizeof(int*));

if(m==NULL) {printf("Greska prilikom alokacije memorije\n"); exit(1);}

for (i=0; i<n; i++)

{

m[i]=(int *)calloc(n,sizeof(int));

if(m[i]==NULL)

{printf("Greska prilikom alokacije memorije\n"); exit(1);}

}

return m;

}

void obrisi(int** m, int n)

{ int i;

for (i=0; i<n; i++)

free(m[i]);

free(m);

}

/* Funkcija postavlja bombe */

void postavi_bombe()

{

broj_bombi=(n*n)/6;

int kolona;

int vrsta;

int i;

/* Inicijalizujemo generator slucajnih brojeva */

srand(time(NULL));

for (i=0; i<broj_bombi; i++)

{ /* Racunamo slucajni polozaj bombe */

kolona=rand()%n;

vrsta=rand()%n;

/* Ukoliko bomba vec postoji tu,

opet idemo u istu iteraciju */

if (bombe[vrsta][kolona]==1)

{ i--;

continue;

220

Page 222: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

}

/* Postavljamo bombu */

bombe[vrsta][kolona]=1;

}

}

/* Funkcija ispisuje tablu sa bombama */

void ispisi_bombe()

{

int i,j;

for (i=0; i<n; i++)

{ for (j=0; j<n; j++)

printf("%d",bombe[i][j]);

printf("\n");

}

}

/* Funkcija ispisuje tekuce stanje */

void ispisi_stanje()

{ int i,j;

for (i=0; i<n; i++)

{ for (j=0; j<n; j++)

{ if (stanje[i][j]==ZATVORENO)

printf(".");

else if (stanje[i][j]==PRAZNO)

printf(" ");

else if (stanje[i][j]==ZASTAVICA)

printf("*");

else

printf("%d",stanje[i][j]);

}

printf("\n");

}

}

/* Funkcija postavlja zastavicu na

dato polje ili je uklanja

ukoliko vec postoji */

void postavi_zastavicu(int i, int j)

{

if (stanje[i][j]==ZATVORENO)

{ stanje[i][j]=ZASTAVICA;

broj_zastavica++;

221

Page 223: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

}

else if (stanje[i][j]==ZASTAVICA)

{ stanje[i][j]=ZATVORENO;

broj_zastavica--;

}

}

/* Funkcija izracunava koliko bombi

postoji u okolini date bombe */

int broj_bombi_u_okolini(int v, int k)

{ int i, j;

int br=0;

/* Prolazimo kroz sva okolna polja */

for (i=-1; i<=1; i++)

for(j=-1; j<=1; j++)

{ /* preskacemo centralno polje */

if (i==0 && j==0)

continue;

/* preskacemo polja "van table" */

if (v+i<0 || k+j<0 || v+i>=n || k+j>=n)

continue;

if (bombe[v+i][k+j]==1)

br++;

}

return br;

}

/* Centralna funkcija koja vrsi otvaranje

polja i pritom se otvaranje "siri"

i na polja koja su oko datog */

void otvori_polje(int v, int k) {

/* Ukoliko smo "nagazili" bombu

zavrsavamo program */

if (bombe[v][k]==1)

{ printf("BOOOOOOOOOOOOOOOOM!!!!\n");

ispisi_bombe();

exit(1);

}

else

{

/* Brojimo bombe u okolini */

int br=broj_bombi_u_okolini(v,k);

222

Page 224: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

/* Azuriramo stanje ovog polja */

stanje[v][k]=(br==0)?PRAZNO:br;

/* Ukoliko u okolini nema bombi,

rekurzivno otvaramo

sva polja u okolini koja su zatvorena */

if (br==0)

{

/* Petlje indeksiraju sva okolna polja */

int i,j;

for (i=-1; i<=1; i++)

for (j=-1; j<=1; j++)

{

/* Preskacemo centralno polje */

/* if (i==0 && j==0)

continue; */

/* Preskacemo polja van table */

if (v+i<0 || v+i>=n || k+j<0 || k+j>=n)

continue;

/* Ukoliko je okolno polje

zatvoreno, otvaramo ga */

if (stanje[v+i][k+j]==ZATVORENO)

otvori_polje(v+i, k+j);

}

}

}

}

/* Funkcija utrdjuje da li je partija gotova

Partija je gotova u trenutku kada su sve

bombe pokrivene zastavicama i

kada nijedno drugo polje nije

pokriveno zastavicom */

int gotova_partija()

{ int i,j;

for (i=0; i<n; i++)

for (j=0; j<n; j++)

{ /* Ukoliko postoji nepokrivena bomba,

partija nije zavrsena */

if (bombe[i][j]==1 && stanje[i][j]!=ZASTAVICA)

return 0;

}

/* Partija je zavrsena samo ukoliko je

223

Page 225: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

broj zastavica jednak broj bombi */

return broj_zastavica==broj_bombi;

}

main() {

/* Unosimo dimenziju table */

printf("Unesite dimenziju table : ");

scanf("%d",&n);

/* Alociramo table */

bombe=alociraj(n);

stanje=alociraj(n);

/* Postavljamo bombe */

postavi_bombe();

/* Sve dok partija nije gotova */

while(!gotova_partija())

{ int v,k;

char akcija;

/* Ispisujemo tekuce stanje */

ispisi_stanje();

/* Sve dok korisnik ne unese o ili z

trazimo od njega da upise

odgovarajucu akciju */

do

{

getchar();

printf("Unesi akciju (o - otvaranje polja,

z - postavljanje zastavice) : ");

scanf("%c",&akcija);

} while (akcija!=’o’ && akcija!=’z’);

/* Trazimo od korisnika da unese koordinate

polja sve dok ih ne unese ispravno

Korisnicke koordinate krecu od 1,

a interne od 0 */

do

{

printf("Unesi koordinate polja : ");

scanf("%d",&v);

224

Page 226: Programiranje 2 - poincare.matf.bg.ac.rspoincare.matf.bg.ac.rs/~milena/OPcasovi/0809PR2.pdf · Glava 2 Datoteke Primer 2.1 Program demonstrira otvaranje datoteka ("r"- read i "w"-

Milena Vujosevic–Janicic Grafovi

scanf("%d",&k);

} while(v<1 || v>n || k<1 || k>n);

/* Reagujemo na akciju */

switch(akcija)

{ case ’o’:

otvori_polje(v-1,k-1);

break;

case ’z’:

postavi_zastavicu(v-1,k-1);

}

}

/* Konstatujemo pobedu */

ispisi_stanje();

printf ("Cestitam! Pobedili ste\n");

obrisi(stanje,n);

obrisi(bombe,n);

}

225