4. Nizovi

Podeli svoje utiske o poglavlju na anketi OVDE.

Preduslovi za rad:

Potrebno je osnovno znanje iz naredbi selekcije i ciklusa.

Do sad je bio uveden koncept promenljive, koja ima jedinstveno ime, sadrži jednu vrednost i određene je veličine. Veličina i vrednost zavise od tipa promenljive. Nizovi predstavljaju više uzastopnih lokacija u memoriji, gde svaka poput promenljive ima svoju vrednost i sve su iste veličine. Ukoliko se upotrebi analogija gde promenljiva predstavlja jednu "kućicu" u memoriji, niz je čitava ulica tipskih kuća (sve liče jedna na drugu). To je zato što nizovi, kao i promenljive, imaju svoj tip i svi članovi niza su tog tipa. Članovi se nazivaju i elementima niza.

Primer deklaracije niza:

int niz[10];

Ova deklaracija napraviće niz od 10 celobrojnih elemenata.

Njima nije moguće pristupiti odjednom, već pojedinačnim elementima:

niz[0] = 5;
int a = niz[2];

Napomena:

Indeksiranje nizova u programskom jeziku C kreće od 0! Za niz od 10 elemenata, validni indeksi su od 0 do 9!

4.1. Osnovni primeri rada sa nizovima

Tokom rada sa nizovima moguće je raditi isključivo sa njegovim pojedinačnim elementima, ne sa celim nizom odjednom. Da bi se obradio svaki element niza, potrebno je koristiti for petlju.

Pretpostavimo da imamo deklaraciju promenljive i i niza celobojnih vrednosti niz na sledeći način:

1    int i, niz[5];

Primer upisa vrednosti u niz sa standardnog ulaza:

1    for(i = 0;i < 5;i++)
2    {
3        printf("niz[%d]: ", i);
4        scanf("%d", &niz[i]);
5    }

Primer ispisa vrednosti iz niza na standardni izlaz:

1    for(i = 0;i < 5;i++)
2    {
3        printf("%d ", niz[i]);
4    }

Na primerima se može videti da se promenljiva i koristi u for petlji, njena početna vrednost je 0, a krajnja veličina niza minus 1. Za primer iznad to će biti vrednost 4. U jednoj iteraciji for petlje, određeni element niza biće učitan, odnosno ispisan.

4.2. Korišćenje određenog dela niza

Kod deklaracije niza je njegova veličina navedena unapred, pre kompajliranja. Često se u praksi dešava situacija da postoje slučajevi korišćenja kad je potrebno popuniti samo deo niza. Sa druge strane, dimenzija niza mora podržati slučaj koji traži najviše iskorišćenih elemenata.

Ovakva fleksibilnost postiže se uvođenjem nove promenljive n, koja će označavati broj upotrebljenih elemenata:

 1#include <stdio.h>
 2
 3#define MAX_SIZE 30
 4
 5int main()
 6{
 7    int i, n, niz[MAX_SIZE];
 8
 9    do
10    {
11        printf("Unesite broj elemenata niza: ");
12        scanf("%d", &n);
13    } while(n <= 0 || n > MAX_SIZE);
14    
15
16    for(i = 0;i < n;i++)
17    {
18        printf("niz[%d]: ", i);
19        scanf("%d", &niz[i]);
20    }
21
22    for(i = 0;i < n;i++)
23    {
24        printf("%d ", niz[i]);
25    }
26
27    return 0;
28}

Na početku programa unosi se vrednost n. Obavezno je da bude u granicama od 1 do MAX_SIZE, kako bi broj korišćenih elemenata bio unutar granica niza.

Primer rada programa:

Unesite broj elemenata niza: 31
Unesite broj elemenata niza: -5
Unesite broj elemenata niza: 5
niz[0]: 1
niz[1]: 2
niz[2]: 3
niz[3]: 4
niz[4]: 5
1 2 3 4 5 

Prve dve vrednosti koje su unete su izvan dimenzija niza, tako da će one biti odbijene. Od korisnika će se tražiti da unosi vrednosti sve dok ne zada neku koja se nalazi u granicama niza (ovo obezbeđuje do-while petlja u programu).

Napomena:

MAX_SIZE je definisano putem #define pretprocesorske direktive.
Ona će pre samog kompajliranja zameniti svuda gde se nalazi MAX_SIZE sa vrednošću koja joj je pridružena.
Funkcionalnost je slična kao kad se kod tekst editora izabere opcija "Find and replace".
Moguće je videti kod nakon pretprocesiranja pomoću sledeće komande: gcc -E kod.c

Primer koji traži srednju vrednost unetih elemenata niza:

 1#include <stdio.h>
 2
 3#define MAX_SIZE 30
 4
 5int main()
 6{
 7    int i, n, niz[MAX_SIZE];
 8
 9    do
10    {
11        printf("Unesite broj elemenata niza: ");
12        scanf("%d", &n);
13    } while(n <= 0 || n > MAX_SIZE);
14    
15
16    for(i = 0;i < n;i++)
17    {
18        printf("niz[%d]: ", i);
19        scanf("%d", &niz[i]);
20    }
21
22    int suma = 0;
23    for(i = 0;i < n;i++)
24    {
25        suma += niz[i];    // isto sto i "suma = suma + niz[i];"
26    }
27
28    printf("Srednja vrednost vrednosti iz niza je: %.2lf\n", (double)suma / n);
29
30    return 0;
31}

Ispis programa:

Unesite broj elemenata niza: 5
niz[0]: 3
niz[1]: 6
niz[2]: 4
niz[3]: 7
niz[4]: 2
Srednja vrednost vrednosti iz niza je: 4.40

4.3. Traženje minimalnog/maksimalnog elementa u nizu

Često je neophodno pronaći vrednost najmanjeg, odnosno, najvećeg člana u nizu. Kako je prethodno navedeno, u programskom jeziku C nije moguće odjednom pristupiti svim članovima i pronaći među njima najveći/najmanji element.

Algoritam je sledeći:

  1. Pretpostaviti da je neki od članova niza njegov najveći/najmanji element, tako što se njegova vrednost sačuva u posebnu promenljivu (uglavnom se bira vrednost prvog člana niza)

  2. Izabrati naredni član niza (uglavnom se ide redom, drugi, treći, četvrti itd.)

  3. Uporediti ga sa vrednošću promenljive koja trenutno glasi kao najveća/najmanja

  4. Ukoliko je trenutni član niza veći/manji od vrednosti iz prethodnog koraka, postaviti vrednost člana niza kao novu najveću/najmanju vrednost

  5. Vratiti se na korak 2. i ponoviti postupak dokle god ima neobrađenih članova niza

Izvršavanje navedenih koraka garantuje da će se nakon prolaska kroz čitav niz, odnosno, obradu svih njegovih članova u promenljivoj koja je namenjena za to naći vrednost najvećeg/najmanjeg elementa. Ovaj algoritam primenljiv je na nizove čiji elementi nisu prethodno uređeni po vrednosti (u rastućem ili opadajućem redosledu).

Primer koji traži maksimalnu vrednost u nizu:

 1#include <stdio.h>
 2
 3#define MAX_SIZE 30
 4
 5int main()
 6{
 7    int i, n, niz[MAX_SIZE];
 8
 9    do
10    {
11        printf("Unesite broj elemenata niza: ");
12        scanf("%d", &n);
13    } while(n <= 0 || n > MAX_SIZE);
14    
15
16    for(i = 0;i < n;i++)
17    {
18        printf("niz[%d]: ", i);
19        scanf("%d", &niz[i]);
20    }
21
22    int maks = niz[0];
23
24    for(i = 1;i < n;i++)
25    {
26        if(niz[i] > maks)
27        {
28            maks = niz[i];
29        }
30    }
31
32    printf("Maksimalna vrednost niza je: %d\n", maks);
33
34    return 0;
35}

Ispis programa:

Unesite broj elemenata niza: 5
niz[0]: 4
niz[1]: 2
niz[2]: 7
niz[3]: 1
niz[4]: 3
Maksimalna vrednost niza je: 7

Za traženje minimalne vrednosti u nizu potrebno je obrnuti samo smer relacionog operatora u if naredbi.

4.4. Sortiranje nizova

Sortiranje niza predstavlja uređenje njegovih elemenata tako da oni budu u rastućem ili opadajućem redosledu. Postoji mnoštvo algoritama za sortiranje niza. Ovde će biti prikazana dva, Bubble Sort i Selection Sort. Kod za učitavanje elemenata niza i njihov ispis je izostavljen radi akcenta na samim algoritmima.

Bubble Sort algoritam:

 1    int i, n, niz[MAX_SIZE];
 2    int nema_promena, j, tmp;
 3
 4    for(i = 0;i < n - 1;i++)
 5    {
 6        nema_promena = 1;   // pretpostavimo da nece biti promena
 7        for(j = 0;j < n - i - 1;j++)
 8        {
 9            if(niz[j] > niz[j + 1])
10            {
11                tmp = niz[j];
12                niz[j] = niz[j + 1];
13                niz[j + 1] = tmp;
14                nema_promena = 0;   // desila se promena, nema_promena postaje netacno
15            }
16        }
17
18        if(nema_promena)
19        {
20            break;
21        }
22    }

Kod ovog algoritma najveći/najmanji element "isplivava" na kraj niza nakon jednog prolaska kroz ceo niz (for petlja po j). To se dešava tako što se susedni elementi porede i ako se ispostavi da je prvi veći/manji od drugog, izvršiće se njihova zamena. Čitav postupak ponavlja se n - 1 puta i svaki put je for petlja po j sve kraća, zbog toga što se sa rastom i desilo "isplivavanje" elemenata na njihove pozicije. Bubble Sort je vrlo neefikasan algoritam za sortiranje niza i koristi se isključivo u svrhe učenja zbog svoje jednostavnosti.

Selection Sort algoritam:

 1    int i, n, niz[MAX_SIZE];
 2    int indeks_najmanjeg, j, tmp;
 3
 4    for(i = 0;i < n - 1;i++)
 5    {
 6        indeks_najmanjeg = i;
 7
 8        for(j = i + 1;j < n;j++)
 9        {
10            if(niz[indeks_najmanjeg] > niz[j])
11            {
12                indeks_najmanjeg = j;
13            }
14        }
15
16        if(indeks_najmanjeg != i)
17        {
18            tmp = niz[i];
19            niz[i] = niz[indeks_najmanjeg];
20            niz[indeks_najmanjeg] = tmp;
21        }
22    }

Za razliku od Bubble Sort algoritma, Selection Sort traži odgovarajući element desno od i-tog člana. Ukoliko nađe najveći/najmanji, pristupa se zameni i-tog i najvećeg/najmanjeg. Ako se, pak, ispostavi da ne postoji veći/manji od i-tog, zamena elemenata se neće dogoditi.

Napomena:

U prethodnim primerima algoritama za sortiranje koristi se "petlja u petlji".
Ovaj koncept naziva se "ugneždena petlja" i funkcioniše na način da će se za jednu iteraciju spoljne petlje 
(u primerima po brojačkoj promenljivoj i), unutrašnja petlja izvršiti do n puta (u primerima po brojačkoj promenljivoj j).
Primer korišćenja ugneždene petlje je, recimo, za traženje duplikata u nizovima.

Ukratko rečeno, unutrašnja petlja će izvršiti sve svoje iteracije za jednu iteraciju spoljašnje.

Napomena:

Da bi dva elementa niza zamenili mesta, potrebno je uvesti treću, pomoćnu promenljivu.
Analogija ovome je da, recimo, imate tečnost u dve čaše i želite da prebacite tečnost iz jedne u drugu.
Ovo nije moguće bez korišćenja treće, prazne čaše, u koju ćete, recimo, usuti tečnost iz prve,
zatim prebaciti tečnost iz druge u prvu, koja je pre toga bila prazna, a tečnost iz treće čaše presućete u drugu.

Promenljiva tmp u primerima izigrava treću čašu.