Documente Academic
Documente Profesional
Documente Cultură
S.Pereteatcu, A.Pereteatcu
Pentru a demonstra lucrul cu tabele neordonate structurate arborescent mai întâi de toate creăm în
baza clasei usual_elem clasa tree_like la care mai adaugăm două câmpuri cu caracter de
pointeri.
//
// c l a s s "t r e e l i k e" e l e m e n t s
//
class tree_like: public usual_elem
{
protected:
int less;
int greater;
public:
tree_like()
{
less=greater=-1;
}
tree_like(char* init_name, int init_year, double init_salary):
usual_elem(init_name, init_year, init_salary)
{
less=greater=-1;
}
int get_less()
{
return less;
}
20
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
int get_greater()
21
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
{
return greater;
}
Clasa tree_table declarăm ca clasa generică derivată de la clasa generică cunoscută table.
//
// C l a s s "t r e e _ t a b l e"
//
template <class el> class tree_table: public table<el>
{
public:
tree_table<el>(int NMAX=200): table<el>(NMAX)
{
}
22
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
23
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
getch();
}
int search(el e)
{
int position = -1;
int forward=1;
int i=0;
int cmp_result;
while(forward)
if(SD::ncomp++, (cmp_result=e.cmp(t[i]))==0)
position=i, forward=0;
else
{
if(cmp_result<0)
i=t[i].get_less();
else
i=t[i].get_greater();
if(i==-1)
forward=0;
}
return position;
}
};
Căutarea în tabelul arborescent după cheia dată are loc asemănător cu căutarea locului, în care se
înscrie adresa de păstrare.
Lungimea medie de căutare în tabelul arborescent depinde de ordinea înscrierii înregistrărilor la
încărcarea tabelului. În cel mai rău caz, când înregistrările veneau în ordine crescătoare (sau
descrescătoare) a cheilor, arborele va avea numai o ramură, şi lungimea medie de căutare rămâne
egală cu n/2, ca şi în cazul tabelelor neordonate. În cel mai bun caz, când ordinea înscrierii
înregistrărilor este aşa, că se primeşte un arbore binar simetric, lungimea căutării se micşorează
până la D2=[log2n+2].
În funcţia main() demonstrăm crearea şi utilizarea tabelelor nerdonate structurate arborescent.
void main()
{
clrscr();
tree_table<tree_like> tree_gr("stud.txt");
tree_gr.show("Group:\n", "");
24
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
tree_gr.tree_show("Group:\n", "");
char ch='n';
char surname[21];
while(ch!='y')
{
printf("Enter a name to search: ");
scanf("%s", surname);
tree_like e(surname, 2000, 0.0);
tree_gr.reset_ncomp();
int pos=tree_gr.search(e);
if(pos<0)
{
printf("No table! ");
printf("The number of comparisons: %d\n", tree_gr.get_ncomp());
}
else
{
printf("There are in the position %d. ", pos+1);
printf("The number of comparisons: %d\n", tree_gr.get_ncomp());
}
printf("Done ? (y/n) ");
ch = getch();
printf("\n");
}
}
Group:
1. Green 1987 350.00
2. Red 1980 450.00
3. Blue 1981 500.00
4. Gray 1968 900.00
5. Orange 1984 550.00
6. White 1980 600.00
7. Cyan 1975 800.00
8. Yellow 1988 300.00
9. Magenta 1983 600.00
10. Black 1981 500.00
End of Table. Press any key ...
S-a afişat tabelul simplu neordonat. După ce vom apăsa orice tastă, ecranul va fi şters şi va apărea
afişarea următoarea:
25
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
Group:
1. Green 1987 350.00 [ 2, 1]
2. Red 1980 450.00 [ 4, 5]
3. Blue 1981 500.00 [ 9, 3]
4. Gray 1968 900.00 [ 6, -1]
5. Orange 1984 550.00 [ 8, -1]
6. White 1980 600.00 [ -1, 7]
7. Cyan 1975 800.00 [ -1, -1]
8. Yellow 1988 300.00 [ -1, -1]
9. Magenta 1983 600.00 [ -1, -1]
10. Black 1981 500.00 [ -1, -1]
End of Table. Press any key ...
Enter a name to search: White
There are in the position 6. The number of comparisons: 3
Done ? (y/n)
Enter a name to search: Green
There are in the position 1. The number of comparisons: 1
Done ? (y/n)
Enter a name to search: Black
There are in the position 10. The number of comparisons: 3
Done ? (y/n)
Enter a name to search: Purple
No table! The number of comparisons: 3
Done ? (y/n)
Dacă vrem să calculăm lungimea medie de căutare pentru acest tabel, scriem funcţia main() astfel:
void main()
{
clrscr();
tree_table<tree_like> gr("stud.txt");
gr.tree_show("Group:\n","");
tree_like sample;
long NCOMP=0;
26
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
NCOMP+=gr.get_ncomp();
}
fclose(pf);
printf("N=%d, NCOMP=%d, ALS=%.2lf", gr.get_n(), NCOMP,
(double)NCOMP/gr.get_n());
printf(", MAX=%.2lf, MIN=%.2lf\n", (gr.get_n()+1)/2.0,
log((double)gr.get_n())/log(2.0)+2.0);
getch();
}
27
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
28
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
t[i+1]=tmp, n++;
}
fclose(SD::pf) , SD::pf=NULL;
}
// Cautarea binara
int search(el e)
{
int a=0, b=n-1;
int result;
while(a<b)
{
int i=(a+b)/2;
if(SD::ncomp++, (result=e.cmp(t[i]))>0)
a=i+1;
else
if(result<0)
b=i;
else
a=b=i;
}
return (SD::ncomp++, e==t[a])? a : -1;
}
int f1=fib(j-2);
int mid=n-f1+1;
int f2=fib(j-3);
int foward=1;
29
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
else
{
mid+=f2;
f1-=f2;
f2-=f1;
}
else
if(f2==0)
foward=0;
else
{
mid-=f2;
int t=f1-f2;
f1=f2;
f2=t;
}
protected:
int fib(int n)
{
int res;
if(n==0 || n==1)
res=n;
else
if(n>=2)
res=fib(n-1)+fib(n-2);
return res;
}
};
sorted_table<usual_elem> sorted_gr("stud.txt");
sorted_gr.show("Group:\n", "");
char ch='n';
30
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
char surname[21];
while(ch!='y')
{
printf("Enter a name to search: ");
scanf("%s", surname);
usual_elem e(surname, 2000, 0.0);
sorted_gr.reset_ncomp();
int pos=sorted_gr.search(e);
if(pos<0)
{
printf("No table! ");
printf("The number of comparisons: %d\n", sorted_gr.get_ncomp());
}
else
{
printf("There are in the position %d. ", pos+1);
printf("The number of comparisons: %d\n", sorted_gr.get_ncomp());
}
printf("Done ? (y/n) ");
ch = getch();
printf("\n");
}
}
31
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
Pentru a demonstra căutarea prin metoda lui Fibonacci înlocuim în funcţia precedentă rândul
int pos=sorted_gr.search(e);
cu
int pos=sorted_gr.fibsearch(e);
32
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
ce pentru n destul de mare aproape că este egal cu limita de jos teoretică pentru metode de căutare,
bazate numai pe compararea cheilor. Teoretic limita de jos este egală cu log2(n+1). Căutarea binară
mult mai efectivă decât parcurgerea secvenţială. Pentru n=1000, D1=500, iar D2=11.
Pentru a calcula lungimea medie practică de căutare binară în tabelul ordonat, creat în baza
fişierului "stud.txt", modificăm funcţia main():
void main()
{
clrscr();
sorted_table<usual_elem> gr("stud.txt");
gr.show("Group:\n","");
usual_elem sample;
long NCOMP=0;
33
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
fclose(pf);
printf("N=%d, NCOMP=%d, ALS=%.2lf", gr.get_n(), NCOMP,
(double)NCOMP/gr.get_n());
printf(", ALS_TEOR=%.2lf\n", log((double)gr.get_n())/log(2.0)+2.0);
getch();
}
Pentru a calcula lungimea medie practică de căutare prin metoda lui Fibonacci în tabelul ordonat,
creat în baza fişierului "stud.txt", înlocuim în funcţia main() precedentă rândul
if(gr.search(sample)>=0)
cu
if(gr.fibsearch(sample)>=0)
34
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
Lungimea medie a parcurgerii secvenţiale în tabel, ordonat după frecvenţa apelărilor la înregistrări,
esenţial depinde de repartizarea frecvenţilor apelurilor şi se obţine după formula generală
n
D i pi . Dacă un număr relativ mic de înregistrări se caută foarte des, atunci lungimea medie
i 1
de căutare poate fi mult mai mică decât la căutare binară. Aceasta uneori se foloseşte la crearea
tabelelor ale translatorului, de exemplu tabelul de cuvinte cheie ale limbajului de intrare.
Ordonarea tabelelor cere adăugător cheltuieli de timp al calculatorului. Deaceea tabelele ordonate se
folosesc mai mult ca tabele constante ale translatorului. Dar uneori se ordonează şi tabele
temporare, cu toate că ordonarea aceasta are anumite greutăţi. Problema constă în aceea că tabelele
temporare ce se alcătuiesc în timpul translaţiei în multe cazuri tot aici se folosesc şi pentru căutare.
Deja completate astfel de tabele au nevoie de verificare: oare nu este inclusă înregistrarea dată în
tabel la etapa precedentă de lucru al translatorului. De aceea ordonarea tabelelor temporare este
necesar de făcut odată cu încărcarea lor.
Pentru reducerea cheltuielilor timpului calculatorului la ordonarea tabelelor temporare uneori se
foloseşte metoda împărţirii, la care tabelul se împarte în compartimente, corespunzătoare
intervalelor diferite ale valorilor cheii. Compartimentele sunt ordonate, iar înăuntru
compartimentelor înregistrările nu se ordonează. Pentru căutarea înregistrărilor se foloseşte metoda
combinată. De exemplu, compartimentul se găseşte prin căutare binară, iar înăuntru
compartimentului se foloseşte parcurgerea secvenţială.
Exerciţii.
1. Supraîncărcaţi operatorul de inserţie în clasa sorted_table.
2. Supraîncărcaţi operatorul de extragere în clasa sorted_table.
3. Înlocuiţi în clasa sorted_table funcţia recursivă fib() cu o versiune iterativă.
4. Modificaţi în clasa sorted_table codul funcţiei de căutare prin metoda lui Fibonacci
fibsearch() ca ultima să nu utilizeze funcţia fib().
35
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
36
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
Noţiuni generale
Deoarece, practic transformarea bireciprocă a cheii în adresa păstrării înregistrării, în mod general,
nu poate fi îndeplinită, atunci suntem nevoiţi să ne refuzăm de cerinţă de reflectare birereciprocă.
Aceasta aduce la suprapunerea înregistrărilor sau, altfel zis, la coliziuni. Ca astfel de coliziuni să fie
cât mai puţine, funcţia de repartizare se alege din condiţia de reprezentare aleatorie şi cu cât se
poate mai uniformă de reflectarea cheilor în adresa de păstrare. Tabele construite după acest
principiu sunt numite tabele de repartizare.
Repartizarea nu exclude pe deplin posibilitatea de suprapunere a înregistrărilor (coliziuni). De acea
se aplică diferite metode pentru înlăturarea coliziunilor. Diferenţa variantelor de tabele de
repartizare este definită prin metoda folosită de înlăturare a coliziunilor.
La includerea unei noi înregistrări algoritmul rămâne determinat până atunci, când vectorul, în care
se reflectă tabelul, conţine măcar o poziţie liberă.
Dacă această condiţie nu se îndeplineşte este posibilă ciclarea, împotriva căreia trebuie de luat
măsuri speciale. De exemplu, se poate introduce un contor de poziţii verificate, dacă acest număr a
devenit mai mare ca n, atunci algoritmul trebuie să fie oprit.
La căutarea înregistrării cu cheia dată K se foloseşte următorul algoritm:
1. Calculăm i=f(K). Trecem la punctul 2
2. Dacă poziţia i este liberă atunci în tabelul dat nu există înregistrarea cu cheia K. Dacă poziţia
este ocupată şi cheia coincide cu cheia K, atunci căutarea este reuşită, în caz contrar trecem la
punctul 3.
i 1, daca i 1 n
3. Punem i=(i+1) mod n, şi trecem la punctul 2: i
0, daca i 1 n
La căutare algoritmul este determinat dacă tabelul conţine înregistrarea tabelară cu cheia K, sau
vectorul conţine poziţii libere. În cazul neîndeplinirii acestor condiţii trebuie de luat măsuri
speciale. De exemplu, se poate introduce un contor de poziţii verificate, dacă acest număr a devenit
mai mare ca n, atunci algoritmul trebuie să fie oprit.
37
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
În exemplul acesta valoarea funcţiei de dispersare este egală cu numărul de ordine al primei litere a
numelui în alfabet. Numărul de ordin se modulează după mărimea vectorului de reprezentare.
O altă versiune a funcţiei hf(), mai complicată, poate fi scrisă astfel:
int hf(int n) // hashing function
{
int sum=0;
for(int i=0; i<4 && i<strlen(name); i++)
sum+=*(unsigned char*)(name+i);
return sum%n;
}
Exerciţii.
1. Supraîncărcaţi operatorul de inserţie în clasa hashing_elem.
2. Supraîncărcaţi operatorul de extragere în clasa hashing_elem.
În baza clasei generice table declarăm clasa hashing_table tot generică, care va asigura lucrul cu
tabele de repartizare cu examinarea lineară.
//
// C l a s s "h a s h i n g t a b l e"
38
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
//
template<class el> class hashing_table: public table<el>
{
protected:
int m;
public:
hashing_table<el>(char* file_name, int NMAX=200): table<el>(NMAX)
{
n=NMAX, m=0;
el tmp;
int repeated;
SD::pf=fopen(file_name, "rt");
m=0;
while(!feof(SD::pf))
if(tmp.fscanf_el(SD::pf)>0)
{
int i=tmp.hf(n);
repeated=-1;
while((repeated==-1) && !t[i].free())
{
if(tmp==t[i])
repeated=i;
else
i=(i+1)%n;
}
if(repeated!=-1)
{
char message[60];
char repeated_str[10];
message[0]='\0';
strcat(message,
"Key coincides with the key in the position: ");
strcat(message, itoa(repeated+1, repeated_str, 10));
strcat(message, "!\n");
error(message);
}
t[i]=tmp;
m++;
}
fclose(SD::pf), SD::pf=NULL;
}
39
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
int search(el e)
{
int position=-1;
int i=e.hf(n);
while((position==-1) && !t[i].free())
if(SD::ncomp++, e==t[i])
position=i;
else
i=(i+1)%n;
return position;
}
int get_m()
{
return m;
}
};
În funcţia main()creăm un tabel de repartizare cu examinarea lineară, încărcând acest tabel din
fişierul text Stud.txt. Apoi afişăm acest tabel şi căutăm careva înregistrări după cheie.
void main()
{
clrscr();
hashing_table<hashing_elem> hashing_gr("stud.txt", 15);
hashing_gr.show("Group:\n","");
char ch='n';
char surname[21];
while(ch!='y')
{
cout << "Enter a name to search: ";
cin >> surname;
hashing_elem e(surname, 2000, 0.0);
hashing_gr.reset_ncomp();
int pos=hashing_gr.search(e);
if(pos<0)
cout << "No table! " << "The number of comparisons: "
<< hashing_gr.get_ncomp() << "\n";
else
cout << "There are in the position " << (pos+1)
<< ". The number of comparisons: "
<< hashing_gr.get_ncomp() << "\n";
40
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
41
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
00│
01│ Blue, 1981, 500
02│ Red, 1980, 450
”Green”
03│ Cyan, 1975, 800
”Red”
04│ Black, 1981, 500
”Blue”
05│
”Gray”
06│ Green, 1987, 350
”Orange”
07│ Gray, 1968, 900
”White”
08│ White, 1980, 600
”Cyan”
09│ Yellow, 1988, 300
”Yellow”
10│
”Magenta”
11│
”Black”
12│ Magenta,1983, 600
13│
14│ Orange, 1984, 550
După cum se vede poziţiile 0, 5, 10, 11, 13 au rămas libere. Pe parcursul completării tabelului în
poziţiile 1, 2, 6, 7 au avut loc coliziuni. Drept vorbind, coliziunea în poziţia 7 pentru înregistrarea
tabelară cu cheia White este secundară.
Cercetările teoretice şi experimente pentru căutarea la metoda de repartizare cu examinarea liniară
au arătat, că pentru repartizarea aleatorie şi uniformă a înregistrărilor prin funcţia de repartizare în
intervalul [0, n-1], lungimea medie de căutare nu depinde de lungimea tabelului, dar depinde numai
m
de factorul de încărcare , unde m - lungimea tabelului, iar n – lungimea vectorului de
n
reprezentare.
Această proprietate este foarte importantă, mai ales pentru tabele mari. Tabele deterministe, atât
aranjate cât şi cele nearanjate nu posedă această proprietate. În tabele deterministe lungimea medie
de căutare creşte cu creşterea lungimii tabelului.
2
Formula aproximativă D( ) , pentru lungimea medie de căutare la metoda de repartizare
2 2
cu examinarea liniară oferă coincidenţă suficientă cu experimentul pentru σ≤0,85.
Formula este obţinută în presupunerea repartizării aleatorie şi uniformă a înregistrărilor pe poziţiile
vectorului de reprezentare.
Următorul tabel demonstrează dependenţa lungimii medie de căutare de factorul de încărcare σ.
σ 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
D(σ) 1.06 1.13 1.21 1.33 1.50 1.75 2.17 3.00 5.5
42
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
hashing_elem sample;
long NCOMP=0;
43
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
În metoda examinării liniare înregistrările ce produc coliziuni se includ în poziţiile libere aceluiaşi
vector de reflectare. Însă pentru aceste înregistrări se poate crea un tabel aparte. În tabelul adăugător
înregistrările se poate lega în lanţ, ca în liste, pentru uşurarea căutării.
44
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
Primary Secondary
Table Table
00│ │ 00|Gray, 1968,900│
01│Blue, 1981,500│02 01│Cyan, 1975,800│
02│Red, 1980,450│01 02│Black, 1981,500|
”Green”
03│ │ 03│ │
”Red”
04│ │ 04│ │
”Blue”
05│ │ 05│ │
”Gray”
06│Green, 1987,350│00 06│ │
”Orange”
07│White, 1980,600│ 07│ │
”White”
08│ │ 08│ │
”Cyan”
09│Yellow,1988,300│ 09│ │
”Yellow”
10│ │ 10│ │
”Magenta”
11│ │ 11│ │
”Black”
12│Magenta,1983,600│ 12│ │
13│ │ 13│ │
14│Orange,1984,550│ 14│ │
m 1
D m, n 1 , n – lungimea vectorului de reflectare, m – lungimea tabelului.
2n
Pentru a demonstra lucru cu tabele de repartizare cu înlănţuirea externă, în primul rând să declarăm
clasa hashing_linked_elem ca clasa derivată de la clasa usual_elem şi dotată cu câmpul next,
pentru a crea lanţuri de legături.
//
// C l a s s "h a s h i n g _ l i n k e d _ e l e m"
//
class hashing_linked_elem: public usual_elem
{
protected:
int next;
public:
hashing_linked_elem()
{
next = -1;
}
45
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
int get_next()
{
return next;
}
Exerciţii.
1. Supraîncărcaţi operatorul de inserţie în clasa hashing_linked_elem.
2. Supraîncărcaţi operatorul de extragere în clasa hashing_linked_elem.
Apoi, pe baza clasei abstracte SD, să declarăm clasa generică extern_hashing_table, care na va
da posibilitatea de a crea tabele de repartizare cu înlănţuirea externă.
46
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
//
// C l a s s "e x t e r n _ h a s h i n g _ t a b l e"
// m/n
template <class el> class extern_hashing_table: public SD
{
protected:
int n;
int m;
el *t;
el *v;
public:
extern_hashing_table<el>(char* file_name, int init_n=0): SD(file_name)
{
n=init_n;
if(n<=0)
n=countn();
t=new el[n];
v=new el[n];
m=0;
el tmp;
int repeated, position;
while(!feof(SD::pf))
if(tmp.fscanf_el(SD::pf)>0)
{
int i=tmp.hf(n);
if(t[i].free())
{
t[i]=tmp;
m++;
}
else
{
repeated=-1;
if( tmp==t[i] )
{
repeated=i;
t[i].show("", " !!!\n");
}
else
{
if(t[i].get_next()==-1)
47
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
{
int j=0;
while(!v[j].free())
j++;
t[i].set_next(j);
v[j]=tmp;
m++;
}
else
{
i=t[i].get_next();
position=-1;
while((repeated==-1) && position==-1)
{
if( tmp==v[i] )
{
repeated=i;
v[i].show("", " !!!\n");
}
else
if(v[i].get_next()==-1)
{
position=i+1;
while(!v[position].free())
position++;
v[i].set_next(position);
v[position]=tmp;
m++;
}
else
i=v[i].get_next();
}
}
}
if ( repeated!=-1 )
{
char message[60];
char repeated_str[10];
message[0]='\0';
//strcat(message, "Key coincides with the key in the position: ");
//strcat(message, itoa(repeated+1, repeated_str, 10));
//strcat(message, "!\n");
48
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
49
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
int search(el e)
50
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
{
int position=-1;
int i=e.hf(n);
if(!t[i].free())
if(SD::ncomp++, e==t[i])
position=i;
else
if((i=t[i].get_next())!=-1)
do
{
if(SD::ncomp++, e==v[i])
position=i;
else
i=v[i].get_next();
}
while((position==-1) && (i!=-1));
return position;
}
int get_n()
{
return n;
}
int get_m()
{
return m;
}
protected:
int countn()
{
return 200;
}
};
51
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
ex_hashing_gr.primary_show("Primary table:\n","");
ex_hashing_gr.secondary_show("Secondary table:\n","");
char ch='n';
char surname[21];
while(ch!='y')
{
printf("Enter a name to search: ");
scanf("%s", surname);
hashing_linked_elem e(surname, 2000, 0.0);
ex_hashing_gr.reset_ncomp();
int pos=ex_hashing_gr.search(e);
if(pos<0)
{
printf("No table! ");
printf("The number of comparisons: %d\n", ex_hashing_gr.get_ncomp());
}
else
{
printf("There are in the position %d. ", pos+1);
printf("The number of comparisons: %d\n", ex_hashing_gr.get_ncomp());
}
printf("Done ? (y/n) ");
ch = getch();
printf("\n"); }
}
52
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
53
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
10.
End of Table. Press any key ...
Secondary table:
1. Gray 1968 900.00 [ -1]
2. Cyan 1975 800.00 [ 3]
3. Yellow 1988 300.00 [ -1]
4. Magenta 1983 600.00 [ -1]
5. Black 1981 500.00 [ -1]
6.
7.
8.
9.
10.
End of Table. Press any key ...
Enter a name to search: Gray
There are in the position 1. The number of comparisons: 2
Done ? (y/n)
Enter a name to search: Blue
There are in the position 2. The number of comparisons: 1
Done ? (y/n)
Enter a name to search: Magenta
There are in the position 4. The number of comparisons: 3
Done ? (y/n)
Enter a name to search: Purple
No table! The number of comparisons: 0
Done ? (y/n)
hashing_linked_elem sample;
long NCOMP=0;
54
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
ex_hashing_gr.reset_ncomp();
if(ex_hashing_gr.search(sample)>=0)
NCOMP+=ex_hashing_gr.get_ncomp();
}
fclose(pf);
printf("m=%d, n=%d, NCOMP=%d, ALS=%.2lf", ex_hashing_gr.get_m(),
ex_hashing_gr.get_n(), NCOMP, (double)NCOMP/ex_hashing_gr.get_m());
printf(", D(m/n)=%.2lf\n",
1.+(ex_hashing_gr.get_m()-1.)/(2.*ex_hashing_gr.get_n()));
getch();
}
55
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
12.
13.
14.
15.
End of Table. Press any key ...
m=10, n=15, NCOMP=13, ALS=1.30, D(m/n)=1.30
Exerciţii.
1. Supraîncărcaţi operatorul de inserţie în clasa extern_hashing_table.
2. Supraîncărcaţi operatorul de extragere în clasa extern_hashing_table.
56
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
00│Black, 1981,500│
01│Blue, 1981,500│00
02│Red, 1980,450│03
”Green”
03│Cyan, 1975,800│
”Red”
04│Gray, 1968,900│
”Blue”
05│ │
”Gray”
06│Green, 1987,350│04
”Orange”
07│White, 1980,600│
”White”
08│ │
”Cyan”
09│Yellow,1988,300│
”Yellow”
10│ │
”Magenta”
11│ │
”Black”
12│Magenta,1983,600│
13│ │
14│Orange,1984,550│
57
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
Pentru a demonstra lucru cu tabele de repartizare cu înlănţuirea internă să declarăm pe baza clasei
abstracte SD clasa generică extern_hashing_table, care na va da posibilitatea de a crea tabele de
repartizare cu înlănţuirea internă.
//
// C l a s s "i n t e r n _ h a s h i n g _ t a b l e"
// m/n
template <class el> class intern_hashing_table: public SD
{
protected:
int n;
int m;
el *t;
el *v;
public:
intern_hashing_table<el>(char* file_name, int init_n=0): SD(file_name)
{
n=init_n;
if(n<=0)
n=countn();
t=new el[n];
v=new el[n];
m=0;
el tmp;
int i, j;
int repeated, position;
while(!feof(SD::pf))
if(tmp.fscanf_el(SD::pf)>0)
{
i=tmp.hf(n);
if(t[i].free())
{
t[i]=tmp;
m++;
}
else
{
repeated=-1;
if( tmp==t[i] )
{
repeated=i;
t[i].show("", " !!!\n");
58
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
}
else
{
if(t[i].get_next()==-1)
{
j=0;
while(!v[j].free())
j++;
t[i].set_next(j);
v[j]=tmp;
m++;
}
else
{
i=t[i].get_next();
position=-1;
while((repeated==-1) && position==-1)
{
if( tmp==v[i] )
{
repeated=i;
v[i].show("", " !!!\n");
}
else
if(v[i].get_next()==-1)
{
position=i+1;
while(!v[position].free())
position++;
v[i].set_next(position);
v[position]=tmp;
m++;
}
else
i=v[i].get_next();
}
}
}
if ( repeated!=-1 )
{
char message[60];
char repeated_str[10];
59
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
message[0]='\0';
//strcat(message, "Key coincides with the key in the position: ");
//strcat(message, itoa(repeated+1, repeated_str, 10));
//strcat(message, "!\n");
strcat(message, "Key coincides !!!\n");
error(message);
}
}
}
fclose(SD::pf), SD::pf=NULL;
el empty;
int k=0;
for(j=0; j<n; j++)
if(!v[j].free())
{
i=v[j].hf(n);
int j1=j;
do
{
while(!t[k].free())
k++;
t[i].set_next(k);
i=k;
t[i]=v[j1];
t[i].set_next(-1);
int jtmp=j1;
j1=v[j1].get_next();
v[jtmp]=empty;
}
while (j1!=-1);
}
delete v;
}
60
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
opening="";
if(!ending)
ending="\n";
printf("%s", opening);
for(int i=0; i<n; i++)
{
if(i>0 && i% nlinepp==0)
{
printf("Press any key to continue ...\n");
getch();
clrscr();
printf("%s", opening);
}
printf("%4d. ", (i+1)); t[i].show();
}
printf("%s", ending);
printf("End of Table. Press any key ...\n");
getch();
}
61
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
getch();
}
int search(el e)
{
int position=-1;
int i=e.hf(n);
if(!t[i].free())
do
{
if(SD::ncomp++, e==t[i])
position=i;
else
i=t[i].get_next();
}
while((position==-1) && (i!=-1));
return position;
}
int get_n()
{
return n;
}
int get_m()
{
return m;
}
protected:
int countn()
{
return 200;
}
};
62
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
in_hashing_gr.primary_show("Primary table:\n","");
char ch='n';
char surname[21];
while(ch!='y')
{
printf("Enter a name to search: ");
scanf("%s", surname);
hashing_linked_elem e(surname, 2000, 0.0);
in_hashing_gr.reset_ncomp();
int pos=in_hashing_gr.search(e);
if(pos<0)
{
printf("No table! ");
printf("The number of comparisons: %d\n", in_hashing_gr.get_ncomp());
}
else
{
printf("There are in the position %d. ", pos+1);
printf("The number of comparisons: %d\n", in_hashing_gr.get_ncomp());
}
printf("Done ? (y/n) ");
ch = getch();
printf("\n");
}
}
63
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
64
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
clrscr();
hashing_linked_elem sample;
long NCOMP=0;
getch();
}
65
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
Funcţii de repartizare
Timpul calculării funcţiei de repartizare f(k) intră în timpul mediu de căutare, deaceiea trebuie de
ţinut cont la alegerea algoritmului, realizând funcţia de repartizare.
O funcţie bună de repartizare trebuie să asigure repartizarea uniformă a înregistrărilor pe poziţiile
vectorului de reflectare, fiindcă distribuirea neuniformă măreşte timpul mediu de căutare. Însă dacă
calcularea valorii funcţiei de repartizare necesită îndeplinirea unui număr mare de operaţii, aceasta
poate distruge toată economia în timpul căutării. Deci, algoritmul calculării funcţiei de repartizare
nu trebuie să fie complicat. Să privim câteva metode de calculare a funcţiei de repartizare:
1. Una din metodele simple se bazează pe evidenţierea unei părţi din codul numeric al cheii. De
exemplu, fie dimensiunea maximă aşteptată a tabelului de nume simbolice nu întrece 256.
Atunci funcţia de repartizare poate avea în calitate de valoare 8 biţi, fiindcă 256=28. Se poate
pur şi simplu de a evidenţia primii 8 biţi din codul binar al identificatorului sau de a lua careva 8
biţi din mijlocul codului. Trebuie doar să asigurăm cu cât este posibil o distribuire uniformă a
înregistrărilor prin funcţia f(k) în intervalul [0, 255].
66
Structuri de date (în baza C++): Suport de curs
S.Pereteatcu, A.Pereteatcu
67