Sunteți pe pagina 1din 17

Ministerul Educaiei al Republicii Moldova

Universitatea de Stat din Moldova


Departamentul Informatic

Lucrare de laborator nr.1


La disciplina Structuri de Date i Metode de Programare
Tema: Metode de cutare n tabele.

Efectuat de studentul
grupei IA21: Ghiderman Ion
Verificat de: Bitcovschi L.

Chiinu, 2015
Sarcina
S se creieze un fiier textual care conine cel puin 50 de nregistrri i cel puin 5 cmpuri.
Unul din cmp este cheia care este unic pentru fiecare nregistrare i este neordonat.
S se implementeze citeva metode de cutare i pentru fiecare metod s se analizeze lungimea
medie teoretic de cutare i lungimea practic de cutare.De descris algoritmul metodei pe pai:
1. Metoda secvenial n tabele neordonate;
2. Metoda de cutare n tabele neordonate structur arborescent;
3. Metoda binara n tabele ordonate;
4. Metoda Hash (de dispersare);
5. Metoda Fibonacci.

Cutarea
Problema cea mai frecvent ntlnit n practic este, probabil, cea a regsirii rapide a unor
informaii. De exemplu, cutarea unui cuvnt n dicionar sau a unui numr n cartea de telefon. De
obicei, informaiile sunt reprezentate sub forma unor nregistrri, fiecare nregistrare coninnd un
cmp numit cheie, ce permite identificarea nregistrrii (de exemplu, ntr-un dicionar nregistrrile sunt
formate dintr-un cuvnt ce reprezint cmpul cheie, din pronunia i definiia corespunztoare
cuvntului respectiv).
O problem de cutare const n identificarea unei nregistrri cu cheia specificat, n scopul
prelucrrii informaiilor corespunztoare. Presupunem c nregistrrile au chei distincte; cazul
nregistrrilor avnd chei identice poate fi tratat n diverse moduri, n funcie de aplicaie (de exemplu,
fiecare nregistrare poate conine o list simplu nlnuit format din nregistrri avnd aceeai cheie
comun).

Cutarea secvential
S presupunem c nregistrrile sunt memorate ntr-un vector global R de dimensiune n. Pentru
a identifica nregistrarea cu cheia specificat x, o prim soluie ar fi de a compara secvenial cheia x cu
cheile nregistrrilor din R:
search (int c) { int position=-1,a,j;
for(int i=0;(position==-1)&&(i<n);i++)
{ a=t[i].getCod();
if(c==a )
{ position=i; t[i].show(); } }
if(position==-1 ) cout<<"\n Inexistent";
}
}
n cazul n care cheia nu se gsete n vector se fac exact n comparaii. n cazul n care cheia se
gsete n vector, n ipoteza c nregistrrile sunt cutate cu aceeai probabilitate (1/n), se fac n medie
2

1
n 1
(1 2... n)
comparaii.
n
2
Deci algoritmul de cutare secvenial este de O(n).
Cutarea secvenial poate fi adaptat n mod natural pentru cazul n care nregistrrile sunt
reinute ntr-o list simplu nlnuit, complexitatea algoritmului rmnnd neschimbat.

Cutarea binar
Dac numrul de nregistrri este mare, putem diminua timpul de cutare presupunnd c
nregistrrile sunt n ordinea cresctoare a cheilor i aplicnd o strategie de tip "divide et impera ".
Comparm cheia cutat x cu cheia nregistrrii din mijloc. n caz de egalitate, cutarea se
ncheie cu succes, altfel njumtim spaiul de cutare, determinnd jumtatea n care exist anse s
se gseasc cheia x i relund procedeul pentru aceast jumtate.
while((c!=t[m].getCod())&&(s<=f))
{ count++; delay(100);
if(t[m].getCod()>c)
{ f=m-1; }
else
{ s=m+1;}
m=(s+f)/2; }
if(s<=f)
{ t[m].show();
} else
{
cout<<"Not found!"; }
Observaii
1. Algoritmul poate fi descris foarte elegant recursiv.
2. Strategiei de cutare binar i se poate asocia un arbore binar astfel :
- rdcina arborelui este indicele elementului din mijloc, (n+1) div 2;
- subarborele stng este format din arborele binar corespunztor cutrii n irul elementelor cu indici
mai mici dect rdcina;
- subarborele drept este format din arborele binar corespunztor cutrii n irul elementelor cu indici
mai mari dect rdcina.

Fig. 1.
3

De exemplu, pentru n=15 arborele binar asociat algoritmului de cutare binar este

Fig. 2.
pe fiecare nivel i0,1,2,3 fiind situate 2 vrfuri.
Pentru n = 10 arborele este :
i

Fig. 3.
primele 3 niveluri fiind complete, al patrulea coninnd 3 vrfuri.
Observaii
1. Parcurgnd n inordine arborele, obinem irul indicilor ordonat cresctor.
2. Pe fiecare nivel i n arbore exist 2 i vrfuri, cu excepia ultimului nivel care este incomplet n cazul
n care n nu este de forma 2k-1.
Folosind aceste observaii, putem calcula nlimea arborelui asociat algoritmului de cutare
binar n funcie de n, dimensiunea spaiului de cutare. Dac notm cu h nlimea arborelui, atunci :
1+2+...+2h-1 < n 1+2+...+2h

2h-1 < n 2h+1-1

2h n < 2h+1

h log2n < h+1 h = [log2n]


Algoritmul de cutare binar efectueaz cel mult o comparaie pe fiecare nivel n arbore :
-dac elementul cutat nu se gsete n spaiul de cutare, cutarea se termin pe un nod
terminal, deci pe nivelul h sau h-1.
-dac elementul cutat se gsete n spaiul de cutare, n funcie de poziia sa, cutarea se poate
termina pe orice nivel, deci sunt necesare cel mult h+1 comparaii.
Putem enuna o teorem de caracterizare a complexitii algoritmului de cutare binar.
Teorem
Algoritmul de cutare binar efectueaz cel mult [log 2n]+1 comparaii, n cazul unei cutri cu
succes, respectiv [log2n] sau [log2n]+1 comparaii, n cazul unei cutri fr succes, unde n este
dimensiunea spaiului de cutare.
Deci complexitatea algoritmului de cutare binar este de O(log2n).
Observaie
4

Problema enunat presupune c mulimea de valori n care se face cutarea este static (deci nu
suport inserri sau tergeri de elemente) i deci arborele binar asociat rmne total echilibrat, cu
nlimea h = [log2n]. De asemeni, n analiza complexitii am presupus c orice valoare este cutat cu
aceeai probabilitate.

Tabele neordonate structurate arborescent


Cteodat n calitate de tabele temporare se aplic tabele neordonate structurate arborescent,
organizate n form de arbore binar. La fiecare nregistrare tabelar n aa tabele se adaug cte doi
pointeri: un pointer spre nregistrarea tabelar cu valoarea mai mic a cheii, iar al doilea pointer
spre nregistrrea tabelar cu valoarea mai mare a cheii.
O nou nregistrare se adaug n tabel la rnd, ca i n tabele neordonate simple. Dup
adugarea n tabel a noii nregistrri valoarea cheii se compar cu valoarea cheii a primei nregistrri a
tabelului. Dup rezultatul comparrii cu ajutorul pointerilor se afl adresa pstrrii urmtoarei
nregistrri, cu cheia crei trebuie de comparat valoarea cheii nregistrrii noi. Acest proces are loc pn
atunci, pn cnd nu va fi gsit nregistrarea tabelar cu pointerul vid n direcia necesar de cutare.
n acest pointer se nscrie adresa pstrrii noii nregistrri tabelare.
Cutarea n tabelul arborescent dup cheia dat are loc asemntor cu cutarea locului, n care
se nscrie adresa de pstrare. Lungimea medie de cutare n tabelul arborescent depinde de ordinea
nscrierii nregistrrilor la ncrcarea tabelului. n cel mai ru caz, cnd nregistrrile veneau n ordine
cresctoare (sau descresctoare) a cheilor, arborele va avea numai o ramur, i lungimea medie de
cutare rmne egal cu n/2, ca i n cazul tabelelor neordonate. n cel mai bun caz, cnd ordinea
nscrierii nregistrrilor este aa, c se primete un arbore binar simetric, lungimea cutrii se
micoreaz pn la D2=[log2n+2].

Cutarea Fibonacci
Algoritm: Sirul numerelor Fibonacci se defineste recursiv astfel: F0=0, F1=1, Fk+2=Fk+1+Fk, k>=0.
Lungimea tabelului este egala cu ultimul numar Fibonacci si este N=Fm. Daca lungimea tabelului nu
este un numar Fibonacci se ia in calitate de Fm cel mai mic numar din F, imediat mai mare decat n.
Pas1 (Initializare):
i=Fk, p=Fk-1, q=Fk-2, unde p si q sunt doua numere Fibonacci consecutive.
Pas2 (Comparare): Daca K<Ki, atunci mergi la pasul 3; daca K>Ki atunci mergi la pasul 4; si daca
K=Ki algoritmul se finiseaza cu succes.
Pas3 (Descrestere i): Daca q=0, atunci avem insucces. In caz contrar se fac urmatoarele atribuiri: i=i-q
si p=q; q=p-q; apoi ne intoarcem la pasul 2.
Pas4 (Incrementare i): Daca p=1, atunci avem insucces. In caz contrar se fac urmatoarele atribuiri
i=i+q p=p-q si apoi q=q-p (p se ia acel obtinut acum); apoi ne intoarcem la pasul 2.

Programul
#include
#include
#include
#include
#include
#include
#include

<conio.h>
<stdio.h>
<iostream.h>
<string.h>
<math.h>
<stdlib.h>
<dos.h>

class elem {
public:

protected :

virtual int fscanf_el (FILE * f)=0;


virtual int show(const char * opening,const char * ending)=0;
virtual int free ()=0;
int operator > (elem &) {
error("Error should overcode operator \">\"!\n"); return 0; }
int operator < (elem &) {
error("Error should overcode operator \"<\"!\n"); return 0; }
int operator >= (elem &) {
error("Error should overcode operator \">=\"!\n"); return 0; }
int operator <= (elem &) {
error("Error should overcode operator \"<=\"!\n"); return 0; }
int operator == (elem &) {
error("Error should overcode operator \"==\"!\n"); return 0; }
int operator != (elem &) {
error("Error should overcode operator \"!=\"!\n"); return 0; }

void error (char * message){


cout<<message; cout<<"Apasa orice tasta pentru a finisa...\n";
getch(); exit(1); }
};
class STUDENT
{
public:
int cod;
char nume[20];
char prenume[20];
char obiect[20];
int an;
int st;
int dr;
STUDENT()
{ cod=0; strcpy(nume,"");
strcpy(prenume,"");
strcpy(obiect,""); an=0; st=-1;
dr=-1;
}
void setcod(int i) { cod=i;}
void setnume(char* n) {
strcpy(nume,n); }
void setprenume(char* a) {
strcpy(prenume,a);}
void setan(int i) {an=i; }
void setobiect(char* d) {
strcpy(obiect,d); }
int getcod() { return cod; }
int set_st(int new_st)
{
st=new_st;
return st;
}

int get_st()
{
return st;
}
int set_dr(int new_dr)
{
dr=new_dr;
return dr;
}
int get_dr()
{
return dr;
}
char* getnume() {
return nume; }
char* getprenume() { return prenume;}
int getan(){ return an; }
char* getobiect() { return obiect; }
int fscanf_el (FILE * f)
{return fscanf(f,"%i %s %s %s
%i",&cod,nume,prenume,obiect,&an);}
void virtual show( ) {
cout<<cod<<" "<<nume<<" "<<prenume<<"
"<<an<<endl; }
virtual int free() { return cod==0; }
int operator >(STUDENT &e2) {
return (this->cod>e2.cod);}
int operator < (STUDENT &e2) {
return (this->cod<e2.cod); }
int operator <= (STUDENT &e2) {
return (this->cod<=e2.cod); }
int operator >= (STUDENT &e2){
return (this->cod>=e2.cod); }
int operator == (STUDENT &e2) {
return (this->cod==e2.cod); }
int operator != (STUDENT &e2) {
return (this->cod!=e2.cod); }
};

"<<obiect<<"

template <class el> class tabel


{
protected:
int n;
el t[200];
public:
tabel() {n=0;}
tabel (char * file);
void search (int c) ;
void searchbin (int c);
int fib_search(el initial);
int searchtree(STUDENT tmp);
void createtree();
void killtree();
void sort();
void show(const char *opening,const char *ending);
protected:
void error(char *message){
cout<<message; cout<<"Apasa orice tasta pentru a finisa...\n";

};

getch(); exit(1); }

template
<class el>
tabel<el>::tabel (char * file) {
FILE *pf;
pf=fopen(file,"rt");
n=0;
while(!feof(pf))
if (t[n].fscanf_el(pf)>0)
n++;
fclose(pf);
}
template
<class el>
void tabel<el>::search (int c) { int position=-1,a,j;
for(int i=0;(position==-1)&&(i<n);i++)
{ a=t[i].getcod();
if(c==a )
{ position=i; t[i].show(); } }
if(position==-1 ) cout<<"\nNu exista";
else{
cout<<"\nLungimea practica de cautare este:"<<position;
cout<<"\nLungimea teoretica de cautare este:"<<n/2; }
}
template
<class el>
int tabel<el>::fib_search(el initial)
{
int position=-1, contor=0, i=0, q=0, p=0;
double durata;
int
fib[]={0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765};
for(int j=0;;j++)
{
if(i>n)
{
i=fib[j-2];
p=fib[j-3];
q=fib[j-4];
break;
}
else i=fib[j];
}
while(2<3)
{
contor++;
if(initial==t[i]) {position=i; break;}
if(initial<t[i])
{
if(q==0) break;
else {int v=p;i=i-q;p=q;q=v-q;}
}
if(initial>t[i])
{

if(p==1) break;
else{ i=i+q; p=p-q; q=q-p;}
}

}
template
<class el>

}
if(position==-1){cout<<endl<<cout<<"Error!Error!"<<endl;}
else
{
cout<<endl;
t[position].show();
cout<<endl<<"Lungimea practica de cautare este:"<<" "<<contor<<endl;
float caut;
caut=log(n)/log(2);
cout<<"Lungimea teoretica de cautare este:"<<" "<<caut<<endl;
}
return position;

void tabel<el>::show(const char *opening,const char *ending)


{ cout<<opening;
for(int i=0;i<n;i++){
t[i].show();if(i%20==0&&i!=0){ cout<<endl<<"Apasa pentru a vedea";
getch();clrscr();}
cout<<ending;
cout<<"\n " ;} }

template
<class el>
void tabel<el>::sort()
{
int j,count=0;
el aux;
for(int i=0;i<n-1;i++)
for(j=i;j<n;j++)
{
if(t[i].getcod()>t[j].getcod())
{
aux=t[i];
t[i]=t[j];
t[j]=aux;
count++;
}
}
cout<<"Sortarea s-a terminat!"<<endl;
getch();
}
template
<class el>
void tabel<el>::searchbin(int c)
{
if(!n)
{
cout<<"ERROR! Introduceti datele";
getch();
return; }
int s=0,f=n-1,count=1,j;
int m=(s+f)/2;

while((c!=t[m].getcod())&&(s<=f))
{ count++;
if(t[m].getcod()>c)
{
f=m-1;
}
else
{
s=m+1;}
m=(s+f)/2; }
if(s<=f)
{
t[m].show();
cout<<"\nLungimea practica de cautare este:"<<" "<<count;
cout<<"\nLungimea teoretica de cautare este:"<<" "<<log(n)/log(2);
}
else
{
cout<<"\n Nu exista asa date!";
}

template
<class el>
void tabel<el>::createtree()
{
for(int i=1;i<n;i++)
{
int forw=1,j=0;
while(forw)
{
if(t[i]<t[j])
{
if(t[j].get_st()==-1)
{
t[j].set_st(i);
forw=0;
}
else
j=t[j].get_st();
}
else
if(t[i]>t[j])
{
if(t[j].get_dr()==-1)
{
t[j].set_dr(i);
forw=0;
}
j=t[j].get_dr();
}
}
}
}
template
<class el>
void tabel<el>::killtree()
{
for(int i=0;i<n;i++)
{
t[i].set_st(-1);

10

t[i].set_dr(-1);

}
template
<class el>
int tabel<el>::searchtree(STUDENT tmp)
{
int i=0,count=0,forw=1;
while(forw)
{
if(tmp==t[i])
{
t[i].show();
cout<<t[i].st<<" "<<t[i].dr<<endl;
cout<<"Lungimea practica de cautare este:"<<count<<endl;
cout<<"Lungimea teoretica maxima de cautare este:"<<n/2<<endl;
cout<<"Lungimea teoretica minima de cautare
este:"<<log(n)/log(2.0)*2.0;
forw=0;
}
else
{
if(tmp<t[i])
i=t[i].get_st();
else
i=t[i].get_dr();
if(i==-1)
{
forw=0;
cout<<"Elementul nu este gasit"<<endl;
}
}
count++;
}
return 0;
}

void main()
{
clrscr();
STUDENT pl,tmp;
tabel <STUDENT> gr("student.txt");
char ch, c;
int coden;
do{
clrscr();
cout<<"Menu:"<<endl;
cout<<"*****************************************"<<endl;
cout<<"0)Afisare tabel"<<endl;
cout<<"1)Metoda secventila"<<endl;
cout<<"2)Metoda arborescenta"<<endl;
cout<<"3)Sortarea"<<endl;
cout<<"4)Metoda binara"<<endl;
cout<<"5)Metoda Fibonacci"<<endl;

11

cout<<"6)Exit"<<endl;
c=getch();
switch(c)
{
case '0':
ch='n';
while(ch!='y')
{
clrscr();
gr.show("Continutul fisierului:\n"," ");
cout<<endl<<"Iesiti?(Y/N)";
ch=getch();
}
break;
case '1':
ch='n';
while(ch!='y')
{
clrscr();
cout<<"Introduceti codul pentru cautare"<<endl;
cin>>coden;
gr.search(coden);
cout<<endl;
cout<<endl<<"Iesiti?(Y/N)";
ch=getch();
}
break;
case '2':
ch='n';
while(ch!='y')
{
clrscr();
cout<<"Introduceti codul pentru cautare"<<endl;
cin>>coden; cout<<endl;
tmp.setcod(coden);
gr.createtree();
cout<<endl;
gr.searchtree(tmp); cout<<endl;
cout<<endl<<"Iesiti?(Y/N)";
gr.killtree();
ch=getch();
}
break;
case '3':
clrscr();
gr.sort();
getch();
break;
case '4':
ch='n';
while(ch!='y')
{
clrscr();
cout<<"Introduceti codul pentru cautare"<<endl;
cin>>coden;
cout<<endl;
gr.searchbin(coden); cout<<endl;
cout<<endl<<"Iesiti?(Y/N)";
ch=getch();

12

}
break;
case '5':
ch='n';
while(ch!='y')
{
clrscr();
cout<<"Introduceti codul pentru cautare"<<endl;
cin>>pl.cod;
cout<<endl;
if(gr.fib_search(pl)==-1) cout<<endl<<"Eroare!
Sortati!"<<endl;
cin.ignore();
cout<<endl;
cout<<endl<<"Iesiti?(Y/N)";
ch=getch();
}
break;
} }
while(c!='6');
}

13

Continutul fiierului student.txt

14

Rezultatul

Afiare tabel:

Metoda secvenial

15

Metoda arborescent

Metoda binar

Metoda Fibonacci

16

Concluzii
La execuia programului s-a afiat tabloul cu nregistrrile ncarcate din fiier apoi la
introducerea unui identificator pentru o inregistrare pe care o vom cauta n tabloul de elemente s-a
afisat nregistrarea, lungimea teoretica de cautare, lungimea practica de cautare, iar daca nu se gaseste
ni se afieaza nregistrare inexistent.
Pentru a vedea mai bine care metod este mai eficient pentru cutarea n tabele am cautat una
i aceiai nregistrare folosind toate metodele descrise n program.
n final am observat c cea mai bun metod pentru cutare este:
1. Metoda Fibonacci
2. Metoda binar
3. Metoda arborescent
4. Metoda secvential
Nu este mare diferen dintre metoda Fibonacci i cea binar, dar totui este la lungimea
teoretic cu citeva miimi.

17