Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




Tabel de dispersie. Tipul abstract dictionar.

Informatica


3.Tabel de dispersie. Tipul abstract dictionar.

Multimi realizate prin tabele de dispersie

Structura de date care permite cel mai bun timp de cãutare într-o multime neordonatã este un tabel de dispersie ("Hash Table"). In expresia "tabel de dispersie", cuvântul "tabel" este sinonim cu "vector". Principala diferentã fatã de un vector obisnuit este modul în care se determinã pozitia (adresa) unde se introduce un nou element. O valoare nouã nu se adaugã în prima pozitie liberã ci într-o pozitie care sã permitã regãsirea rapidã a acestei valori (fãrã o cãutare prin vector).



Ideea este de a calcula pozitia în vector în functie de valoarea care trebuie introdusã. Acelasi calcul se face atât la introducere în vector cât si la regãsire.

Tabelul de dispersie este folosit pentru implementarea a douã tipuri abstracte de date: multime de valori si dictionar de perechi cheie-valoare.

In principiu procedura de calcul are douã pãrti:

- Reducerea cheii (elementului) la o valoare numericã (dacã nu este deja un numãr întreg pozitiv);

- Transformarea numãrului obtinut într-un indice corect pentru vectorul respectiv, de exemplu prin calculul restului împãrtirii prin lungimea vectorului.

Procedura de calcul a indicelui din cheie se numeste si metodã de dispersie, deoarece trebuie sã asigure dispersia cât mai uniformã a cheilor pe vectorul alocat pentru memorarea lor.

Nu existã o metodã generalã de dispersie si ea trebuie aleasã de utiliz 20520c26u ator, dupã o analizã a valorilor cheilor si a rezultatelor aplicãrii metodelor propuse la cheile din fiecare aplicatie. Orice metode de dispersie conduce inevitabil la aparitia de "sinonime", adicã chei diferite pentru care rezultã aceeasi pozitie în vector. Se mai foloseste si cuvântul "coliziune" pentru situatia când mai multe chei ar trebui plasate la aceeasi adresã în vector, ceea ce este imposibil.

Pentru a exemplifica sã considerãm cã trebuie sã memorãm într-un vector de 6 elemente urmãtoarele siruri de câte 2 caractere (sau adresele acestor siruri): AL, AS, BO, DT, PA, PC. Adunând codurile celor 2 litere si retinând ultimii 4 biti obtinem numerele întregi :13, 20, 17, 24, 17, 19. Resturile împãrtirii prin 6 ale acestor numere conduc la indicii: 1,2,5,0,5,1. Dupã plasarea primelor 4 chei, în pozitiile 1,2,5,0 rãmân libere pozitiile 3,4 si vor apare 2 coliziuni. Se observã cã este importantã si ordinea de introducere a cheilor într-un tabel de dispersie.

S-au propus mai multe metode de redistribuire a sinonimelor (de rezolvare a coliziunilor):

- Metode care calculeazã o nouã adresã în acelasi vector pentru sinonimele ce gãsesc ocupatã pozitia rezultatã din calcul : fie se cautã prima pozitie liberã, fie se aplicã o a doua metodã de dispersie pentru coliziuni, fie altã solutie.

- Metode care plaseazã coliziunile în afara vectorului principal: fie într-un vector auxiliar, fie în liste înlãntuite de sinonime care pleacã din pozitia rezultatã din calcul pentru fiecare grup de sinonime. Un vector de pointeri la liste de sinonime este o solutie destul de generalã si comodã pentru utilizator (care nu trebuie sã ia o hotãrâre de fiecare datã).

Pentru exemplul anterior structura de tabel hash cu 6 liste de sinonime aratã astfel:

0 1 2 3 4 5

DT AL , PC AS - - BO,PA

In general, dupã introducerea în tabel, se pierde si ordinea de introducere, iar afisarea în ordine a datelor din tabel nu este posibilã decât prin crearea unui alt vector ordonat.

In literaturã se discutã diverse variante ale tabelelor de dispersie care sã reducã timpul de cãutare: segmentarea tabelei si calculul unei adrese de segment ('Bucket'), cu plasare si cãutare secventialã în fiecare segment, plasarea sinonimelor în functie de frecventa de cãutare, s.a.

Functiile care urmeazã aratã cum pot fi realizate operatiile elementare cu o multime de cuvinte realizatã ca tabel de dispersie , folosind un vector de pointeri la liste înlãntuite de sinonime.

#define H 11  // dimens. tabel hash

typedef struct snod * pnod, *pos;

typedef pnod Set[H];

// functie de dispersie

int hashno ( char * s)

// initializare multime

void initS (Set h)

// afisare dictionar

void printS (Set h)

}

// cautare cuvânt în multime

int containsS (Set h, char * c)

// adauga cuvint la multime

void addS ( Set h, char * c)

Functiile anterioare pot fi generalizate pentru o multime de pointeri la orice tip de date.

Redimensionarea vectorului de pointeri necesitã re-crearea tabelului de dispersie, deoarece se modificã numãrul si continutul listelor de sinonime. Aceastã decizie poate fi luatã atunci când lungimea listelor si timpul de cãutare secventialã în aceste liste devine mare. Avantajul unui tabel de dispersie cu liste fatã de o singurã listã înlãntuitã este acela cã înlocuieste cãutarea într-o singurã listã mare cu cãutarea într-una din listele mai mici, selectatã direct pe baza valorii cãutate.

Tipul abstract "Dictionar"

Un dictionar sau o asociere ( "Map") este o colectie de perechi cheie - valoare, în care cheile sunt distincte si sunt folosite pentru regãsirea rapidã a valorilor asociate. Un dictionar poate fi realizat ca un singur vector, ca listã înlãtuitã de perechi sau prin doi vectori, dacã performantele nu sunt importante. Cele mai folosite implementãri ale tipului "dictionar" sunt ca tabel de dispersie ("hash") sau ca arbore binar echilibrat de cãutare, atunci când performantele la cãutare sunt importante (se fac cãutãri frecvente). Cheia poate fi de orice tip.

De observat cã în liste sau în vectori de perechi se poate face cãutare dupã diverse chei dar în tabele de dispersie si în arbori aceastã cãutare este posibilã numai dupã o singurã cheie, stabilitã la crearea structurii si care nu se mai poate modifica sau înlocui cu o altã cheie (cu un alt câmp).



Operatiile principale specifice unui dictionar sunt :

- Introducerea unei perechi cheie-valoare într-un dictionar: putD

- Extragerea dintr-un dictionar a valorii asociate unei chei date: getD

- Eliminarea unei perechi cu cheie datã dintr-un dictionar: delD

Rezultatul functiilor este 1 (adevarat) dacã cheia cãutatã este gãsitã sau 0 (fals) dacã cheia nu este gãsitã. Functia "putD" modificã oricum dictionarul, fie prin adãugarea unei noi perechi la dictionar, fie prin modificarea valorii asociate unei chei existente.

Alte operatii posibile cu dictionare sunt : afisarea tuturor perechilor din dictionar (printD),

initializare dictionar (initD), cãutarea unei chei date (containsKey), cãutarea unei valori date (containsValue), creare multime de chei (keySet), creare listã de valori (values) s.a.

Un exemplu de aplicatie a unui dictionar este crearea si afisarea unei liste a cuvintelor distincte extrase dintr-un text, împreunã cu numãrul de aparitii în text al fiecãrui cuvânt. Pentru a face programul mai simplu, se citesc de la consolã cuvinte aflate pe linii separate.

In aceastã problemã cheile sunt cuvintele , iar valorile asociate lor sunt numere întregi.

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

printD(dc); // afisare dictionar

Urmeazã definitiile si functiile pentru operatii cu un dictionar realizat ca un tabel de dispersie :

#define H 11 // dimens. tabel hash

typedef struct snod * pnod, *pos;

typedef pnod Dic[H];  // tip dictionar

// functie de dispersie

int hashno ( char * s)

// initializare dictionar

void initD (Dic dc)

// afisare dictionar

void printD (Dic dc)

}

// cautare cuvânt în dictionar

pos locD (Dic dc, char * c)

// adauga pereche cheie-val la dictionar

void putD ( Dic dc, char * c, int nr) else

// extrage valoarea asociata unei chei date

int getD (Dic dc, char* c)

Functiile "getD" si "putD" implicã o cãutare a unei chei în dictionar, deci o comparatie de valori. Functia de comparare depinde de tipul datelor comparate si ar trebui transmisã ca argument fie celor douã functii, fie functiei de initializare a dictionarului (care memoreazã adresa functiei de comparare, pentru a fi folositã de alte functii). Functia de initializare poate primi ca parametru si dimensiunea vectorului de pointeri, dimensiune care nu se mai modificã ulterior.

Dictionar cu valori multiple

Uneori unei chei i se asociazã o listã (o multime) de valori si nu o singurã valoare. Aceastã variantã de dictionar (numitã uneori "Multimap" = dictionar cu valori multiple) este necesarã în aplicatia "Tabel de referinte încrucisate". O listã de referinte încrucisate, creatã pe baza unui text (care poate fi un program sursã), contine pentru fiecare cuvânt distinct din text numerele liniilor unde apare acel cuvânt. O variantã a acestei probleme este un glosar de termeni (un index) asociat unei cãrti, glosar care contine doar anumite cuvinte, considerate esentiale.

In programul urmãtor am folosit un dictionar realizat din doi vectori : un vector de cuvinte (de pointeri la cuvinte) si un vector de pointeri la liste înlãntuite cu numere de linii:

// dictionar cu valori multiple (vector de liste)

#define M 1000  // nr maxim de cuvinte

// tip nod de lista inlantuita

typedef struct sn Nod;

// tip dictionar

typedef struct dic, *Dic ;

// operatii cu structura dictionar

Dic initD ()

// cauta cuvant in dictionar

Nod * get (Dic d, char *w)

// adauga pereche la dictionar

void addD (Dic d, char * w, int nl)

else



// afisare dictionar

void printD (Dic d)

printf("\n");

}

// creare si afisare lista de referinte încrucisate

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

}

printD (crf); // afisare multime

Dictionarul cu liste de valori asociate unei chei poate fi definit pentru a lucra cu chei si valori de orice tip, dar codul sursã devine greu de urmãrit. De asemenea, putem modifica implementarea tipului dictionar.

Tipul "Colectie de multimi disjuncte"

Unele aplicatii cu grafuri necesitã gruparea elementelor unei multimi în mai multe submultimi disjuncte. Continutul si chiar numãrul multimilor din colectie se modificã de obicei pe parcursul executiei programului. O astfel de aplicatie este determinarea componentelor (subgrafurilor) conexe ale unui graf (un graf este conex dacã existã o cale între orice pereche de noduri din graf).

O altã aplicatie asemãnãtoare este determinarea claselor de echivalentã ce se pot forma cu o serie de numere (sau alte valori), cunoscând perechi de elemente (numere) echivalente.

Ceea ce este specific acestor aplicatii este faptul cã o multime din colectie nu este identificatã printr-un nume sau un numãr, ci printr-un element care apartine multimii. De exemplu, pentru a gãsi o componentã conexã din graf se dã un numãr de nod.

Operatiile asociate tipului abstract "Colectie de multimi disjuncte" ("Disjoint Sets") sunt:

- Crearea unei multimi având ca unic membru o valoare datã x , în colectia 'c': initDS (c, x)

- Gãsirea multimii dintr-o colectie 'c' care contine o valoare datã x: findDS (c,x)

- Reuniunea multimilor din colectia 'c'ce contin valorile x si y : unionDS (c,x,y)

Functia "findDS" are ca rezultat o multime, deci tipul ei depinde de implementarea colectiei.

Algoritmul pentru determinarea componentelor conexe dintr-un graf poate fi exprimat astfel:

repetã ptr. fiecare nod v din graful g

initDS (g,v)

repetã pentru fiecare arc (x,y) din graf

dacã findDS(g,x) != findDS(g,y) atunci

unionDS (g,x,y)

Initial se creazã în colectie câte o multime pentru fiecare nod din graf, iar apoi se reduce treptat numãrul de multimi prin analiza arcelor existente. Dupã epuizarea listei de arce fiecare multime din colectie reprezintã un subgraf conex. Dacã graful dat este conex, atunci în final colectia va contine o singurã multime. Exemplu: Graful neorientat cu 5 noduri si cu arcele (1,3), (3,5), (1,5), (2,4) are douã componente conexe: si .

Programul urmãtor foloseste tipul abstract "DS" si nu depinde de implementarea acestui tip.

void main ()

Cea mai simplã implementare a unei colectii de multimi disjuncte foloseste un singur vector de întregi "m", unde m[x] reprezintã numãrul multimii care contine valoarea "x". Evolutia acestui vector în cazul unei colectii de 6 multimi (un graf cu 6 noduri), dupã operatiile determinate de citirea arcelor 1-3, 4-6, 2-5, 3-6, 2-3 , este ilustratã în secventa urmãtoare:

initial 1 2 3 4 5 6

dupa 1-3 1 2 1 4 5 6

dupã 4-6 1 2 1 4 5 4

dupã 2-5 1 2 1 4 2 4

dupã 3-6 1 2 1 1 2 1

dupã 2-3 2 2 2 2 2 2

O altã implementare a unei colectii de multimi poate folosi un vector de pointeri la liste înlãntuite; fiecare listã este o multime si este identificatã printr-un pointer.




Document Info


Accesari: 5635
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2025 )