nav_dugme codeBlog codeBlog
  • početna Početna stranica
  • Sačuvani članci Sačuvani članci
  • Članci
     (spisak)
  • Kontakt
Povratak na vrh stranice

Info & povezani članci Info o članku - dugme

Info

trejler_sat Datum objave: 25.12.2020.

trejler_olovka Poslednja izmena: 03.03.2022.

trejler_dokument Jezici: JavaScript

trejler_teg_narandzasti Težina: 7/10

JavaScript
algoritam
strukture podataka
nizovi
stek
queue
lista
teorija
tutorijali
saveti

Povezani članci

Strukture podatakaUvod u JavaScript i DOM (Document Object Model)Operacije sa nizovima u programskom jeziku JavaScriptJavaScript ES6 sintaksaUvod u Node.jsŠablonske niske u programskim jezicimaIzbor prvog programskog jezikaCallback funkcije i lambda izraziAsinhrono programiranje u JavaScriptuASCII, UNICODE i UTF - Predstavljanje znakova na računarimaKako napraviti syntax highlighterGNU/Linux - 1. deo - Uvod
Svi članci
There is a big difference between making a simple product and making a product simple.
Des Traynor

Tutorijal - Implementacija struktura podataka u programskom jeziku JavaScript

Facebook LinkedIn Twitter Viber WhatsApp E-mail
zoom_plus zoom_minus bookmark
početna > Članci > Saveti

Uvod

Na prvi pogled, JavaScript ne deluje kao jezik koji je "kao stvoren" za implementiranje čvorova i struktura podataka, što je zadatak za koji bi (u praksi), većina programera najverovatnije izabrala neki od jezika kao što su C, C++, Java ili C# (i svakako preporučujemo čitaocima koji cene svoje obrazovanje u oblasti programiranja da se prvo upoznaju sa procesom implementacije struktura podataka - upravo kroz neki od navedenih jezika).

Ako ste "baš" spremni za izazove, preporučujemo (baš) "običan C". :)

Međutim, ako ste rešili "prvi zadatak", to jest, već imate ličnog iskustva u implementaciji struktura podataka u jezicima sa strogom tipizacijom; imate pri tom želju da unapredite svoju veštinu korišćenja skriptnih jezika i upoznati ste (u opštem smislu), sa strukturama podataka o kojima diskutujemo, nastavljamo dalje ....

Pre svega, jasno je da u skriptnim jezicima sa dinamičkom tipizacijom podataka (kao što su JavaScript i Python) - takođe postoji potreba za složenim strukturama podataka iz "domaće radinosti", i stoga ćemo se u nastavku pozabaviti načinima za implementiranje složenijih struktura podataka u skriptnim jezicima, a usput ćemo verovatno uvideti i to da celo iskustvo takođe može biti vrlo zabavno (nešto nalik izradi montažnih kuća, u poređenju sa klasičnim načinima gradnje). :)

Za mnoge potrebe, postoje gotove strukture podataka, ali, cilj je da mladi programeri nauče, kako se strukture podataka implementiraju "ispod haube" (ne samo kako se koriste gotova rešenja), i stoga će nivo implementacije u ovom članku biti u skladu sa navedenim okvirima: nećemo prikazivati gotova rešenja, već, onoliko koliko je dovoljno da čitaoci, koji su već upoznati sa procesom implementacije struktura podataka u "striktnijim" jezicima, * samostalno "dovrše posao" implementacije struktura kojima ćemo se baviti u nastavku ovog teksta.

* Na našim stranicama već ste mogli videti primere u C++-u i Javi).

Osnovna struktura čvora

Za primere u članku (koji između ostalog demonstriraju i upotrebu klasa u JavaScript-u), implementiraćemo čvorove kakvi se mogu koristiti u binarnim stablima pretrage, ali (uz manje izmene), i u stekovima, redovima za čekanje i (naravno) mnogim drugim strukturama podataka).

Osnovna klasa koja definiše čvor može se implementirati na sledeći način:

		
class Cvor {

	constructor (vrednost) {
		this.vrednost = vrednost;
		this.levi     = null;
		this.desni    = null;
	}
}
		
	
Slika 1. - Klasa Cvor, koja definiše čvor binarnog stabla pretrage, steka, reda za čekanje ili neke druge strukture podataka.

Vidimo da polja vrednost, levi i desni nemaju eksplicitno definisan tip (u 'tipičnom JS maniru'), i takođe vidimo da se konstruktor ne definiše kao u drugim programskim jezicima (koje smo prethodno koristili), ali, sa druge strane, upotreba rezervisane reči constructor, verovatno ne ostavlja mesta sumnji po pitanju toga o kakvoj se metodi radi. :)

Sada se može pozvati sledeći kod:

		
C1 = new Cvor(12);
C2 = new Cvor(8);
C3 = new Cvor(18);

C2.levi  = C1;
C2.desni = C3;
		
	
Slika 2. - Kreiranje objekata klase Cvor (praktično je u pitanju "mini-stablo" sa tri elementa).

.... i svi čvorovi će biti uredno definisani (praktično: čvorovi C1 i C3 postaju "potomci" čvora C2 i "listovi" stabla).

Međutim, preko primera u ranijim člancima pokazali smo da je u jeziku JavaScript moguće napisati (a takođe i pozvati), i naredbe dodele nalik sledećim naredbama ....

		
C1.levi  = 12;
C1.desni = "Obedska bara";
		
	
Slika 3. - Neadekvatna upotreba polja klase Cvor (pošto JavaScript dozvoljava ovakve dodele, moramo sami voditi računa o tome kakve vrednosti zadajemo objektima).

.... i takva primedba sasvim je na mestu.

U opštem smislu, "slobodna" interpretacija tipova podataka svojstvena programskom jeziku JavaScript, može delovati problematično, ali (kao što smo već nagovestili) - pod uslovom da sami pazimo da ne dođe do naredbi dodele kakve smo videli na slici #3 - stvari u praksi ipak funkcionišu sasvim dobro.

Nije, ni teško, ni "zazorno", samostalno voditi računa o implementaciji kakvu smo videli, jer ("na kraju krajeva"), programe pišemo "mi" (ljudi/programeri), to jest - programi (ionako) ne pišu sami sebe (uglavnom je još uvek tako; ako ovaj članak budete čitali 2038, situacija može biti drugačija). :)

Takođe, "na osnovnom nivou zdravog razuma", možemo primetiti da u Javi ili C#-u (ili nekom drugom jeziku sa strogom tipizacijom), takođe ne bismo pokušavali da upišemo int vrednosti ili niske, u polja koja su deklarisana kao reference na čvorove - pa onda nema potrebe da takve stvari radimo u JS-u. :)

Implementacija steka

U prethodnom članku o nizovima u JS-u prikazali smo (preko primera), način upotrebe metoda push i pop: prva metoda dodaje novi element u niz (na kraj niza), a druga vraća referencu na poslednji (dodati) element i uklanja element iz niza, što u potpunosti odgovara principu LIFO (last in - first out), po kome stek funkcioniše.

Ako "zažmurimo" - i "pravimo se da ne znamo" da je moguće u svakom trenutku pristupiti proizvoljnom elementu niza (što svakako nije odlika tipičnog steka) - shvatićemo da na raspolaganju (praktično) imamo sasvim funkcionalan stek:

		
STEK = [];
STEK.push(C1);  // STEK - [C1]
STEK.push(C2);  // STEK - [C1, C2]
STEK.push(C3);  // STEK - [C1, C2, C3]

vrhSteka = STEK.pop();           // STEK - [C1, C2] 
console.log(vrhSteka.vrednost);  // 18

vrhSteka = STEK.pop();           // STEK - [C1] 
console.log(vrhSteka.vrednost);  // 8

vrhSteka = STEK.pop();           // STEK - [] 
console.log(vrhSteka.vrednost);  // 12
		
	
Slika 4. - Implementacija osnovne funkcionalnosti steka u JavaScript-u.

I na ovom mestu (kao i do sada), vidimo "liberalni JS pristup" koji ne zahteva preciznu deklaraciju steka (koji će "nositi" objekte određene klase), ali, u praktičnom smislu, vidimo takođe i programski kod koji je veoma nalik na programski kod iz nekog od 'kompajliranih' jezika, koji od programera zahtevaju da sami precizno deklarišu tipove podataka.

Praktičan savet je (ponovo): potrebno je da pazimo šta radimo i ne treba da izvršavamo naredbe koje ni inače (preko drugih jezika), ne bismo izvršavali nad stekovima.

U nekim (drugim) situacijama, JavaScript može biti zaista "konfuzan", ali, u većini svakodnevnih situacija (kao što su situacije koje opisujemo u članku), samo se treba ponašati onako kako bismo se ponašali da koristimo Javu, C, ili C# (ili neki drugi jezik sa strogo definisanim tipovima podataka).

Jednostavno (u slučaju primera koji opisujemo): ne treba da pokušavamo da (nepotrebno) pristupamo unutrašnjim elementima niza koji u programu predstavlja stek (budući da u implementaciji u Javi, C-u, ili C#-u (i sl) - takođe ne bismo pokušavali da pristupimo nekom od unutrašnjih elemenata).

Implementacija reda za čekanje

Za implementaciju reda za čekanje, koji funkcioniše po principu FIFO (first in - first out), ne može se koristiti isti pristup kojim smo se vodili pri implementaciji steka, ali, svakako se možemo "snaći".

Elementi se i ovoga puta mogu dodavati preko metode push, a uklanjanje se može obaviti preko metode shift, koja (da se podsetimo), iz niza (odnosno, u našem slučaju, iz 'reda za čekanje' :)), uklanja prvi element i vraća referencu na uklonjeni element.

		
RED = [];
RED.push(C1);  // RED - [C1]
RED.push(C2);  // RED - [C1, C2]
RED.push(C3);  // RED - [C1, C2, C3]

pocetakReda = RED.shift();          // RED - [C2, C3] 
console.log(pocetakReda.vrednost);  // 12

pocetakReda = RED.shift();          // RED - [C3] 
console.log(pocetakReda.vrednost);  // 8

pocetakReda = RED.shift();          // RED - [] 
console.log(pocetakReda.vrednost);  // 18
		
	
Slika 5. - Implementacija osnovne funkcionalnosti reda za čekanje u JavaScript-u.

Da ponovimo još jednom (znate šta sledi): ako ne budemo "nepotrebno čeprkali" po unutrašnjim elementima reda, cela struktura će funkcionisati onako kako očekujemo.

Implementacija stabla pretrage

Za implementaciju stabla pretrage, koristićemo klasu u kojoj je jedno od polja, referenca na koreni čvor.

Pored osnovne implementacije, razmotrićemo takođe (ali - samo u svojstvu zanimljivog fenomena (jer nije u pitanju pristup koji je preporučljivo primenjivati u praksi)), implementaciju koja podrazumeva kreiranje samo jednog (jedinog) objekta klase Cvor:

		
const KOREN = new Cvor(10);
		
	
Slika 6. - Implementacija binarnog stabla može se izvesti tako što ćemo kreirati (samo) referencu na koreni čvor (ali - u praksi to ipak nećemo raditi).

Pod uslovom da se promenljiva KOREN ne 'odveže' od korenog čvora, uvek će postojati način da se pristupi korenom čvoru stabla - što nadalje omogućava da se i ostali čvorovi dodaju, referenciraju, uklanjaju i sl.

Da se podsetimo: iako promenljiva nosi naziv KOREN, praktično je u pitanju referenca na (celo) stablo, što omogućava izgradnju stab(a)la proizvoljne visine/dubine/strukture, i upravo je referenca na koreni čvor, nešto što se predaje metodama za dodavanje, uklanjanje i pronalaženje čvorova (kao što ste već mogli videti u člancima o implementaciji AVL stabla u Javi).

Međutim, u praksi (kao što smo takođe prethodno naveli, i kao što smo već videli u prethodnim primerima implementacije struktura podataka u drugim jezicima), koreni čvor će biti definisan kao zasebna referenca (tj. polje) - unutar klase (koja tipično obuhvata i mnoga pomoćna polja i (naravno) metode):

		
class Stablo {
	
	constructor(vrednost) {
		this.Koren  = new Cvor(vrednost);
		this.Brojac = 0;
	}
}
		
	
Slika 7. - Klasa Stablo koja sadrži referencu na koreni čvor i polje Brojac, preko koga se može "očitati" na kojoj "dubini" se nalazi traženi element stabla.

I dalje koristimo polje Brojac koje smo koristili i u članku o implementaciji AVL stabla u programskom jeziku Java (takvo polje dobro dođe pri upoznavanju sa strukturom stabla i pokazuje da li se pretraga obavlja pravilno, po algoritmu složenosti O(logn)).

Kao i "inače", metode se mogu implementirati unutar klasa (prikazaćemo samo primer uprošćene metode za dodavanja čvorova):

		
class Stablo {
	
	....

	function dodavanje(cvor, vrednost) {
		if (cvor == null) {
			return new Cvor(vrednost);
		}

		if (vrednost < cvor.vrednost) {
			cvor.levi = dodavanje(cvor.levi, vrednost);
		}
		else {
			if (vrednost > cvor.vrednost) {
				cvor.desni = dodavanje(cvor.desni, vrednost);
			}
			else {
				alert("Vrednost već postoji u stablu!");
				return cvor;
			}
		}

		return cvor;
	}
}
		
	
Slika 8. - Metoda za dodavanje novog čvora (zapisana u okviru klase Stablo).

Metoda dodavanje poziva se na sledeći način:

		
let stablo = new Stablo(12);
Stablo.dodavanje(Stablo.Koren, 10);
		
	
Slika 9. - Pozivanje metode "dodavanje".

Ako pogledamo metodu dodavanje, jasno je da su delovi koji se tiču balansiranja čvorova izostavljeni (a očigledno fale i metode za pretragu, uklanjanje čvorova i sl).

Implementaciju navedenih (preostalih) metoda, ostavljamo čitaocima - za vežbu. :)

Za kraj ....

Za kraj ostaje da vas još jednom pozovemo da dovršite samostalno neku od započetih implementacija iz članka (ukoliko imate dovoljno slobodnog vremena), a takođe želimo da se osvrnemo, još jednom, na opšti pristup koji smo preporučili na početku.

Pod uslovom da već imate prethodnog iskustva sa implementiranjem struktura podataka (po mogućnosti, "baš" u C-u, ali, i ostali jezici koje smo naveli na početku poslužiće sasvim dobro), proces implementacije struktura podataka u JavaScript-u, neće biti težak.

Naprotiv, biće (gotovo je sigurno da je tako) - znatno lakši.

I upravo je u tome stvar (kao što smo jednom prilikom već naveli): ukoliko se na početku usmerite na teže jezike i teže primere, i uspete da dobro savladate tehnike (doduše, nemojte baš ni preterivati, pogotovo ako ste veoma mlad polaznik), upoznavanje sa jezicima koji koriste ponešto pojednostavljenu sintaksu (npr. JS i Python), možda se neće desiti "samo od sebe", ali, biće mnogo (mnogo!) lakše nego kada biste primenili 'obrnuti pristup' (što nikako ne preporučujemo).

U svakom slučaju, programiranje se najbolje uči - pisanjem programa. :)

Autor članka Nikola Vukićević Za web portal codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook LinkedIn Twitter Viber WhatsApp E-mail
početna > Članci > Tutorijal - Implementacija struktura podataka u programskom jeziku JavaScript
codeBlog codeBlog
Sajt posvećen popularizaciji kulture i veštine programiranja.
Napomena: Tekstovi i slike na sajtu codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt