Efectuat de studenta:
Țînțaru Valeria, gr.I1801
Verificat de:
Opinca Carolina, conf. Univ.
Chişinău, 2020
1
Sarcina laboratorului:
Să se creeze un fișier textual care conține cel puțin 50 de înregistrări și cel puțin 5 cîmpuri.
Unul din cîmp este cheia care este unică pentru fiecare înregistrare și este neordonată.
Să se implementeze citeva metode de căutare și pentru fiecare metodă să se analizeze lungimea
medie teoretică de căutare și lungimea practică de căutare.De descris algoritmul metodei pe pași:
1. Metoda secvențială în tabele neordonate;
2. Metoda de căutare în tabele neordonate structură arborescentă;
3. Metoda binara în tabele ordonate;
4. Metoda Hash (de dispersare);
5. Metoda Fibonacci.
Căutarea:
Problema cea mai frecvent întâlnită în practică este, probabil, cea a regăsirii rapide a unor
informaţii. De exemplu, căutarea unui cuvânt în dicţionar sau a unui număr în cartea de telefon. De
obicei, informaţiile sunt reprezentate sub forma unor înregistrări, fiecare înregistrare conţinând un
câmp numit cheie, ce permite identificarea înregistrării (de exemplu, într-un dicţionar înregistrările sunt
formate dintr-un cuvânt ce reprezintă câmpul cheie, din pronunţia şi definiţia corespunzătoare
cuvântului respectiv).
O problemă de căutare constă în identificarea unei înregistrări cu cheia specificată, în scopul
prelucrării informaţiilor corespunzătoare. Presupunem că înregistrările au chei distincte; cazul
înregistrărilor având chei identice poate fi tratat în diverse moduri, în funcţie de aplicaţie (de exemplu,
fiecare înregistrare poate conţine o listă simplu înlănţuită formată din înregistrări având aceeaşi cheie
comună).
Cãutarea secventialã:
2
În cazul în care cheia nu se găseşte în vector se fac exact n comparaţii. În cazul în care cheia se
găseşte în vector, în ipoteza că înregistrările sunt căutate cu aceeaşi probabilitate (1/n), se fac în medie
1 n 1
(1 2 ... n ) comparaţii.
n 2
Deci algoritmul de căutare secvenţială este de O(n).
Căutarea secvenţială poate fi adaptată în mod natural pentru cazul în care înregistrările sunt
reţinute într-o listă simplu înlănţuită, complexitatea algoritmului rămânând neschimbată.
Căutarea binară
Dacă numărul de înregistrări este mare, putem diminua timpul de căutare presupunând că
înregistrările sunt în ordinea crescătoare a cheilor şi aplicând o strategie de tip "divide et impera ".
Comparăm cheia căutată x cu cheia înregistrării din mijloc. În caz de egalitate, căutarea se
încheie cu succes, altfel înjumătăţim spaţiul de căutare, determinând jumătatea în care există şanse să
se găsească cheia x şi reluând procedeul pentru această jumătate.
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!"; }
Observaţii
1. Algoritmul poate fi descris foarte elegant recursiv.
2. Strategiei de căutare binară i se poate asocia un arbore binar astfel :
- rădăcina arborelui este indicele elementului din mijloc, (n+1) div 2;
- subarborele stâng este format din arborele binar corespunzător căutării în şirul elementelor cu indici
mai mici decât rădăcina;
- subarborele drept este format din arborele binar corespunzător căutării în şirul elementelor cu indici
mai mari decât rădăcina.
3
Fig. 1.
De exemplu, pentru n=15 arborele binar asociat algoritmului de căutare binară este
Fig. 2.
i
pe fiecare nivel i0,1,2,3 fiind situate 2 vârfuri.
Pentru n = 10 arborele este :
Fig. 3.
primele 3 niveluri fiind complete, al patrulea conţinând 3 vârfuri.
Observaţii
1. Parcurgând în inordine arborele, obţinem şirul indicilor ordonat crescător.
2. Pe fiecare nivel i în arbore există 2 i vârfuri, cu excepţia ultimului nivel care este incomplet în cazul
în care n nu este de forma 2k-1.
Folosind aceste observaţii, putem calcula înălţimea arborelui asociat algoritmului de căutare
binară în funcţie de n, dimensiunea spaţiului de căutare. Dacă notăm cu h înălţimea 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 căutare binară efectuează cel mult o comparaţie pe fiecare nivel în arbore :
-dacă elementul căutat nu se găseşte în spaţiul de căutare, căutarea se termină pe un nod
terminal, deci pe nivelul h sau h-1.
-dacă elementul căutat se găseşte în spaţiul de căutare, în funcţie de poziţia sa, căutarea se poate
termina pe orice nivel, deci sunt necesare cel mult h+1 comparaţii.
Putem enunţa o teoremă de caracterizare a complexităţii algoritmului de căutare binară.
4
Teoremă
Algoritmul de căutare binară efectuează cel mult [log2n]+1 comparaţii, în cazul unei căutări cu
succes, respectiv [log2n] sau [log2n]+1 comparaţii, în cazul unei căutări fără succes, unde n este
dimensiunea spaţiului de căutare.
Deci complexitatea algoritmului de căutare binară este de O(log2n).
Observaţie
Problema enunţată presupune că mulţimea de valori în care se face căutarea este statică (deci nu
suportă inserări sau ştergeri de elemente) şi deci arborele binar asociat rămâne total echilibrat, cu
înălţimea h = [log2n]. De asemeni, în analiza complexităţii am presupus că orice valoare este căutată cu
aceeaşi probabilitate.
Căutarea 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.
5
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.
Listingul programului:
#include <conio.h>
#include <stdio.h>
#include <iostream.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <dos.h>
class elem {
public:
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; }
protected :
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);}
6
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<<" "<<obiect<<"
"<<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); }
};
7
void killtree();
void sort();
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++;
8
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;}
}
}
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;
}
template
<class el>
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)
9
{
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();
}
}
}
}
1
template
<class el>
void tabel<el>::killtree()
{
for(int i=0;i<n;i++)
{
t[i].set_st(-1);
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();
1
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;
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')
1
{
clrscr();
cout<<"Introduceti codul pentru cautare"<<endl;
cin>>coden;
cout<<endl;
gr.searchbin(coden); cout<<endl;
cout<<endl<<"Iesiti?(Y/N)";
ch=getch();
}
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');
}
1
Continutul fișierului student.txt
1
Rezultatul
Afișare tabel:
1
Metoda secvențială
Metoda arborescentă
Metoda binară
1
Metoda Fibonacci
Concluzii
La execuția programului s-a afișat tabloul cu înregistrările încarcate din fișier 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 afișeaza înregistrare inexistentă.
Pentru a vedea mai bine care metodă este mai eficientă pentru căutarea în tabele am cautat una
și aceiași înregistrare folosind toate metodele descrise în program.
În final am observat că cea mai bună metodă pentru căutare este:
1. Metoda Fibonacci
2. Metoda binară
3. Metoda arborescentă
4. Metoda secventială
Nu este mare diferență dintre metoda Fibonacci și cea binară, dar totuși este la lungimea
teoretică cu citeva miimi.