Documente Academic
Documente Profesional
Documente Cultură
Laborator PCIII
Laborator PCIII
ar
m
Lucrarea nr. 1
ra
Determinarea experimentala a timpului de
og
execuþie al unui program
pr
de
1. Scopul lucrãrii - lucrarea prezintã aspecte legate de diferite
ci
modalitaþi de determinare experimentalã a timpilor de execuþie pentru
ni
diferite secvenþe de program.
eh
it
2. Aspecte teoretice
is
2.1. Determinarea experimentalã a timpului de execuþie al unui
itm
program
Determinarea timpului de execuþie al unui program prin metode
or
analitice este foarte dificilã în practicã. Aºa ca mai exista si o alta
lg
-A metoda experimentalã, mult mai uºoarã, ºi cu rezultate la fel de
concludente în condiþiile în care se porneºte de la anumite premize
da
metode.
m
.Z
D
5
e
ar
m
Este cunoscut faptul cã, în general, timpul de execuþie al unui program
ra
depinde de urmãtorii factori :
og
-volumul datelor de intrare
pr
-calitatea codului generat de compilator
de
-natura si viteza de execuþie a instrucþiunilor programului (dependentã
ºi de performanþele calculatorului pe care acesta ruleazã)
ci
ni
-complexitatea algoritmului care stã la baza programului.
eh
In condiþiile în care algoritmii sunt rulaþi pe calculatoare cu performanþe
it
similare, utilizîndu-se acelaºi compilator, practic timpul de execuþie
is
devine funcþie doar de volumul datelor de intrare ºi complexitatea
itm
algoritmului. In aceste condiþii simplificate, evaluarea experimentalã a
performanþelor unui algoritm este cu adevarat relevantã.
or
In C++, funcþia prin care se poate determina ora curentã a calculatorului
lg
este gettime, ºi se gãseºte în bibioteca <dos.h> .
-A
2.2. Exemplu de determinarea experimentala a timpului de executie
da
al unui program
an
6
e
ar
m
for (i=0;i<n;i++)
ra
for (j=n;j>=i+1;j--)
og
if (a[j-1]>a[j])
pr
{
temp=a[j-1];
de
a[j-1]=a[j];
ci
a[j]=temp
ni
}
eh
}
it
is
void main()
{
itm
...
or
(1) struct time t1,t2;
int durata;
lg
-A (2) gettime(&t1);
(3) bubble_sort(a);
(4) gettime(&t2);
da
(5) durata=(t2.ti_sec*100+t2.ti_hund)
an
-(t1.ti_sec*100+t1.ti_hund);
ar
printf(“Durata de executie=%dms”,durata);
m
...
.Z
}
D
7
e
ar
m
memorie utilizat se face pe seama timpului de execuþie. Un exemplu
ra
tipic în acest sens îl constituie reprezentarea compactã a matricilor:
og
economia de memorie obþinutã în acest fel este însã în detrimentul
pr
timpului de execuþie, deoarece accesul la elementele matricii este mult
de
mai complicat.
În privinþa aspectelor referitoare la portabilitate, reutilizabilitate sau
ci
mentenanþã, estimarea performanþelor este mult mai puþin precisã.
ni
Evident, utilizarea în programare doar a construcþiilor standard va
eh
permite transferul uºor al programelor dintr-un mediu în altul, dar
it
aceasta vine în contradicþie cu cerinþele referioare la timp ºi memorie
is
ocupatã. De asemenea, pentru a permite reutilizarea modulelor este
itm
necesar ca interfaþa oferitã de acestea sã fie cît mai clarã ºi sã ofere cît
mai multe servicii posibile, astfel încît sã nu fie necesar nici un acces
or
care sã ocoleascã acestã interfaþã: satisfacerea acestor cerinþe intrã din
lg
-A
nou în contradicþie cu cele referitoare la eficienþã. În concluzie,
programatorul, atunci cînd elaboreazã un algoritm pentru o anumitã
da
#include <conio.h>
#include <stdio.h>
#include <dos.h>
int nr;
struct time timp1,timp2;
unsigned long int fact(int n)
{unsigned long int f=1;
for (long int i=1;i<=n;i++)
{f*=i;
//fortam o intarziere pentru punere în
//evidenþã a timpului de executie
delay(100);
}
return f;
}
8
e
ar
m
void main()
ra
{ clrscr();
og
do
pr
{ //citire numar
de
printf("Dati numarul (1..30) = ");
scanf("%d",&nr);
ci
}
ni
while ((nr<1)||(nr>30));
eh
it
//get time start
is
gettime(&timp1);
//calculam si afisam factorialul
itm
printf("%d!=%lu",nr,fact(nr));
or
//get time final
lg
gettime(&timp2);
-A
//convertim timpii in ms
da
(timp1.ti_hour*3600000);
ar
(timp2.ti_sec*1000) + (timp2.ti_min*60000) +
.Z
(timp2.ti_hour*3600000);
D
3. Probleme propuse
9
e
ar
m
Lucrarea nr. 2
ra
Tehnici de cãutare în tablouri
og
pr
1. Scopul lucrãrii - în foarte multe programe, se ajunge la un moment
de
dat în situaþia de a fi necesarã cãutarea unei anumite informaþii, dupã
ci
anumite criterii, dintr-un set de informaþii. Lucrarea curentã prezintã
ni
unele metode de implementare a unor astfel de algoritmi de cautare.
eh
it
2. Aspecte teoretice
is
2.1. Tehnica fanionului.
itm
Vom considera cã dorim sã cãutam dacã existã un anumit numãr dat,
stocat într-o variabilã X ,într-un set de n numere stocate într-un tablou
or
dat A. Tehnica fanionului urmãreºte îmbunãtãþirea performanþei
lg
algoritmului de cãutare standard, prin simplificarea condiþiei de cãutare.
-A
Principiul acestuia este foarte simplu : se mai adauga la sfarsitul
tabloului înca un element (fanion) ºi care se asigneazã cu valoarea
da
int a[100],n,x,i;
void main()
{
clrscr() ;
(1) printf(”Dati valoarea lui n=”);
(2) scanf(“%d”,&n) ;
for (i=0 ;i<n ;i++)
{
(3) printf(“Dati a[%d]=”,i);
(4) scanf(“%d”,&a[i]) ;
}
(5) printf(“Dati valoarea de cautat (x)=”);
(6) scanf(“%d”,&x);
(7) i=0;
(8) a[n]=x;
(9) while (a[i]!=x) i++;
(10) if (i==n)
(11) printf(“Nu a fost gasit in tablou.”);
10
e
ar
m
else
ra
(12) printf(“A fost gasit pe pozitia %d.”,i);
og
}
pr
de
Se poate observa cã singurele diferenþe fata de algoritmul clasic de
cãutare se gãsesc în liniile 8 si 9. Linia 8 pozitioneazã fanionul , iar linia
ci
9 face cãutarea propriu zisaã, cãutare care se va derula pîna cînd se va
ni
gãsi o valoare în tablou egalã cu. Deci, toatã condiþia de continuare al
eh
algoritmului de cãutare se rezuma la o singura expresie logica a[i]!=x.
it
Ideal : cînd elementul cãutat e primul element (prima poziþie) din
is
tablou, acesta fiind gãsit încã de la primul pas
itm
Dezavantajos : cînd elementul nu exista în tablou , deci cãutarea se va
opri pe elementul fanion (ultimul).
or
lg
-A 2.2. Cãutarea binarã
Cãutarea într-un tablou poate fi acceleratã în mod considerabil dacã
elementele unui tablou sînt în prealabil ordonate (sortate). Necesitatea
da
componentã (cheie) ce aparþine unui tip scalar, iar cãutarea se face dupã
ar
aceastã componentã. În acest caz principiul cel mai des utilizat este cel
m
11
e
ar
m
9. atata timp cat [(a[mij]!=x) si (stanga<=dreapta)]
ra
10. daca (a[mij]=x) elemental a fost gasit pe pozitia mij
og
11. altfel elemental nu a fost gasit
pr
de
La fiecare repetiþie, intervalul inspectat, situat între indicii stanga ºi
dreata este înjumãtãþit. Numãrul necesar de comparaþii este cel mult
ci
egal cu log2(N), N reprezentînd numãrul de elemente ale tabloului în
ni
care se face cãutarea.
eh
Ideal : elementul cãutat este exact la mijlocul tabloului nostru, rezultînd
it
cã va fi gãsit încã de la primul pas
is
Dezavantajos : elementul nu existã în tablou
itm
or
2.3. Cautarea binarã performantã
Bazatã pe cãutara binarã prezentatã anterior, aceastã tehnicã foloseºte
lg
-A
un alt mod de calcul a extremitãþii dreapte a intervalului curent, astfel
încît se simplificã condiþia de ciclare.
da
1.
Citire element de cautat x
ar
2.
stanga=0;dreapta=n;
m
3.
repeta
.Z
4.
calculeaza mijlocul : mij=(stanga+dreapta)/2;
D
5. .
6. . daca (a[mij]>x) x se afla in partea stanga => dreapta=mij
7. . altfel x se afla in partea dreapta => stanga=mij+1
8. atata timp cat (stanga<dreapta)
9. daca (a[mij]=x) elemental a fost gasit pe pozitia mij
10. altfel elemental nu a fost gasit
12
e
ar
m
2.4. Cãutarea prin interpolare
ra
Este similarã cu cãutarea binarã, dar foloseºte o altã formulã pentru
og
calculul mijlocului, ºi anume:
pr
mij=stanga+(x-a[stanga])*(dreapta-stanga)
de
div (a[dreapta]-a[stanga])
ceea ce conduce la o delimitare mai rapidã a zonei din tablou în care
ci
s-ar putea gãsi x. Ca principiu, metoda este inspiratã dupã procedeul
ni
c ãutarii într-o carte de telefon.
eh
Pseudocodul unui program care foloseste aceastã metodã este
it
urmãtorul :
is
itm
1. Citire tablou (trebuie obligatoriu sa fie sortat)
2. Citire element de cautat x
or
3. stanga=0;dreapta=n-1;
lg
-A 4. repeta
5. . calculeaza mijlocul : mij= …….
da
.
8. atata timp cat (a[mij]!=x) si (stanga<dreapta) si
ar
În continuare vom explica pe larg metoda prin care s-a ajuns la formula
de calcul al mijlocului. Principiul este urmãtorul : unde ar trebui sa fie
acel numar, în cazul în care distribuþia numerelor în tablou ar fi
uniformã.
Presupunem urmatoarea situaþie :
3 6 10 12 20 31
| |
stanga dreapta
13
e
ar
m
Pasul = (31-3) / (5-0) = 28/5 = 5,6.
ra
Deci , tabloul nostru ar arãta astfel dacã ar exista distribuþie uniformã:
og
pr
3 8,6 14,2 19,8 25,4 31
de
| |
stanga dreapta
ci
ni
Deci, dacã ar fi sã cãutam elementul cu valoarea 16, acesta ar fi undeva
eh
în zona lui 14,2 , adicã pe pozitia a 3-a relativ la stînga. Cum se
it
calculeaza asta? Astfel :
is
-presupunem ca avem o distribuþie uniformã, cu primul element 1 ºi
itm
pasul 5.
or
lg
1 6 11 16 21 26
| |-A
stanga dreapta
da
urmãtoarea formulã :
ar
m
14
e
ar
m
2.5. Problemã rezolvatã
ra
Sã se realizeze un program în C++ cu ajutorul cãruia se va cãuta
og
existenþa unui anumit student într-o listã de n studenti. Pentru cãutare se
pr
va folosi tehnica cãutarii binare. Fiecare student va fi caracterizat prin
de
numele ºi grupa din care face parte.
ci
#include <conio.h>
ni
#include <stdio.h>
eh
#include <string.h>
it
is
struct st
itm
{
char nume[20];
or
char grupa[6];
lg
}student[51];
-A
int nr_studenti;
da
char nume_cautat[20];
an
void citeste_studenti()
ar
{ do
m
{
.Z
scanf("%d",&nr_studenti);
}
while ((nr_studenti<5)||(nr_studenti>50));
for (int i=1;i<=nr_studenti;i++)
{
printf("\nDati numele studentului %d=",i);
scanf("%s",student[i].nume);
printf("Dati grupa studentului %d=",i);
scanf("%s",student[i].grupa);
}
}
void sorteaza_studenti()
{st temp;
int gasit;
do
{gasit=0;
for (int i=1;i<=nr_studenti-1;i++)
if
(strcmp(student[i].nume,student[i+1].nume)>0)
15
e
ar
m
{
ra
gasit=1;
og
temp=student[i];
pr
student[i]=student[i+1];
student[i+1]=temp;
de
}
ci
}
ni
while (gasit==1);
eh
}
it
is
void cautare_binara()
{ int stanga=1,dreapta=nr_studenti,mijloc;
itm
do
or
{
mijloc=(stanga+dreapta)/2;
lg
if -A
(strcmp(student[mijloc].nume,nume_cautat)>0)
dreapta=mijloc-1;
da
else
an
stanga=mijloc+1;
ar
}
m
while
.Z
((strcmp(student[mijloc].nume,nume_cautat)!=0)
&& (stanga<=dreapta));
D
if
(strcmp(student[mijloc].nume,nume_cautat)==0)
printf("\n\nStudentul a fost gasit in
grupa %s.",student[mijloc].grupa);
else
printf("\nStudentul NU a fost gasit!");
}
void main()
{ clrscr();
citeste_studenti();
//pentru a aplica cautarea binara, studentii vor trebui sortati
sorteaza_studenti();
//afisam studentii gata sortati
clrscr();
printf("Studentii sortati alfabetic sunt:");
for (int f=1;f<=nr_studenti;f++)
printf("\nNume:%20s\tGrupa:%5s",student[f].nume,
student[f].grupa);
16
e
ar
m
//citeste numele studentului care dorim sa-l cautam
ra
printf("\n\nDati numele studentului de
og
cautat = ");
pr
scanf("%s",nume_cautat);
//lansam cautarea binara
de
cautare_binara();
ci
getch();
ni
}
eh
it
is
3. Probleme propuse
itm
1. Se considerã o matrice de numere de dimensiune MxN. Sã se citeascã
or
de la consolã un numãr, ºi sã se caute existenþa acestui numãr în
lg
matrice, folosind tehnica fanionului. Evaluaþi experimental timpul de
-A execuþie al algoritmului de cãutare, pentru diferite valori ale lui M ºi N,
folosind metoda descrisã în lucrarea 1.
da
an
se scrie programul care cautã societatea a cãrui rulaj este cel mai
.Z
17
e
ar
m
ra
Lucrarea nr. 3
og
Tehnici de sortare directe ale tablourilor
pr
de
1. Scopul lucrãrii – îl reprezintã prezentarea celor mai cunoscute
ci
metode de sortare directe a tablourilor.
ni
eh
2. Aspecte teoretice
it
Metodele directe de sortare, cele mai cunoscute, ºi care realizeazã
is
sortarea in situ sunt urmãtoarele:
itm
- sortarea prin inserþie
- sortarea prin selecþie
or
- sortarea prin interschimbare
lg
Performanþa acestora este O(N2). -A
2.1.Tehnica sortãrii prin inserþie
da
secvenþã destinaþie a1 .... ai-1 ºi respectiv într-o secvenþã sursã ai ... an. La
ar
void sortare_insertie()
{
int i,j,x;
for (i=2;i<=n;i++)
{
x=a[i];
a[0]=x;
j=i-1;
while (a[j]>x)
{
a[j+1]=a[j];
j=j-1;
}
a[j+1]=x;
}
}
18
e
ar
m
ra
Algoritmul de sortare prin inserþie poate fi îmbunãtãþit pornind de la
og
observaþia cã secvenþa destinaþie este deja ordonatã. În acest caz
pr
cãutarea locului de inserare se poate face mai rapid utilizînd cãutarea
de
binarã, prin împãrþiri succesive a intervalului din secvenþa destinaþie.
Aceastã metodã, (o variantã a metodei clasice de inserþie), poartã
ci
denumirea de inserþie binarã.
ni
eh
void insertie_binara()
it
{
is
int i,j,s,d,m,x;
itm
{
for (i=1;i<=n;i++)
or
{
lg
-A x=a[i];
s=0;
d=i-1;
da
while (s<=d)
an
{
ar
y=div((s+d),2);
m=y.quot;
m
}
D
void sortare_selectie()
{
int i,j,k,x;
for (i=1;i<=n-1;i++)
{
k=i;
x=a[i];
for (j=i+1;j<=n;j++)
19
e
ar
m
if (a[j]<x)
ra
{
og
x=a[j];
pr
k=j;
}
de
a[k]=a[i];
ci
a[i]=x;
ni
}
eh
}
it
2.3. Tehnica sortãrii prin interschimbare (bubble-sort)
is
Principiul acestei metode este urmãtorul: se comparã ºi se interschimbã
perechile de elemente alãturate, parcurgînd tabloul de la stînga la
itm
dreapta, pînã cînd toate elementele devin sortate. La fiecare trecere prin
or
tablou se deplaseazã cel mai mic element spre capãtul din stînga al
lg
tabloului. Dacã considerãm tabloul vertical ºi vom asimila elementele
-A
sale cu niºte bule de aer în interiorul unui lichid, fiecare bulã avînd o
“greutate” proporþionalã cu valoarea cheii, atunci fiecare trecere prin
da
void sortare_bubblesort()
{
D
int i,j,x;
for (i=1;i<=n;i++)
{
for (j=n;j>=i;j--)
if (a[j-1]>a[j])
{
x=a[j-1];
a[j-1]=a[j];
a[j]=x;
}
}
}
20
e
ar
m
- la o analizã atentã se poate observa o asimetrie particularã: astfel un
ra
singur element "uºor" plasat la capãtul "greu" este readus la locul sãu
og
într-o singurã trecere, pe cînd un element "greu" plasat la capãtul "uºor"
pr
va fi readus pe locul sãu doar cu cîte o poziþie la fiecare trecere. Aceastã
de
asimetrie sugereazã ca îmbunãtãþire alternarea sensurilor de parcurgere
ale trecerilor consecutive. Algoritmul care conþine aceste îmbunãtãþiri
ci
se numeºte shakersort sau sortare prin amestecare.
ni
eh
void shakersort()
it
{
is
int j,k,l,r,x;
itm
l=2;r=n;k=n;
do
or
{
lg
-A for (j=r;j>=l;j--)
if (a[j-1]>a[j])
{
da
x=a[j-1];a[j-1]=a[j];a[j]=x;
an
k=j;
ar
}
l=k+1;
m
for (j=l;j<=r;j++)
.Z
if (a[j-1]>a[j])
D
{
x=a[j-1];a[j-1]=a[j];a[j]=x;
k=j;
}
r=k-1;
} while (l<=r);
}
21
D
.Z
m
22
ar
an
da
-A
lg
or
itm
is
it
eh
ni
ci
de
pr
og
ra
m
ar
e
e
ar
m
#include <stdio.h>
ra
#include <conio.h>
og
pr
struct bat_struct
{ int lungime;
de
char culoare[15];
ci
}bat[51];
ni
eh
int nr_bete;
it
is
void afis_bete()
{
itm
for (int i=1;i<=nr_bete;i++)
or
printf("\n%s ->
%d",bat[i].culoare,bat[i].lungime);
lg
-A }
void sort_insertie()
da
{
an
int i,j;
ar
bat_struct x;
m
for (i=2;i<=nr_bete;i++)
.Z
{
x=bat[i];
D
j=i-1;
while (bat[j].lungime>x.lungime)
{
bat[j+1]=bat[j];
j--;
}
bat[j+1]=x;
}
}
void main()
{
clrscr();
//citim numarul de bete
do
{
printf("Dati numarul de bete (5..50) = ");
scanf("%d",&nr_bete);
}
while ((nr_bete<5)||(nr_bete>51));
23
e
ar
m
for (int i=1;i<=nr_bete;i++)
ra
{
og
printf("\nDati lungimea batului nr.
pr
%d = ",i);
scanf("%d",&bat[i].lungime);
de
printf("Dati culoarea batului nr. %d = ",i);
ci
scanf("%s",bat[i].culoare);
ni
}
eh
it
//afisam varianta initiala inainte de sortare
is
clrscr();
printf("Betisoarele nesortate sunt:");
itm
afis_bete();
or
//sortam betisoarele
lg
sort_insertie(); -A
//afisa varianta de dupa sortare
da
afis_bete();
ar
getch();
m
}
.Z
D
3. Probleme propuse
24
e
ar
m
ra
2. Se considerã un set de 49 numere. Calculatorul va extrage aleator 6
og
numere din setul de mai sus, ºi va afisa aceste 6 numere în ordine
pr
crescãtoare. Se va folosi sortarea prin selecþie.
de
3. Se considerã un set de n planete, caracterizate prin nume ºi cele 3
ci
coordonate în spatiu : X,Y,Z. Sã se afiºeze cea mai apropiatã pereche de
ni
planete, precum ºi cele mai îndepãrtate douã planete.
eh
it
is
itm
or
lg
-A
da
an
ar
m
.Z
D
25
e
ar
m
Lucrarea nr. 4
ra
Tehnici de sortare avansate ale tablourilor
og
pr
1. Scopul lucrãrii – îl reprezintã prezentarea celor mai cunoscute
de
metode de sortare avansate a tablourilor.
ci
ni
2. Aspecte teoretice
eh
2.1 Tehnica sortãrii prin inserþie cu diminuarea incrementului.
it
Secvenþa de program care urmeazã implementeazã algoritmul pentru o
is
secvenþã de incremenþi descrescãtori formatã din t elemente, h1,h2,......ht
itm
ºi care îndeplinesc condiþia ht=1 ºi hi+1>hi, pentru i>=1.
Fiecare sortare-h este programatã ca o sortare prin inserþie (poate fi
or
folosita si o alta sortare directa la nivelul acestor elemente) utilizînd
lg
tehnica fanionului în vederea simplificãrii condiþiei de terminare a
-A
procesului de cãutare. Deoarece fiecare sortare necesitã propriul sãu
fanion, pentru o cãutare cît mai simplã, tabloul iniþial (A) se extinde în
da
# define n 8
# define h1 9
# define t 4
int a[n+h1+1];
int i;
void shellsort()
{
int i,j,k,s,m,x;
int h[t+1];
h[1]=h1;h[2]=5;h[3]=3;h[4]=1;
for (m=1;m<=t;m++)
26
e
ar
m
{
ra
k=h[m];
og
s=h1-k;
pr
for (i=k+h1+1;i<=n+h1;i++)
{
de
x=a[i];
ci
j=i-k;
ni
if (s==h1) s=h1-k;
eh
s=s+1;
it
a[s]=x;
is
while (x<a[j])
{
itm
a[j+k]=a[j];j=j-k;
or
}
a[j+k]=x;
lg
-A }
}
}
da
an
void main()
ar
{
m
for(i=h1+1;i<=n+h1;i++)
.Z
{
printf("elementul %d = ",i);
D
scanf("%d",&a[i]);
}
shellsort();
for(i=h1+1;i<=n+h1;i++)
{
printf("%d",a[i]);
printf("\n");
}
}
27
e
ar
m
ansamblu deoarece nu existã nici o pereche de indici i ºi j care sã
ra
satisfacã relaþia j=2i (sau j=2i+1). În procesul de construire al unui
og
ansamblu in situ, se ia fiecare element de la jumãtate în jos ºi se
pr
deplaseazã în tablou pînã la locul sãu (se comparã elementul i cu
de
elementele 2i respectiv 2i+1 , adicã cu cel mai mic dintre ele, ºi dacã
elementul i este mai mare decît vreunul dintre acestea, atunci se
ci
schimbã între ele cele douã elemente). Operaþia se repetã pînã cînd nu
ni
se mai face nici o schimbare ºi apoi tot procesul se reia pentru
eh
elementul urmãtor. Deplasarea unui element în tablou poate fi descrisã
it
de functia deplasare prezentatã în continuare:
is
itm
#define TRUE 1
or
#define FALSE 0
lg
void deplasare(int s, int d) -A
{
da
int i,j,ret,x;
an
i=s;j=2*i;x=a[i];ret=FALSE;
ar
{
.Z
if (j<d)
D
if (a[j]>a[j+1]) j=j+1;
if (x>a[j])
{
a[i]=a[j];i=j;j=2*i;
}
else
ret=TRUE;
}
a[i]=x;
}
s=(n/2)+1;
while (s>1)
{
s=s-1;
deplasare(s,n);
28
e
ar
m
}
ra
og
Dupã ce ansamblul a fost generat, în vederea sortãrii elementelor se
pr
executã N paºi de deplasare, dupã fiecare pas selectîndu-se vîrful
de
ansamblului. La fiecare pas se ia ultima componentã a ansamblului ºi se
memoreazã vîrful ansamblului în locul ei. Componenta se lasã apoi sã
ci
alunece în ansamblu, la locul ei (procedura deplasare).
ni
eh
d=n;
it
while (d>1)
is
{
itm
x=a[1];a[1]=a[d];a[d]=x;
d=d-1;
or
y=div(d,2);
lg
-A s=d/2+1;
while (s>1)
{
da
s=s-1;
an
deplasare(s,d);
ar
}
}
m
}
.Z
D
29
e
ar
m
element. Tehnica sortãrii pe bazã de partiþionare este ilustratã de
ra
procedura sortare, care se apeleazã recursiv pe ea însãºi.
og
pr
void sortare(int s,int d)
de
{
int i,j;
ci
element x,w;
ni
i=s;j=d;
eh
x=a[(i+j)/2];
it
do
is
{
itm
while (a[i].cheie<x.cheie) i=i+1;
while (x.cheie<a[j].cheie) j=j-1;
or
if (i<=j)
lg
{
w=a[i];a[i]=a[j];a[j]=w; -A
i=i+1;j=j-1;
da
}
} while (i<=j);
an
if (s<j) sortare(s,j);
ar
if (i<d) sortare(i,d);
m
}
.Z
D
void quicksort()
{
int m=1;
int p=n;
sortare(1,n);
}
#include <stdio.h>
#include <conio.h>
30
D
.Z
m
ar
an
da
-A
lg
or
itm
is
it
eh
ni
ci
de
pr
og
ra
m
ar
e
#include <ctype.h>
#include <string.h>
31
e
ar
m
#define t 4
ra
#define h1 9
og
typedef char cuvant[20];
pr
int h[t+1],nr_cuvinte=0,i;
char propozitia[200];
de
cuvant cuv[21],cuv_work[21+h1];
ci
ni
int exista_deja(char c[20])
eh
it
{for (int i=1;i<=nr_cuvinte-1;i++)
is
if (strcmp(cuv[i],c)==0) return 1;
return 0;
itm
}
or
void get_cuvinte()
lg
{ int cuvant=0,f; -A
char temp[20];
strcpy(temp,"");
da
for (f=0;f<strlen(propozitia);f++)
an
propozitia[f]=toupper(propozitia[f]);
ar
for (f=0;f<strlen(propozitia);f++)
m
{if
.Z
(((propozitia[f]>='A')&&(propozitia[f]<='Z'))||
D
((propozitia[f]>='0')&&(propozitia[f]<='9')))
{if (cuvant==0)
{strcpy(temp,"");
cuvant=1;
}
char lit[2];
lit[0]=propozitia[f];
lit[1]='\0';
strcat(temp,lit);
}
else
if (cuvant)
{cuvant=0;
if (!exista_deja(temp))
{nr_cuvinte++;
strcpy(cuv[nr_cuvinte],temp);
}
}
}
}
32
e
ar
m
void sort_shell()
ra
{ int f,m,k,s,i,j;
og
char x[20];
pr
for (f=1;f<=nr_cuvinte;f++)
strcpy(cuv_work[f+h1],cuv[f]);
de
for (m=1;m<=t;m++)
ci
{ k=h[m];
ni
s=h1-k;
eh
for (i=k+h1+1;i<=nr_cuvinte+h1;i++)
it
{ strcpy(x,cuv_work[i]);
is
j=i-k;
if (s==h1) s=h1-k;
itm
s++;
or
strcpy(cuv_work[s],x);
while (strcmp(x,cuv_work[j])<0)
lg
-A {strcpy(cuv_work[j+k],cuv_work[j]);
j-=k;
}
da
strcpy(cuv_work[j+k],x);
an
}
ar
}
m
for (f=1;f<=nr_cuvinte;f++)
.Z
strcpy(cuv[f],cuv_work[f+h1]);
}
D
void main()
{h[1]=9;h[2]=5;h[3]=3;h[4]=1;
clrscr();
//citim propozitia
printf("Dati propozitia=");
gets(propozitia);
strcat(propozitia," ");
//scoatem cuvintele distincte
get_cuvinte();
//afisam cuvintele nesortate
printf("\n\nCuvintele distincte
nesortate sunt:");
for (i=1;i<=nr_cuvinte;i++)
printf("\n%s",cuv[i]);
33
e
ar
m
sortate sunt:");
ra
for (i=1;i<=nr_cuvinte;i++)
og
printf("\n%s",cuv[i]);
pr
getch();
}
de
ci
3. Probleme propuse
ni
eh
1. Sã se realizeze un program interactiv care implementeazã metodele
it
de sortare avansate prezentate în aceastã lucrare. Programul va prezenta
is
urmãtorul meniu:
itm
- C - iniþializeazã tabloul de sortat cu valori generate aleator
- V - vizualizeazã tablou
or
- R - reiniþializeazã tabloul: vezi lucrarea 3.
lg
- S - sortare shellsort -A
- H - sortare heapsort
- Q- sortare quicksort
da
- X - parasirea programului.
an
34
e
ar
m
Lucrarea nr. 5
ra
Sortarea fiºierelor secvenþiale (externã)
og
pr
1. Scopul lucrãrii – îl reprezintã prezentarea celor mai cunoscute
de
metode de sortare a fiºierelor.
ci
ni
2. Aspecte teoretice
eh
Metodele aplicate la sortarea tablourilor nu pot fi aplicate pentru date
it
care nu încap în memoria calculatorului (internã), dar care pot fi de
is
exemplu memorate pe dispozitive periferice secvenþiale. În acest caz
itm
datele pot fi descrise cu ajutorul unei structuri tip fiºier, avînd drept
caracteristicã esenþialã faptul cã, în fiecare moment este accesibilã doar
or
o singurã componentã. Aceastã restricþie face ca metodele de sortare a
lg
-A fiºierelor sã fie total diferite de cele ale tablourilor.
urmãtoarea:
.Z
#define FALSE 0
#define TRUE 0
#define n 10
int x;
FILE *a, *b, *c;
int p,k,i;
int temp;
35
e
ar
m
void injumatatire(int *nr)
ra
{
og
a=fopen(“…\a.dat”,”r”);
pr
b=fopen(“…\b.dat”,”w”);
c=fopen(“…\c.dat”,”w”);
de
if (!feof(a)) fscanf(a,”%d”,&x);
ci
while (!feof(a))
ni
{
eh
i=0;
it
while ((i<(*nr))&&(!feof(a)))
is
{
fprintf(b,”%d\n”,x);
itm
fscanf(a,”%d”, &x);
or
i++;
}
lg
i=0; -A
while ((i<(*nr))&&(!feof(a)))
{
da
fprintf(c,”%d\n”,x);
an
fscanf(a,”%d”, &x);
ar
i++;
m
}
.Z
}
fclose(a); fclose(b); fclose(c);
D
36
e
ar
m
{
ra
fprintf(a,”%d\n”, x); i++;
og
if feof(b) termb=TRUE;
pr
else
{
de
fscanf(b,”%d”,&x); termb=feof(b);
ci
}
ni
}
eh
else
it
{
is
fprintf(a,”%d\n”, y); j++;
if feof(c) termc=TRUE;
itm
else
or
{
fscanf(c,”%d”,&y); termc=feof(c);
lg
-A }
}
}
da
while ((i<*nr)&&(termb==FALSE))
an
{
ar
fprintf(a,”%d\n”,x); i++;
m
if (feof(b) termb=TRUE;
.Z
else
{
D
fscanf(b,”%d”,&x); termb=feof(b);
}
}
while ((j<*nr)&&(termc==FALSE))
{
fprintf(a,”%d\n”,y); j++;
if (feof(c) termc=TRUE;
else
{
fscanf(c,”%d”,&y); termc=feof(c);
}
}
if ((termb==FALSE)||(termc==FALSE)) k++;
} while ((termb==FALSE)||(termc==FALSE));
fclose(a); fclose(b); fclose(c);
}
void main()
{
a=fopen(“…\a.dat”,”w”);
for (i=1;i<=n;i++)
37
e
ar
m
{
ra
scanf(“%d”, &temp);
og
fprintf(a, ”%d\n”, temp);
pr
}
fclose(a); //crearea fisierului
de
a=fopen(“…\a.dat”,”r”);
ci
for (i=1;i<=n;i++)
ni
{
eh
fscanf(a,“%d”, &temp);
it
printf(”%d\n”, temp);
is
}
fclose(a); //tiparirea fisierului creat
itm
//incepe inteclasarea
or
p=1;
lg
do
{ -A
injumatatire(&p);
da
k=0;
interclasare(&p);
an
p=2*p;
ar
}while (k!=0);
m
a=fopen(“…\a.dat”,”r”);
.Z
for (i=1;i<=n;i++)
D
{
fscanf(a,“%d”, &temp);
printf(”%d\n”, temp);
}
fclose(a); } //tiparirea fisierului sortat
38
e
ar
m
ra
Practic, diferenþa dintre sortarea echilibratã ºi cea naturalã este aceea cã
og
lungimea subssecvenþelor de interclasat nu este constantã (1,2,4,8,
pr
º.a.m.d.) ci variazã, fiind egalã cu lungimea unei monotonii. Deci,
de
sortarea naturalã interclaseazã monotorii. Fiecare trecere constã în douã
faze alternative: defalcare ºi interclasare. În faza de defalcare
ci
monotoriile fiºierului c se defalcã alternativ pe fiºierele a ºi b. În faza
ni
de interclasare se recombinã în c monotoriile de pe fiºierele a ºi b.
eh
Sortarea se terminã în momentul în care numãrul monotoriilor din
it
fiºierul c este 1. Pentru numãrarea monotoriilor se foloseºte variabila
is
l.
itm
Prin aceastã metodã la distribuire se depune fie un numãr egal de
or
monotorii pe fiºierele a respectiv b, fie cu o monotorie mai mult pe
lg
fiºierul a. Dupã interclasarea monotoniilor perechi, monotoria
-A nepereche trebuie recopiatã în c.
da
#include <conio.h>
D
#include <stdio.h>
FILE *sursa,*dest,*a,*b;
char ch;
int file_size=0,a_size=0,b_size=0;
void transfera_sursa_in_destinatie()
{ sursa=fopen("sursa.txt","rt");
dest=fopen("dest.txt","wt");
while (!feof(sursa))
{ ch=fgetc(sursa);
if (!feof(sursa))
{fputc(ch,dest);
file_size++;
}
}
fclose(sursa);
fclose(dest);
}
39
e
ar
m
void split_destinatie(int nr)
ra
{ int ch_scrise=0;
og
dest=fopen("dest.txt","rt");
pr
a=fopen("a.tmp","wt");
b=fopen("b.tmp","wt");
de
a_size=0;b_size=0;
ci
while (ch_scrise<file_size)
ni
{ int i;
eh
for
it
(i=1;((i<=nr)&&(ch_scrise<file_size));i++)
is
{ ch=fgetc(dest);
fputc(ch,a);
itm
a_size++;
or
ch_scrise++;
}
lg
for -A
(i=1;((i<=nr)&&(ch_scrise<file_size));i++)
{ch=fgetc(dest);
da
fputc(ch,b);
an
b_size++;
ar
ch_scrise++;
m
}
.Z
}
fclose(dest);
D
fclose(a);
fclose(b);
}
if (read_next_b)
40
e
ar
m
{cb=fgetc(b);
ra
read_next_b=0;
og
b_citit++;
pr
if (b_citit>b_size) {gata=1;nb=nr+1;}
}
de
ci
if (gata==0)
ni
if (ca<cb)
eh
{
it
fputc(ca,dest);
is
ch_scrise++;read_next_a=1;na++;
}
itm
else
or
{
fputc(cb,dest);
lg
-A ch_scrise++;read_next_b=1;nb++;
}
}
da
while ((na<=nr)&&(nb<=nr));
an
ar
if (nb<=nr)
m
{ fputc(cb,dest);ch_scrise++;
.Z
for
(int i=nb;((i<nr)&&(b_citit<b_size));i++)
D
{ cb=fgetc(b);b_citit++;
fputc(cb,dest);ch_scrise++;
}
}
else
{ fputc(ca,dest);ch_scrise++;
for
(int i=na;((i<nr)&&(a_citit<a_size));i++)
{ ca=fgetc(a);a_citit++;
fputc(ca,dest);ch_scrise++;
}
}
na=1;read_next_a=1;
nb=1;read_next_b=1;
}
fclose(a);
fclose(b);
fclose(dest);
}
41
e
ar
m
void sorteaza_destinatie()
ra
{
og
int n=1;
pr
while (n<=file_size)
{ split_destinatie(n);
de
interclaseaza(n);
ci
n*=2;
ni
}
eh
}
it
is
void main()
{ clrscr();
itm
transfera_sursa_in_destinatie();
or
sorteaza_destinatie();
}
lg
-A
3. Probleme propuse
da
an
42
e
ar
m
Lucrarea nr. 6
ra
Algoritmi recursivi
og
pr
1. Scopul lucrãrii – îl reprezintã prezentarea conceptului de
de
recursivitate precum ºi a cîtorva categorii tipice de algoritmi recursivi.
ci
ni
2.Aspecte teoretice
eh
2.1. Definirea recursivitãþii
it
Recursivitatea presupune de asemenea execuþia repetatã a unei
is
porþiuni de program. În contrast cu iteraþia însã, în cadrul recursivitãþii
itm
condiþia este verificatã în decursul execuþiei programului (nu la sfîrºitul
ei ca la iteraþie) ºi, în caz de rezultat satisfãcãtor, întreaga porþiune de
or
program este apelatã din nou ca subprogram a ei însãºi, în particular ca
lg
-A un subprogram a porþiunii de program originale care însã nu ºi-a
terminat execuþia. În momentul satisfacerii condiþiei de revenire, se reia
execuþia programului apelant exact din punctul din care s-a apelat pe el
da
satisfacerii condiþiei.
ar
43
e
ar
m
apel recursiv al unei subrutine necesitã alocarea unui volum de memorie
ra
(in stiva) destinat obiectelor (variabilelor) sale curente. În plus, alãturi
og
de acestea mai trebuie memoratã ºi starea curentã a programului, cu
pr
scopul de a fi refãcutã atunci cînd noua activitate a subrutinei se terminã
de
ºi urmeazã ca cea veche sã fie reluatã.
Algoritmii recursivi sînt potriviþi a fi utilizaþi atunci cînd problema care
ci
trebuie rezolvatã sau datele care trebuiesc prelucrate sunt definite în
ni
termeni recursivi (algoritmii implementeazã în acest caz definiþia
eh
recursivã). Utilizarea recursivitãþii trebuie însã evitatã ori de cîte ori stã
it
la dispoziþie o rezolvare bazatã pe interaþie. De fapt, implementarea
is
recursivitãþii pe elemente nerecursive dovedeºte faptul cã, practic, orice
itm
algoritm recursiv poate fi transformat într-unul pur interativ. La
transformare putem distinge douã cazuri:
or
a). Cazul în care apelul recursiv al procedurii apare la sfîrºitul ei, drept
lg
-A
ultima instrucþiune a procedurii (tail recursion). În aceastã situaþie,
recursivitatea poate fi înlocuitã cu o buclã simplã de iteraþie. Acest
da
lucru este posibil deoarece, în acest caz, revenirea dintr-un apel încuibat
presupune ºi terminarea instanþei respective a subrutinei, motiv pentru
an
44
e
ar
m
2.2. Tehnica divizãrii (divide and conquer)
ra
Una dintre metodele fundamentale de proiectare a algoritmilor se
og
bazeazã pe tehnica divizãrii (divide and conquer). Principiul de bazã
pr
este acela de a descompune o problemã în mai multe subprobleme a
de
cãror rezolvare este mai simplã ºi din soluþiile cãrora se poate determina
soluþia problemei iniþiale. Acest mod de lucru se repetã recursiv pînã
ci
cînd subproblemele devin banale iar soluþiile lor, evidente. O aplicaþie
ni
tipicã a tehnicii divizãrii are urmãtoarea structurã:
eh
it
procedure rezoiva (x)
is
begin
itm
if x este divizibil in subprobleme then
begin
or
divide pe x in doua sau mai multe parti: x1.....xk;
lg
-A combina cele k solutii partiale intr-o solutie pentru x
end
da
else
rezolva pe x direct
an
end;
ar
m
45
e
ar
m
ra
B. Algoritm pentru determinarea tuturor soluþiilor de ieºire
og
dintr-un labirint
pr
Algoritmul urmãtor presupune un labirint descris cu ajutorul unui
de
tablou tridimensional de caractere de dimensiuni [N+1] x [N+1], în care
cu ajutorul caracterul ‘*’ sunt reprezentaþi pereþii iar cu ajutorul
ci
caracterului ' ' (blanc) culoarele; punctul de start este centrul
ni
labirintului (dar nu este obligatoriu). Cãutarea se executã astfel: dacã
eh
valoarea caracterului din poziþia curentã este ' ', se intrã pe o linie
it
posibilã de ieºire ºi se macheazã poziþia cu caracterul '+'. Dacã s-a ajuns
is
la ieºire, se executã tipãrirea tabloului (labirintul ºi drumul gãsit). În caz
itm
contrar se apeleazã recursiv procedura P pentru cele patru poziþii din
or
vecinãtatea imediatã a poziþiei curente.
Este important sã subliniem cã marcajul de drum se ºterge, de îndatã ce
lg
-A
s-a ajuns la o fundãturã. ªtergerea se executã prin generarea unui
caracter ' ' (blanc) pe poziþia curentã înainte de pãrãsirea procedurii.
da
#include <stdio.h>
an
#include <stdlib.h>
ar
#include <conio.h>
m
#define n 5
.Z
D
char m[n+1][n+1];
int i,j;
div_t a,b;
void tipar()
{
for (i=0;i<=n;i++)
{
printf(" ");
for (j=0;j<=n;j++) printf("%c",m[i][j]);
printf("\n");
}
printf("\n");
}
void p(int x,int y)
{
if (m[x][y]==' ')
{
m[x][y]='+';
a=div(x,n);
b=div(y,n);
46
e
ar
m
if ((a.rem==0) || (b.rem==0))
ra
tipar();
og
else
pr
{
p(x+1,y); p(x,y+1); p(x-1,y); p(x,y-1);
de
}
ci
m[x][y]=' ';
ni
}
eh
}
it
void main()
is
{
for (i=0;i<=n;i++)
itm
for (j=0;j<=n;j++)
or
m[i][j]='*';
for (j=2;j<=5;j++) m[1][j]=' ';
lg
-A m[2][1]=' ';
m[2][2]=' ';
m[2][4]=' ';
da
m[3][2]=' ';
an
m[3][3]=' ';
ar
m[3][4]=' ';
m
m[4][2]=' ';
.Z
m[5][2]=' ';
tipar();
D
a=div(n,2);
p(a.quot,a.quot);
}
#include <conio.h>
#include <stdio.h>
int a[20],n,i;
int pr[20];
void perm(int i)
47
e
ar
m
{ int j,f;
ra
if (i==n+1)
og
{ printf("\n");
pr
for (f=1;f<=n;f++) printf("%d ",pr[f]);
}
de
else
ci
for (j=1;j<=n;j++)
ni
if (pr[j]==-1)
eh
{pr[j]=a[i];
it
perm(i+1);
is
pr[j]=-1;
}
itm
}
or
void main()
lg
{clrscr(); -A
printf("Dati numarul de elemente = ");
scanf("%d",&n);
da
for (i=1;i<=n;i++)
an
{printf("\n\nDati valoarea
ar
elementului %d = ",i);
m
scanf("%d",&a[i]);
.Z
pr[i]=-1;
}
D
perm(1);
getch(); }
3. Probleme propuse
48
e
ar
m
b). tipãrirea nodurilor unei liste în ordine inversã
ra
c). analog cu b), dar pentru elementele unui tablou
og
d). copierea în ordine inversã a liniilor dintr-un fisier text, în altul.
pr
de
5. Sa se generalizeze programul TAIETURA (prezentat în curs) astfel
încît sã se gaseascã toate variantele, precum ºi cea optimã pentru
ci
ni
tãierea unui fir de lungime L în pãrþi de lungimi L1,L2,...,Ln date, în
eh
condiþiile:
it
a). nu existã nici un fel de restricþie în ce priveºte numãrul de bucãþi
is
din fiecare lungime;
itm
b). se taie cel mult o bucatã de fiecare lungime;
c). numãrul de bucãþi de fiecare lungime sã difere prin cel mult o
or
unitate.
lg
-A
da
an
ar
m
.Z
D
49
e
ar
m
Lucrarea nr. 7
ra
Algoritmi recursivi (continuare)
og
pr
1. Scopul lucrãrii – îl reprezintã prezentarea în continuare a altor
de
cîtorva clase tipice de algoritmi recursivi, respectiv algoritmii cu
ci
revenire (backtracking) ºi algoritmii de tipul “înlãþuie ºi limiteazã”
ni
(branch and bound algorithms).
eh
it
2.Aspecte teoretice
is
2.1. Algoritmi cu revenire (backtracking)
itm
Principiul acestei tehnici este aceea de descompunere a obiectivului în
obiective parþiale (task-uri parþiale). De regulã acestea sunt exprimate în
or
mod recursiv ºi constã în exploatarea unui numãr finit de sub-task-uri.
lg
Întregul proces poate fi privit ca un proces de încercare ºi cãutare care
-A
construieºte în mod gradat ºi parcurge în acelaºi timp un arbore de
subprobleme.
da
function incearca;
begin
iniþializeazã selecþia candidatilor;
repeat
selecteazã urmãtorul candidat;
if acceptabil then
begin
înregistreazã-l;
if soluþie incompletã then
begin
încearcã pasul urmãtor;
if nu este reuºit then sterge inregistrarea
end
end
until (pas reuºit) or (nu mai sunt candidaþi)
end;
Acest model principial, general, poate fi însã concretizat în mai multe
forme. Astfel, dacã presupunem cã la fiecare pas numãrul candidatilor
de examinat este fix (M=8 în cazul problemei calului prezentatã la curs)
50
e
ar
m
precum ºi cã procedura este apelatã iniþial prin incearca(i), unde i
ra
reprezintã numãrul mutãrii urmãtoare, rezultã urmãtoarea formã
og
particularã a algoritmului:
pr
de
function incearca (i:integer);
ci
var k: integer;
ni
begin
eh
k:=0;
it
repeat
k:=k+1;{selecteaza al k-lea candidat}
is
if acceptabil then
itm
begin
înregistreazã-l;
or
lg
if i<n_total_solutie_completa then
-A begin
incearca(i+1);
da
if nereusita then
ºterge înregistrarea
an
end
ar
end
m
end;
D
function incearca(i:integer);
var k:integer;
begin
for k:=1 to m do
begin
selecteazã cel de-al i-lea candidat;
if acceptabil then
begin
51
e
ar
m
înregistreazã-l;
ra
if i< n_total_solutie_completa
og
pr
then incearca(i+1)
else tipãreºte soluþia;
de
ºterge înregistrarea
ci
end
ni
end
eh
end;
it
Din cauza simplificãrii la un singur termen a condiþiei de terminare a
is
procesului de selecþie (k=M în acest caz), ciclul repeat a putut fi
itm
înlocuit cu unul for.
or
2.2. Algoritmi pentru realizarea selecþiei optime
lg
Se propune în continuare un algoritm pentru aflarea soluþiei optime din
-A
mai multe soluþii posibile. În acest scop, este necesar sã fie generate
toate soluþiile posibile, ºi pe parcursul procesului de generare, sã fie
da
Astfel, variabila optim înregistreazã cea mai bunã soluþie întîlnitã pînã
ar
function incearca(i:integer);
{încearcã includerea /excluderea celui de-al i-lea obiect}
begin
if incluziunea este acceptabilã then
begin
include cel de-al i-lea obiect;
if i< n_total_solutie_completa
then incearca (i+1)
else verificã optimalitatea;
eliminã cel de-al i-lea obiect;
end
52
e
ar
m
if excluzimea este acceptabilã then
ra
og
if i<n then incearca(i+1)
else verificã optimalitatea
pr
end;
de
Algoritmii care se bazeazã pe tehnica revenirii ºi care utilizeazã un
ci
factor de limitare care restrînge creºterea arborelui potenþial de cãutare
ni
se mai numesc ºi algorimi de tip “înlãþuie ºi limiteazã” (branch and
eh
bound algorithms).
it
is
2.3. Problemã rezolvatã
itm
Se considerã un lac îngheþat de dimensiune NxM. Vom avea o broscuþã
or
la poziþia (1,1) ºi o insectã la poziþia (N,M). Luînd în considerare ca
lg
broscuþa poate sãri doar în L , sã se afle drumul cel mai scurt pe care
-A trebuie broscuþa sa-l parcurgã de la poziþia initialã pînã la insectã.
OBS : în locul de unde a sãrit broscuþa, gheaþa se sparge, aºa cã în acel
da
#include <conio.h>
m
#include <stdio.h>
.Z
struct drum
D
{ int x,y;
} d[100],d_b[100];
int a[50][50],n,m,i,j;
int nr_pasi_b=-1,nr_pasi=0;
void afis_tabla()
{ clrscr();
int i,j;
for (i=3;i<=n+2;i++)
for (j=3;j<=m+2;j++)
{ gotoxy(i-2,j-2);
switch (a[i][j])
{ case -1:printf("°");break;
case 1:printf("ž");break;
case 2:printf("B");break;
case 3:printf("M");break;
}
}
}
53
e
ar
m
void tratare()
ra
{ if ((nr_pasi<nr_pasi_b)||(nr_pasi_b==-1))
og
{ nr_pasi_b=nr_pasi;
pr
for (int i=0;i<nr_pasi;i++)
{d_b[i].x=d[i].x;
de
d_b[i].y=d[i].y;
ci
}
ni
}
eh
}
it
is
void go(int x,int y)
{ if ((x==n+2)&&(y==m+2))
itm
{ d[nr_pasi].x=x;
or
d[nr_pasi].y=y;
nr_pasi++;
lg
tratare(); -A
nr_pasi--;
}
da
else
an
{ d[nr_pasi].x=x;
ar
d[nr_pasi].y=y;
m
nr_pasi++;
.Z
int temp=a[x][y];
a[x][y]=-1;
D
if (a[x-1][y-2]!=-1) go(x-1,y-2);
if (a[x-2][y-1]!=-1) go(x-2,y-1);
if (a[x-2][y+1]!=-1) go(x-2,y+1);
if (a[x-1][y+2]!=-1) go(x-1,y+2);
if (a[x+1][y+2]!=-1) go(x+1,y+2);
if (a[x+2][y+1]!=-1) go(x+2,y+1);
if (a[x+2][y-1]!=-1) go(x+2,y-1);
if (a[x+1][y-2]!=-1) go(x+1,y-2);
a[x][y]=temp;
nr_pasi--;
}
}
void main()
{ clrscr();
printf("Dati N=");scanf("%d",&n);
printf("Dati M=");scanf("%d",&m);
for (i=1;i<=n+4;i++)
for (j=1;j<=m+4;j++)
a[i][j]=-1;
for (i=3;i<=n+2;i++)
54
e
ar
m
for (j=3;j<=m+2;j++)
ra
a[i][j]=1;
og
a[n+2][m+2]=3;
pr
go(3,3);
de
ci
for (i=1;i<=n+4;i++)
ni
for (j=1;j<=m+4;j++)
eh
a[i][j]=-1;
it
for (i=3;i<=n+2;i++)
is
for (j=3;j<=m+2;j++)
a[i][j]=1;
itm
a[n+2][m+2]=3;
or
for (i=0;i<nr_pasi_b;i++)
lg
-A {a[d_b[i].x][d_b[i].y]=2;
afis_tabla();
getch();
da
a[d_b[i].x][d_b[i].y]=-1;
an
}
ar
}
m
.Z
3. Probleme propuse :
D
55
e
ar
m
2. Se considerã un labirint tridimensional de dimensiune NxNxN. Sã se
ra
realizeze programul cu ajutorul cãruia se va calcula un anumit drum
og
prin acest labirint de la o anumitã poziþie de start la o anumitã
pr
destinaþie.
de
3. Numai 12 din cele 92 de soluþii ale problemei celor 8 regine diferã în
ci
mod esenþial, celelalte pot fi deduse din cele 12 prin considerente de
ni
eh
simetrie faþã de axe sau faþã de centru. Sã se scrie programul care
determinã cele 12 soluþii.
it
is
itm
or
lg
-A
da
an
ar
m
.Z
D
56
e
ar
m
Lucrarea nr. 8
ra
Structura de date lista
og
pr
1. Scopul lucrãrii – îl reprezintã prezentarea structurii de date tip listã
de
înlãnþuitã precum ºi a operaþiilor care se realizeazã asupra acesteia.
ci
ni
2.Aspecte teoretice
eh
2.1. Implementarea listelor cu ajutorul tipului pointer
it
În aceastã implementare, o listã liniarã este o structurã dinamicã ºi deci,
is
ea poate fi definitã în termeni recursivi utilizînd urmãtoarele structuri de
itm
date:
or
struct nod {
lg
-A int cheie;
struct nod *urm;
...info;
da
};
an
inceput
D
cheie 1 2 3 Í
urm … Íull
info … … … …
q=(nod*)malloc(sizeof(nod));
q->urm=inceput;
inceput=q;
57
e
ar
m
Inser þia unui nod la începutul listei se prezintã grafic astfel:
ra
og
… …
pr
inceput inceput
de
ci
q->urm=inceput q->urm=inceput
ni
eh
q
it
inceput=q
is
itm
Figura 2: Inserþia unui nod la începutul unei liste
or
lg
Inserþia unui nod la sfirºitul unei liste se poate realiza prin secvenþa
urmãtoare: -A
da
r =(nod*)malloc(sizeof(nod));
an
r->urm=NULL;
sfirsit->urm=r;
ar
sfirsit=r;
m
.Z
sfirsit Null
sfirsit=r
sfirsit->urm=r
r
Null
58
e
ar
m
Pe lîng ã cele douã situaþii prezentate anterior (inserþia unui nod la
ra
începutul unei liste ºi respectiv inserþia unui nod la sfirºitul unei liste),
og
mai rãmîne de arãtat modalitatea de a insera noduri într-o poziþie
pr
oarecare a unei liste. Aici putem avea douã cazuri ºi anume cînd se
de
doreºte ca inserþia noului nod sã aibã loc dupã un anumit nod, indicat de
un pointer p, sau înaintea acestuia.
ci
Astfel, fie p pointerul care indicã un nod al listei iar q o variabilã
ni
pointer ajutatoare, care va indica nodul care se insereazã. Inserþia
eh
acestuia dupã nodul indicat de pointerul p, p->, se realizeazã prin
it
urmatoarea secvenþã:
is
Grafic, inserþia se prezintã în felul urmãtor:
itm
or
lg
q 25 q->urm=p->urm
-A
da
p->urm=q
an
…
ar
10 20 30
…
m
.Z
D
59
e
ar
m
ra
og
q 20
pr
de
*q=*p
ci
…
ni
10 20 30
…
eh
it
is
itm
p
or
Figura 5a: Crearea unui nod ºi copierea vechiului nod peste acesta
lg
-A 20
da
an
p->urm=q
ar
…
m
10 15 30
.Z
…
D
q =(nod*)malloc(sizeof(nod));
*q=*p;
p->urm=q;
r=p->urm;
60
e
ar
m
p->urm=r->urm;
ra
og
În secvenþa de program mai sus se poate evita utilizarea variabilei
pr
pointer r prin înlocuirea fragmentului de mai sus cu instrucþiunea:
de
p->urm=p->urm->urm;
ci
ni
Utilizarea variabilei r are însã avantajul cã, prin intermediul ei,
eh
programatorul poate avea acces ulterior la nodul suprimat, acces care
it
altfel se pierde.
is
Dacã programatorul nu mai are nevoie ulterior de nodul suprimat, se
itm
recomandã utilizarea procedurii standard dispose(r) pentru a
elibera zona de memorie ocupatã de *r.
or
Daca însã se doreºte suprimarea chiar a nodului indicat de pointerul p
lg
-A dintr-o listã, ºi anume chiar nodul *p, observãm cã ºi aici apare aceeaºi
dificultate, generatã de imposibilitatea de a afla simplu adreasa
da
r=p->urm;
*p=*r;
D
free(r);
q=inceput;
while (q!=NULL)
{
prelucrare(*q);
q=q->urm;
}
61
e
ar
m
O opera þie frecvent utilizatã o reprezintã cãutarea unui nod dat într-o
ra
listã: secvenþa prin care se cautã nodul cu cheia x datã într-o listã
og
înlãnþuitã unde inceput este pointerul de început de listã este
pr
urm ãtoarea:
de
#define 0 FALSE
ci
#define 1 TRUE
ni
eh
b=FALSE;
it
q=inceput;
is
while ((q!=NULL) && (b==FALSE))
itm
if (q->cheie==x) b=TRUE;
else q=q->urm;
or
lg
-A
Dacã dupã terminarea secvenþei avem b=true atunci *q este nodul
c ãutat; în caz contrar, nodul nu s-a gãsit ºi avem q=NULL.
da
an
#include <conio.h>
#include <stdio.h>
#include <string.h>
struct student_struct
{ char nume[20];
char prenume[20];
char grupa[6];
student_struct *urm;
};
student_struct *prim,*p,*t;
int o;
void adauga_student()
{ char nume[20],prenume[20],grupa[6];
printf("\n\nAdaugare student");
62
e
ar
m
printf("\n\nDati numele studentului : ");
ra
scanf("%s",nume);
og
printf("Dati prenumele studentului : ");
pr
scanf("%s",prenume);
printf("Dati grupa studentului : ");
de
scanf("%s",grupa);
ci
if (prim==NULL)
ni
//daca nu avem o lista, se creazã primul nod
eh
{ prim=new student_struct;
it
strcpy(prim->nume,nume);
is
strcpy(prim->prenume,prenume);
strcpy(prim->grupa,grupa);
itm
prim->urm=NULL;
or
}
//daca avem deja o lista, adaugam la capat
lg
else
-A { p=prim;
while (p->urm!=NULL) p=p->urm;
da
t=new student_struct;
strcpy(t->nume,nume);
an
strcpy(t->prenume,prenume);
ar
strcpy(t->grupa,grupa);
m
t->urm=NULL;
.Z
p->urm=t;
}
D
void afis_lista()
{printf("\n\nAfisare lista studenti\n\n");
if (prim==NULL)
printf("Nu avem nici un student in lista");
else
{ p=prim;
while (p!=NULL)
{printf("\nNume:%s\nPrenume:%s\nGrupa:%s\n",p-
>nume,p->prenume,p->grupa);
p=p->urm;
}
}
getch();
}
void afis_student()
{char nume[20];
63
e
ar
m
printf("\n\nAfisare student");
ra
printf("\n\nDati numele studentului
og
cautat = ");
pr
scanf("%s",nume);
int gasit=0;
de
p=prim;
ci
while (p!=NULL)
ni
{if (strcmp(p->nume,nume)==0)
eh
{gasit=1;
it
is
printf("\nNume:%s\nPrenume:%s\nGrupa:%s\n",
p->nume,p->prenume,p->grupa);
itm
}
or
p=p->urm;
}
lg
if (!gasit) -A
printf("\nNu am gasit studentul %s.",nume);
getch();
da
}
an
ar
void sterge_student()
m
{ printf("\n\nStergere student");
.Z
char nume[20];
printf("\n\ndati numele studentului ce se
D
sterge : ");
scanf("%s",nume);
p=prim;int gasit=0;
while (p!=NULL)
{if (strcmp(p->nume,nume)==0)
{gasit=1;
printf("\nNume:%s\nPrenume:%s\nGrupa:%s\n",
p->nume,p->prenume,p->grupa);
break;
}
p=p->urm;
}
if (!gasit)
printf("Nu am gasit studentul %s.",nume);
else
{if (p==prim) //daca e tocmai primul din lista
prim=prim->urm;
else
if (p->urm==NULL) //daca e ultimul
64
e
ar
m
{t=prim;
ra
while (t->urm!=p) t=t->urm;
og
delete p;
pr
t->urm=NULL;
}
de
else //daca e undeva prin lista
ci
{t=prim;
ni
while (t->urm!=p) t=t->urm;
eh
t->urm=p->urm;
it
delete p;
is
}
}
itm
getch();
or
}
void main()
lg
-A {do
{clrscr();
printf("Meniu\n");
da
printf("1.Adaugare student\n");
an
printf("2.Stergere student\n");
ar
printf("3.Afisare student\n");
m
printf("5.Iesire\n\n");
printf("Alegeti optiunea : ");scanf("%d",&o);
D
switch (o)
{ case 1:adauga_student();break;
case 2:sterge_student();break;
case 3:afis_student();break;
case 4:afis_lista();break;
}
}
while (o!=5);
}
3. Probleme propuse
65
e
ar
m
2. Se dã o listã înlãnþuitã L care conþine ca ºi elemente ºiruri de
ra
caractere (cuvinte), ºi o altã listã P, care conþine numere întregi în
og
ordine crescãtoare. Sã se scrie funcþia prin care se ºterg din lista L toate
pr
elementele care se aflã pe poziþiile specificate prin numerele din lista P,
de
ca o funcþie cu parametri de tipul sterge(L,P). Funcþia va trata prin
mesaje sugestive situaþiile de eroare.
ci
ni
3. Se considerã douã fraze oarecare, formate din cuvinte separate prin
eh
caracterul spaþiu, virgulã sau punct-virgulã, care se citesc de la
it
tastaturã. Frazele se terminã cu caracterul punct. Sã se scrie un program
is
care, utilizînd o structurã de listã simplu înlãnþuitã pentru memorarea
itm
frazelor, determinã cite cuvinte comune au cele douã fraze.
or
lg
4. Sã se scrie o funcþie care realizeazã operaþia de cut-and-paste a unei
-A
porþiuni dintr-o listã înlãnþuitã L1 în altã listã înlãnþuitã L2, conþinînd
ºiruri de caractere, astfel încît numãrul de mutãri de elemente sã fie cît
da
din a doua lista L2 dupã care se face paste. Sã se scrie un program care
ar
utilizeazã funcþia astfel creatã, ºi care va afiºa cele douã liste în formã
m
66
e
ar
m
ra
Lucrarea nr. 9
og
Liste ordonate. Utilizarea tehnicii fanionului
pr
în structura de listã
de
ci
1. Scopul lucrarii – îl reprezintã prezentarea modalitãþilor de creare a
ni
listelor ordonate precum ºi a utilizãrii tehnicii fanionului în cadrul
eh
structurii de listã înlãnþuitã
it
is
2.Aspecte teoretice
itm
2.1. Crearea unei liste ordonate utilizînd tehnica celor doi pointeri
or
Deosebirea fatã de variantele anterioare de construcþie a listelor
înlãnþuite, constã în aceea cã în acest caz inserþia unui nou nod nu se
lg
-A mai face întotdeauna la începutul listei, sau la sfirºitul ei, ci în acea
poziþie astfel încît lista sã se pãstreze ordonatã.
da
termina cu prima cheie mai mare decat cea cãutatã. Inserþia unui nod
m
lucrarii 8.
O altã tehnicã de inserþie care poate fi utilizatã în acest caz o reprezintã
aºa numita tehnicã a celor doi pointeri. Aceasta se bazeazã pe
utilizarea a doi pointeri, de exemplu q1 ºi q2, care indicã tot timpul
douã noduri consecutive ale listei.
Inceput
Q2 Q1
67
e
ar
m
momentul în care *q1 devine egal cu fanionul (se utilizeazã tehnica
ra
fanionului ºi în acest caz). Dacã în acel moment cheia lui *q1 este strict
og
mai mare decat x sau q1=fanion, atunci înseamnã cã nodul nu existã
pr
de fapt în listã ºi deci trebuie inserat; inserþia noului nod se face
de
întotdeauna între nodurile indicate de cei doi pointeri q1 ºi q2. În caz
contrar, adicã în cazul în care cheia lui q1 este egalã cu x, înseamnã cã
ci
ni
nodul cu cheia x a fost gãsit în cadrul listei ºi atunci, funcþie de
eh
specificul aplicaþiei în care se utilizeazã lista, se va permite sau nu
inserarea de noduri cu aceeaºi cheie.
it
Funcþionarea corectã a algoritmului care utilizeazã tehnica celor doi
is
pointeri presupune existenþa iniþialã în listã a cel puþin douã noduri, deci
itm
cel puþin încã un nod în afara fanionului. Din acest motiv lista se va
or
iniþializa cu douã noduri fictive, astfel:
lg
-A
inceput=(nod *)malloc(sizeof(nod));
fanion=(nod *)malloc(sizeof(nod));
da
inceput->urm=fanion;
an
void insert_ord(int x)
{
struct nod *q1, *q2, *q3;
q2=inceput;
q1=q2->urm;
fanion->cheie=x;
while (q1->cheie<x)
{
q2=q1;
q1=q2->urm;
}
if ((q1->cheie==x) && (q1!=fanion))
q1->numar++;
// contorizeaza numarul de aparitii ale aceleiasi chei
else
{
q3=(nod *)malloc(sizeof(nod));
q3->cheie=x;
q3->numar=1;
68
e
ar
m
q3->urm=q1;
ra
q2->urm=q3;
og
}
pr
}
de
2.2. Cãutarea în listã cu reordonare
ci
Cãutarea în listã cu reordonare reprezintã o metodã de cãutare ºi inserþie
ni
a nodurilor într-o listã înlãnþuitã care asigurã o performanþã mai bunã la
eh
cãutarea ulterioarã a unui nod. Ea constã în aceea cã, ori de cîte ori un
it
identificator se cautã ºi se gãseºte în listã, el se va muta la începutul
is
listei, astfel încît la proxima sa cãutare el va fi gãsit imediat. Cu alte
itm
cuvinte lista se reordoneazã dupã fiecare cãutare finalizatã cu gãsirea
nodului. Dacã un nod nu este gãsit în listã, el se va insera la începutul
or
acesteia.
lg
-A Utilizarea reordonãrii presupune existenþa unui nod fanion. Astfel, în
programul principal sunt necesare în acest caz urmãtoarele iniþializãri:
da
inceput=(nod *)malloc(sizeof(nod));
an
fanion=inceput;
ar
este urmãtoarea:
.Z
D
void caut_reord(int x)
{
struct nod *q1, *q2, *q3;
q1=inceput;
fanion->cheie=x;
if (q1==fanion)
{
inceput=(nod *)malloc(sizeof(nod));
inceput->cheie=x;
inceput->numar=1;
inceput->urm=fanion;
}
else
if (q1->cheie==x) q1->numar++;
else
{
do
{
q2=q1;
q1=q2->urm;
} while (q1->cheie!=x);
69
e
ar
m
if (q1==fanion)
ra
{
og
q2=inceput;
pr
inceput=(nod *)malloc(sizeof(nod));
inceput->cheie=x;
de
inceput->numar=1;
ci
inceput->urm=q2;
ni
}
eh
else
it
{
is
q1->numar++;
q2->urm=q1->urm;
itm
q1->urm=inceput;
or
inceput=q1;
}
lg
} -A
}
da
#include <conio.h>
#include <stdio.h>
#include <string.h>
struct client_struct
{ char nume[20];
char prenume[20];
char adresa[50];
long int suma_cont;
client_struct *urm;
};
client_struct *prim,*p1,*p2,*t;
int o;
70
e
ar
m
void afisare()
ra
{ if (prim==NULL)
og
printf("\n\nNu exita nici un client
pr
in lista");
else
de
{ p1=prim;
ci
while (p1!=NULL)
ni
{printf("\nNume : %s",p1->nume);
eh
printf("\nPrenume : %s",p1->prenume);
it
printf("\nAdresa : %s",p1->adresa);
is
printf("\nCont : %ld$\n",
p1->suma_cont);
itm
p1=p1->urm;
or
}
}
lg
-A getch();
}
da
void introducere()
an
{ char nume[20],prenume[20],adresa[50];
ar
fflush(stdin);
printf("Dati adresa : ");gets(adresa);
printf("Dati valoarea contului : ");
scanf("%ld",&suma_cont);
if (prim==NULL)
{ prim=new client_struct;
strcpy(prim->nume,nume);
strcpy(prim->prenume,prenume);
strcpy(prim->adresa,adresa);
prim->suma_cont=suma_cont;
prim->urm=NULL;
}
else
{t=new client_struct;
strcpy(t->nume,nume);
strcpy(t->prenume,prenume);
strcpy(t->adresa,adresa);
t->suma_cont=suma_cont;
p2=prim;
while((p2->suma_cont>suma_cont)&&(p2!=NULL))
p2=p2->urm;
71
e
ar
m
if (p2==prim)
ra
{t->urm=p2;
og
prim=t;
pr
}
else
de
{p1=prim;
ci
while (p1->urm!=p2) p1=p1->urm;
ni
t->urm=p2;
eh
p1->urm=t;
it
}
is
}
}
itm
or
void main()
{do
lg
{ clrscr(); -A
printf("Meniu\n");
printf("\n1.Introducere client");
da
printf("\n2.Afisare lista");
an
printf("\n3.Iesire");
ar
scanf("%d",&o);
.Z
switch (o)
{case 1:introducere();break;
D
case 2:afisare();break;
}
}
while (o!=3);
}
3. Probleme propuse
72
e
ar
m
Sã se scrie un program care determinã polinomul sumã ºi polinomul
ra
produs a celor douã polinoame citite iniþial. Polinoamele rezultate vor fi
og
afiºate în ordinea descrescãtoare a exponenþilor.
pr
de
3. Sã se realizeze un meniu cu urmãtoarele opþiuni : Adãugare nod,
Stergere nod, Afiºare listã. Sã se scrie un program care, utilizînd
ci
meniul de mai sus, va crea o listã înlãnþuitã cu chei intregi, cu
ni
proprietatea cã valorile de pe poziþiile pare vor fi sortate întotdeauna
eh
crescãtor, iar valorile de pe poziþiile impare vor fi sortate descrescãtor.
it
is
4. Informaþiile despre rezultatele concursului de admitere, la care au
itm
fost admiºi un numãr de N studenþi (N=NO1+NO2+NO3) sunt pãstrate
într-o listã odonatã descrescãtor funcþie de media cu care fiecare student
or
a fost admis. Pentru fiecare student se cunoaºte numele, media ºi
lg
-A secvenþa de opþiuni alese (O1 sau O2 sau O3).Aceastã listã se considerã
data de intrare. Sã se scrie programul care repartizeaza pe opþiuni cei N
da
73
e
ar
m
Lucrarea nr. 10
ra
Liste dublu înlãnþuite
og
pr
1. Scopul lucrãrii – îl reprezintã prezentarea particularitãþilor listelor
de
dublu înlãnþuite prin comparaþie cu cele simplu înlãnþuite.
ci
ni
2. Aspecte teoretice
eh
2.1. Caracteristicile listei dublu înlãnþuite
it
Structura de date tip listã dublu înlãþuitã se poate defini utilizînd
is
urmãtoarele tipuri de date:
itm
struct nod {
or
tipelement element;
lg
struct nod *anterior, *urmator;
-A
};
da
… …
… …
74
e
ar
m
#include <conio.h>
ra
#include <stdio.h>
og
#include <string.h>
pr
struct student_struct
de
{ char nume[20];
ci
char prenume[20];
ni
char grupa[6];
eh
student_struct *urm,*prec;
it
};
is
student_struct *prim,*p,*t;
itm
int o;
or
void adauga_student()
lg
-A { char nume[20],prenume[20],grupa[6];
printf("\n\nAdaugare student");
printf("\n\nDati numele studentului : ");
da
scanf("%s",nume);
an
scanf("%s",prenume);
m
scanf("%s",grupa);
if (prim==NULL) //daca nu avem lista,se creazã primul nod
D
{ prim=new student_struct;
strcpy(prim->nume,nume);
strcpy(prim->prenume,prenume);
strcpy(prim->grupa,grupa);
prim->urm=NULL;
prim->prec=NULL;
}
else
//daca avem deja o lista, adaugam la capat
{ p=prim;
while (p->urm!=NULL) p=p->urm;
t=new student_struct;
strcpy(t->nume,nume);
strcpy(t->prenume,prenume);
strcpy(t->grupa,grupa);
t->urm=NULL;
p->urm=t;
t->prec=p;
}
}
75
e
ar
m
ra
void afis_lista()
og
{printf("\n\nAfisare lista studenti\n\n");
pr
if (prim==NULL)
printf("Nu avem nici un student in lista");
de
else
ci
{ p=prim;
ni
while (p!=NULL)
eh
it
{printf("\nNume:%s\nPrenume:%s\nGrupa:%s\n",
is
p->nume,p->prenume,p->grupa);
p=p->urm;
itm
}
or
}
getch();
lg
} -A
void afis_student()
da
{char nume[20];
an
printf("\n\nAfisare student");
ar
cautat = ");
.Z
scanf("%s",nume);
int gasit=0;
D
p=prim;
while (p!=NULL)
{if (strcmp(p->nume,nume)==0)
{gasit=1;
printf("\nNume:%s\nPrenume:%s\nGrupa:%s\n",
p->nume,p->prenume,p->grupa);
}
p=p->urm;
}
if (!gasit) printf("\nNu am gasit studentul
%s.",nume);
getch();
}
void sterge_student()
{ printf("\n\nStergere student");
char nume[20];
printf("\n\ndati numele studentului ce
se sterge : ");
scanf("%s",nume);
76
e
ar
m
p=prim;int gasit=0;
ra
while (p!=NULL)
og
{if (strcmp(p->nume,nume)==0)
pr
{gasit=1;
printf("\nSterg\nNume:%s\nPrenume:%s\n
de
Grupa:%s\n",p->nume,p->prenume,p->grupa);
ci
break;
ni
}
eh
p=p->urm;
it
}
is
if (!gasit)
printf("Nu am gasit studentul %s.",nume);
itm
else
or
{if (p==prim) //daca e tocmai primul din lista
lg
{prim=prim->urm;
-A prim->prec=NULL;
}
else
da
{t=p->prec;
ar
delete p;
m
t->urm=NULL;
.Z
}
else //daca e undeva prin lista
D
{t=p->prec;
t->urm=p->urm;
p->urm->prec=t;
delete p;
}
}
getch();
}
void main()
{do
{clrscr();
printf("Meniu\n");
printf("1.Adaugare student\n");
printf("2.Stergere student\n");
printf("3.Afisare student\n");
printf("4.Afisare lista studenti\n");
printf("5.Iesire\n\n");
printf("Alegeti optiunea : ");
77
e
ar
m
scanf("%d",&o);
ra
switch (o)
og
{ case 1:adauga_student();break;
pr
case 2:sterge_student();break;
case 3:afis_student();break;
de
case 4:afis_lista();break;
ci
}
ni
}
eh
while (o!=5);
it
}
is
itm
3. Probleme propuse
or
lg
1. a). Realizaþi implementarea operaþiilor de bazã pentru liste dublu
-A
înlãnþuite : inserþie, traversare în ambele sensuri, cãutare, ºtergere.
b). Scrieþi o funcþie care schimbã între ele douã elemente adiacente ale
da
unei liste dublu înlãnþuite. Poate fi realizatã aceastã funcþie numai prin
an
ºtergere.
D
78
e
ar
m
Lucrarea nr. 11
ra
Stive ºi cozi
og
pr
1. Scopul lucrãrii – îl reprezintã prezentarea structurilor de coadã ºi
de
stivã, ca structuri derivate din structura de listã. De asemenea, se
ci
prezintã ºi implementãri alternative ale acestora, în varianta de utilizare
ni
a tablourilor în locul listelor înlãnþuite.
eh
it
2. Aspecte teoretice
is
2.1. Stive
itm
O stivã este un tip special de listã în care toate inserþiile ºi suprimãrile
se executã la un singur capãt care se numeºte vîrful stivei. Din acest
or
motiv, stivele se mai numesc ºi structuri lista de tip LIFO
lg
-A (Last-In-First-Out).
Structura de stivã poate fi consideratã o listã cu un caracter mai special;
în consecinþã, toate implementãrile listelor descrise pînã în prezent sunt
da
..
.
VIRF PRIMUL ELEMENT
AL DOILEA ELEMENT
..
.
LUNGIME_MAXIMÃ ULTIMUL ELEMENT
79
e
ar
m
#include <stdio.h>
ra
#define TRUE 1
og
#define FALSE 0
pr
#define lungime_maxima 10
de
typedef struct{
ci
int virf;
ni
int elemente[lungime_maxima];
eh
}stiva;
it
is
Implementarea celor cinci operaþii de bazã asupra stivelor este
urmãtoarea:
itm
or
int er;
lg
stiva s;
-A
void initilizare()
da
{
s.virf=lungime_maxima;
an
}
ar
m
int stivid(stiva s)
.Z
{
D
tipelement virfst(stiva s)
{
if (stivid(s)==TRUE)
{
er=TRUE;
printf("stiva este vida");
}
else
return s.elemente[s.virf];
}
tipelement pop()
{
if (stivid(s)==TRUE)
{
er=TRUE;
printf("stiva este vida");
80
e
ar
m
}
ra
else
og
{
pr
return s.elemente[s.virf++];
}
de
}
ci
ni
void push(tipelement x)
eh
{
it
if (s.virf==1)
is
{
er=TRUE;
itm
printf("stiva este plina");
or
}
lg
else
-A {
s.virf--;
s.elemente[s.virf]=x;
da
}
an
}
ar
m
.Z
2.2. Cozi
D
#define TRUE 1
#define FALSE 0
struct nod {
tipelement element;
struct nod *urm;
};
Pe baza acestora se poate defini o structurã de tip coadã care constã din
doi pointeri indicînd faþa respectiv spatele unei liste înlãnþuite. Astfel,
se defineºte structura de coadã:
typedef struct{
81
e
ar
m
struct nod *fatza, *spate;
ra
}coada;
og
pr
Primul nod al cozii este un nod fictiv în care cîmpul element este
de
ignorat. Acestã convenþie permite o reprezentare ºi o manipulare mai
uºoarã a cozii vide.
ci
Secvenþele de program care implementeazã cele 5 operaþii de bazã care
ni
se efectueazã asupra cozilor sunt urmatoarele:
eh
it
coada c;
is
int er;
itm
void initializare()
{
or
c.fatza=(nod*)malloc(sizeof(nod));
lg
c.fatza->urm=NULL; -A
c.spate=c.fatza;
}
da
an
int vid(coada c)
{
ar
}
D
tipelement fatza(coada c)
{
if (vid(c)==1)
{
er=TRUE;
printf("coada este vida");
}
else
return c.fatza->urm->element;
}
void adauga(tipelement x)
{
c.spate->urm=(nod*)malloc(sizeof(nod));
c.spate=c.spate->urm;
c.spate->element=x;
c.spate->urm=NULL;
}
82
e
ar
m
void scoate()
ra
{
og
if (vid(c)==TRUE)
pr
{
er=TRUE;
de
printf("coada este vida");
ci
}
ni
else
eh
c.fatza=c.fatza->urm;
it
}
is
O altã variantã de implementare o reprezintã tablourile circulare. Un
itm
tablou circular este un tablou în care prima poziþie urmeazã ultimei
or
pozi þii, dupã cum aratã figura urmãtoare:
lg
-A LUNGIME MAXIMÃ 1
da
2
an
ar
.
.
m
.
.
.
.Z
.
.
.
D
.
.
.
.
C, SPATE C, FATZA
COADA
83
e
ar
m
Pentru sesizarea corectã a cozii vide (ºi respectiv pline), în tablou se vor
ra
introduce doar lungime_maxima-1 elemente (deºi existã
og
lungime_maxima poziþii). Astfel, testul de coadã plina conduce la o
pr
valoare adevaratã dacã c.spate devine egal cu c.fatza dupã douã
de
înaintãri succesive, iar testul de coadã vidã conduce la o valoare
adevãratã dacã c.spate devine egal cu c.fatza dupã înaintarea cu
ci
ni
o poziþie. Structura de date utilizatã în aceastã implementare este:
eh
#define TRUE 1
it
#define FALSE 1
is
#define lungime_maxima 10
itm
typedef struct{
or
tipelement elemente[lungime_maxima];
lg
int fatza, spate; -A
} coada;
da
an
coada c;
.Z
int er;
D
int avanseaza(int i)
{
div_t x;
x = div(i,lungime_maxima);
return x.rem;
}
int vid(coada c)
{
if (avanseaza(c.spate)==c.fatza) return TRUE;
else return FALSE;
}
tipelement fatza(coada c)
{
if (vid(c)==TRUE)
{
er=TRUE;
printf("coada este vida");
}
else
return c.elemente[c.fatza];
84
e
ar
m
}
ra
og
void adauga(tipelement x)
pr
{
de
if (avanseaza(avanseaza(c.spate))==c.fatza)
{
ci
er=TRUE;
ni
printf("coada este plina");
eh
}
it
else
is
{
c.spate=avanseaza(c.spate);
itm
c.elemente[c.spate]=x;
or
}
lg
}
-A
void scoate()
da
{
if (vid(c)==TRUE)
an
{
ar
er=TRUE;
m
}
else
D
c.fatza=avanseaza(c.fatza);
}
int o,i,stiva[100],sp=-1;
//sp=pozitia ultimului element din stiva
void afisare()
{ printf("\n");
if (sp==-1)
printf("Stiva este goala");
else
85
e
ar
m
{printf("Stiva : ");
ra
for (i=0;i<=sp;i++)
og
printf("%d ",stiva[i]);
pr
}
getch();
de
}
ci
ni
void adaugare()
eh
{ if (sp==99)
it
{printf("Stiva este deja plina !!!");
is
getch();
}
itm
else
or
{int valoare;
printf("\nDati numarul de introdus in stiva
lg
: "); -A
scanf("%d",&valoare);
stiva[++sp]=valoare;
da
}
an
}
ar
m
void scoate()
.Z
{if (sp==-1)
printf("Stiva este goala !!!");
D
else
printf("Elementul scos este %d",stiva[sp--
]);
getch();
}
void golire()
{
sp=-1;
}
void main()
{ do
{ clrscr();
printf("Meniu");
printf("\n1.Adaugare in stiva (PUSH)");
printf("\n2.Scoatere din stiva (POP)");
printf("\n3.Golire stiva");
printf("\n4.Afisare stiva");
printf("\n5.Iesire");
printf("\n\nAlegeti optiunea : ");
86
e
ar
m
scanf("%d",&o);
ra
switch (o)
og
{
pr
case 1:adaugare();break;
case 2:scoate();break;
de
case 3:golire();break;
ci
case 4:afisare();break;
ni
}
eh
}
it
while (o!=5);
is
}
itm
or
3. Probleme propuse
lg
-A 1. O listã dublu înlãnþuitã conþine o expresie aritmeticã în notatie
postfix, în care operanzii sunt reprezentaþi prin litere mici, iar operatorii
da
struct nod {
m
int val;
//valoare operand sau 0 dacã e operator
D
2. Este posibil sã fie memorate douã structuri de date de tip stivã într-un
acelaºi tablou, una crescînd de la pozitia 1 spre sfirºit, cealaltã în sens
invers. Se cere sã se redacteze o funcþie generalã push(x,s) unde s
este una sau alta dintre stive. Funcþia va include toate testele de eroare
necesare, care vor fi semnalate prin mesaje corespunzãtoare.
87
e
ar
m
este folosit în comunicarea dintre doua procese, coada fiind de fapt un
ra
buffer de date de la procesul A la procesul B).
og
Lucrarea nr. 12
pr
Tehnica dispersiei
de
ci
1. Scopul lucrãrii – îl reprezintã prezentarea principiului tehnicii
ni
dispersiei precum ºi evidenþierii eficacitãþii utilizãrii acestei tehnici în
eh
procesul de cãutare.
it
is
2. Aspecte teoretice
itm
2.1. Principiul tehnicii dispersiei
Tehnica dispersiei reprezintã o manierã specificã de organizare a unei
or
mulþimi de chei (elemente cu o cheie specificã) astfel încât regãsirea
lg
unei chei sã necesite cât mai puþin efort. Rezolvarea acestei probleme se
-A
reduce la gãsirea unei asocieri specifice H a mulþimii cheilor K cu
mulþimea adreselor A, adicã :
da
an
H:K->A
ar
88
e
ar
m
care se depune la nodul în tabloul dispersat în poziþia T(l) , unde T este
ra
tabloul de dispersie. Dacã în continuare apare o altã cheie k’ care are
og
acelaºi indice asociat l, adicã l=H(k’)=H(k), atunci s-a ajuns la aºa
pr
numita situaþie de coliziune, care se poate rezolva în mai multe moduri,
de
dupã cum se va ilustra ulterior.
Pentru a regãsi un nod cu o cheie k se procedeazã similar, adicã se
ci
calculeazã indicele asociat cheii k de cãutat ºi în tabloul de dispersie la
ni
indicele gãsit ca asociat cheii k se va cãuta cheia. Dacã cheia se va
eh
regãsi cãutarea se va termina, dar dacã nodul cãutat nu se va regãsi
it
atunci se ajunge într-o situaþie de coliziune, situaþie ce se va rezolva la
is
fel ca ºi la introducerea de noduri.
itm
Aplicarea în practicã a tehnicii dispersiei presupune deci rezolvarea a
or
douã probleme ºi anume: definirea funcþiei de dispersie H ºi tratarea
situaþiei de coliziune.
lg
-A
2.2. Determinarea funcþiei de dispersie. Tratarea situaþiei de
da
coliziune
Funcþia de dispersie trebuie sã repartizeze cât mai uniform mulþimea
an
Una dintre funcþiile de dispersie cel mai des utilizate este urmãtoarea:
D
length ( k )
H (k ) = ( ∑ i =1
2 i ord (k[i ]) mod p
89
e
ar
m
nu sã aparþinã tabelului iniþial. În caz cã nu, memoria necesarã se alocã
ra
din aºa numita “zonã de depãºire”. Aceastã metodã, care este deosebit
og
de eficientã, are douã dezavantaje: necesitatea menþinerii unor liste
pr
secundare ºi prelungirea fiecãrui nod cu un spaþiu pentru pointerul
de
necesar înlãnþuirii. In aceastã variantã, algoritmul utilizeazã un tablou
de pointeri ce indicã fiecare spre o listã cu nodurile care sunt asociate
ci
aceleiaºi întrãri din tabloul dispersat.
ni
Algoritmul este urmãtorul:
eh
it
#include<stdio.h>
is
#include<conio.h>
itm
#include<alloc.h>
#include <stdlib.h>
or
lg
#define N 10 -A
typedef struct NOD
{
da
int cheie;
an
};
m
{
D
div_t y;
y=div(x,N);
y.rem;
return y.rem;
}
void TIPLIST (NOD *f) //tiparirei tablou dispersat
{ NOD *b;
b=f;
while (b!=NULL)
{
printf("%d\t",b->cheie);
b=b->urm;
}
printf(" \n ");
}
void main(void)
{
NOD *el; /* el = un element */
NOD a;
int x,i;
char c;
90
e
ar
m
NOD *t[N]; /* tabloul de dispersie*/
ra
for(i=0;i<N;i++) t[i]=NULL;
og
clrscr();
pr
printf(" Introduceti un element
de
intreg \n");
scanf("%d",&x);
ci
while (x!=0)
ni
{
eh
el=(NOD *)malloc(sizeof(NOD));
it
el->cheie=x;
is
i=DISPERSIE(x);
/*pozitia in tablou */
itm
el->urm=t[i];
or
t[i]=el;
lg
printf("Mai doriti sa
-A introduceti alte elmente?y/n");
c=getch();
da
if (c!='y')
{
an
x=0;
ar
}
m
else
.Z
{
printf(" \n
D
91
e
ar
m
Algoritmul are tendinþa de a îngrãmãdi însã nodurile în continuarea
ra
celor existente, datoritã metodei de parcurgere folosite
og
- adresare patraticã: indicele l+r (l = indicele care a provocat
pr
coliziune; r = o valoare care se iniþializeazã pe 1 ºi se
de
incrementeazã cu 2 la fiecare nereuºitã). Un dezavantaj minor al
acestei metode este acela cã se poate întîmpla sã nu se gãseascã nici
ci
un element liber, ajungîndu-se la depãºirea tabloului, cu toate cã în
ni
tabel mai existã elemente libere. Totuºi, probabilitatea ca acest
eh
lucru sã se întîmple este foarte micã, ºi în general metoda este
it
foarte performantã
is
itm
or
2.3. Problemã rezolvatã
Sa se realizeze un program cu ajutorul cãruia se va face dispersia unei
lg
-A
mulþimi de numere date folosindu-se funcþia de dispersie :
k = (x mod n) + 1.
da
an
#include <conio.h>
#include <stdio.h>
ar
m
struct coloana_struct
.Z
{ int linie[100];
D
int nr_linii;
};
coloana_struct coloana[100];
int f,n,o,x;
void afisare_coloana(int c)
{ printf("\nColoana %d : ",c);
int f;
if (coloana[c].nr_linii==0) printf("Goala");
else
for (f=1;f<=coloana[c].nr_linii;f++)
printf("%d ",coloana[c].linie[f]);
}
void afis_coloana()
{ int c;
do
{printf("Dati coloana pe care doriti s-o
afisati [1..%d] = ",n);
scanf("%d",&c);
}
92
e
ar
m
while ((c<1)||(c>n));
ra
afisare_coloana(c);
og
getch();
pr
}
de
void adauga_element()
ci
{ printf("Dati elementul ce doriti sa-l
ni
introduceti : ");
eh
scanf("%d",&x);
it
int poz=(x%n)+1;
is
printf("Elementul %d a fost adaugat la
coloana %d.",x,poz);
itm
or
coloana[poz].linie[++coloana[poz].nr_linii]=x;
getch();
lg
-A }
void main()
da
clrscr();
ar
dispersie : ");
.Z
scanf("%d",&n);
D
do
{ clrscr();
printf("Meniu pt dispersie cu %d
coloane",n);
printf("\n1.Adaugare numar");
printf("\n2.Afisare o anumita coloana");
printf("\n3.Afisare toate coloanele");
printf("\n4.Iesire");
printf("\n\nAlegeti optiunea : ");
scanf("%d",&o);
switch (o)
{case 1:adauga_element();break;
case 2:afis_coloana();break;
case 3:{for (f=1;f<=n;f++)
afisare_coloana(f);
getch();
} break;
}
}
while (o!=4);
}
93
e
ar
m
ra
3. Probleme propuse
og
pr
1. Se cere sã se construiascã o tabelã de dispersie conþinînd
de
identificatorii dintr-un text (aflat într-un fisier), fiecare identificator
avînd asociat ºi contorul de apariþii. Textul este format din propoziþii,
ci
separate prin caracterul punct. Cuvintele sunt la rindul lor separate cu
ni
caracterul spaþiu, virgulã sau punct-virgulã.
eh
it
2. Sa se realizeze un program prin care se va face dispersia unor
is
studenþi pe baza primei litere din nume (adica studenþii al cãror nume
itm
incep cu „A” vor fi introduºi incepind de la prima pozitie a tabloului
or
dispersat, cei cu „B” incepind de la a doua pozitie, º.a.m.d) . Datele
specifice unui student vor fi : nume, grupa, medie. Ca ºi interfaþã se va
lg
-A
folosi un meniu cu urmãtoarele opþiuni: adãugare student, ºtergere
student, cãutare student, afiºarea tuturor studenþilor al cãror nume care
da
poate genera un alt tablou de dispersie, toate cheile din tabloul iniþial
D
copiindu-se in cel de-al doilea, dupã care zona ocupatã de tabloul iniþial
se elibereaza. Aceasta tehnica se numeste tehnica redispersiei
(re-hashing). Sã se scrie un program care implementeazã aceastã
tehnicã.
94