Documente Academic
Documente Profesional
Documente Cultură
#include <stdio.h>
#include <conio.h>
#define MaxS 10
typedef int ElTip; // ElTip - definire tip element din stiva
typedef
struct {
ElTip elem [MaxS];// spatiu pentru elem. stivei
int
sp;
/* index/ referinta la varful stivei (stack pointer) */
} TStiva, *AStiva;
void eroare (char *meserr){
printf("%s\n", meserr);
}
void InitStiva (AStiva rs){
/* initializare stiva, sp nu refera un element (stiva goala) */
rs->sp=-1;
}
int StivaGoala (AStiva rs){
return (rs->sp == -1);
}
int StivaPlina (AStiva rs){
/* stiva este plina, index=MaxS-1, indice maxim,
nu se mai pot introduce elemente in stiva */
return (rs->sp == MaxS-1);
}
void Push (AStiva rs, ElTip el){
/* introduce element in stiva, daca nu este plina */
if (StivaPlina (rs))
eroare ("Stiva Plina !!!");
else {rs->sp=rs->sp+1;
rs->elem[rs->sp]=el;
}
}
ElTip Pop (AStiva rs){
/* extrage element din stiva, daca nu este goala */
ElTip e;
if (StivaGoala (rs))
{eroare ("Stiva Goala !!!");
return 0;
}
else
{e=rs->elem[rs->sp];
rs->sp=rs->sp-1;
return e;
}
}
ElTip CapStiva (AStiva rs){
if (StivaGoala (rs))
{eroare ("Stiva Goala !!!");
return 0;
6
}
else
return rs->elem[rs->sp];
}
void AfisareStiva (AStiva rs){
/* se creaza o copie a stivei curente, pentru a utiliza functiile
definite pentru operare stiva (Pop, CapStiva, StivaGoala) */
TStiva s=*rs;
AStiva pt=&s;
int nvl=0;
/* Numar de valori afisate pe linie */
if (pt->sp == -1)
eroare("Stiva Goala !!!\n");
else
while (!StivaGoala (pt))
{printf ("%d -> ", CapStiva (pt));
Pop (pt);
if (++nvl % 10 == 0)
printf("\n");
}
printf("NULL\n");
}
void Afisare_Poz_sp (AStiva rs){
printf("Referinta (indexul), sp: %d\n", rs->sp);
if (!rs->sp)
printf("\tSTIVA GOALA!!!\n");
else if (rs->sp == MaxS-1)
printf("\tSTIVA PLINA !!! ELIMINATI ...\n");
}
void main (void){
TStiva s, *rs;
char optiune[20];
ElTip n;
clrscr();
printf("Exemplu de stiva realizata utilizand un tablou.\n");
printf("Stiva este alcatuita din numere intregi.\n");
InitStiva (rs);
printf("\nOptiuni Introduc, Extrag, Afis stiva, Cap stiva, Poz sp, Stop: ");
gets(optiune);
while (optiune[0] != '\0' && optiune[0] != 's' && optiune[0] != 'S'){
switch(optiune[0]){
case 'i':
case 'I':if (StivaPlina(rs)){
eroare("\n\tSTIVA PLINA !!! ELIMINATI...\n");
break;}
printf("Introd. nr. (CTRL-Z, Enter-Stop Introd): ");
while (scanf("%d", &n) == 1 && !StivaPlina(rs)){
Push (rs, n);
if (StivaPlina (rs)){
eroare("Stiva PLINA !!! ELIMINATI.....\n");
7
break;
}
printf("Introd. nr. (CTRL-Z, Enter-Stop Introd): ");
}
fflush(stdin);
break;
case 'e':
case 'E': optiune[0]='D';
while ( !StivaGoala (rs) &&
(optiune[0] == 'D' || optiune[0] == 'd') )
{printf ("Se extrage primul element din stiva: %d\n",
CapStiva (rs));
Pop (rs);
if (!StivaGoala (rs)){
printf("Continuati extragerea (Da/Nu): ");
gets(optiune);
fflush(stdin);
}
}
if (StivaGoala (rs))
printf("Stiva Goala !!! INTRODUCETI ELEMENTE \n");
break;
case 'c':
case 'C': if (!StivaGoala (rs))
printf("Afisare element din capul stivei: %d\n",
CapStiva (rs));
else
eroare("Stiva Goala !!!\n");
break;
case 'a':
case 'A':AfisareStiva (rs);
break;
case 'p':
case 'P': Afisare_Poz_sp (rs);
break;
}
printf("\nOptiuni Int, Ext, Afis stiva, Cap stiva, Poz sp, Stop: ");
gets(optiune);
}
}
O coad este o list liniar n care inserrile se fac doar n capul listei (prim), iar extragerile
doar din coada listei (ultim). Cozile se numesc i liste FIFO (First In First Out). O reprezentare
secvenial pentru o coad se obine prin utilizarea unui tablou C[n], pe care l tratm ca i cum ar fi
circular: dup locatia C[n-1] urmeaz locaia C[0]. Dac cei doi indeci (ultim i prim) refer cele dou
locaii, sau dou locaii n aceast ordine (k-1, respectiv k) coada este goal; dac cei doi indeci sunt
egali (k), atunci coada conine un singur element, iar dac ultim=k-2, iar prim=k, atunci coada este
plin.
8
return 0;
}
else
{e=pc->elem[pc->prim];
pc->prim=IncIndexC(pc->prim);
return e;
}
}
ElTip CapCoada (ACoada pc){
if (CoadaGoala (pc))
{eroare ("Coada Goala !!!");
return 0;
}
else
return pc->elem[pc->prim];
}
void AfisareCoada (ACoada pc){
/* se creaza o copie a cozii curente, pentru a utiliza functiile
definite pentru operare coada (ScotElC, CapCoada, CoadaGoala) */
TCoada c=*pc;
ACoada pt=&c;
int nvl=0;
/* Numar de valori afisate pe linie */
if (CoadaGoala(pt))
eroare("Coada Goala !!!\n");
else
while (!CoadaGoala (pt))
{printf ("%d -> ", CapCoada (pt));
ScotElC (pt);
if (++nvl % 10 == 0)
printf("\n");
}
printf("NULL\n");
}
void Afisare_Poz_Prim_Ultim (ACoada pc){
printf("Cei doi indecsi (referinte), prim si ultim: %d(p), %d(u)\n",
pc->prim, pc->ultim);
}
void main (void){
TCoada c, *pc;
char optiune[20];
ElTip n;
clrscr();
printf("Exemplu de coada realizata utilizand un tablou.\n");
printf("Coada este alcatuita din numere intregi.\n");
InitCoada (pc);
printf("\nOptiuni Int, Ext, Afis coada, Cap coada, Poz p/u, Stop: ");
gets(optiune);
while (optiune[0] != '\0' && optiune[0] != 's' && optiune[0] != 'S'){
switch(optiune[0]){
10
case 'i':
case 'I':if (CoadaPlina(pc)){
eroare("\n\tCOADA PLINA !!! ELIMINATI...\n");
break;}
printf("Introd. nr. (CTRL-Z, Enter-Stop Introd): ");
while (scanf("%d", &n) == 1){
AdaugElC (pc, n);
if (CoadaPlina (pc)){
eroare("Coada PLINA !!! ELIMINATI.....\n");
break;
}
printf("Introd. nr. (CTRL-Z, Enter-Stop Introd): ");
}
fflush(stdin);
break;
case 'e':
case 'E': optiune[0]='D';
while ( !CoadaGoala (pc) &&
(optiune[0] == 'D' || optiune[0] == 'd') )
{printf ("Se extrage primul element din coada: %d\n",
CapCoada (pc));
ScotElC (pc);
if (!CoadaGoala (pc)){
printf("Continuati extragerea (Da/Nu): ");
gets(optiune);
fflush(stdin);
}
}
if (CoadaGoala (pc))
printf("Coada Goala !!! INTRODUCETI ELEMENTE \n");
break;
case 'c':
case 'C': if (!CoadaGoala (pc))
printf("Afisare element din capul cozii: %d\n",
CapCoada (pc));
else
eroare("Coada Goala !!!\n");
break;
case 'a':
case 'A':AfisareCoada (pc);
break;
case 'p':
case 'P': Afisare_Poz_Prim_Ultim (pc);
break;
}
printf("\nOptiuni Int, Ext, Afis coada, Cap coada, Poz p/u, Stop: ");
gets(optiune);
}
11
}
Structurile de date utilizate anterior (tablouri, sau structuri, uniuni) nu sunt structuri de date
dinamice, deoarece ramn fixe n ce privete dimensiunea lor pe toat durata lor de via, adic pe
durata execuiei funciei respective. Astfel de exemple sunt tablourile sau structurile, ale cror
dimensiuni pot fi cunoscute de la declararea lor.
Sunt ns situaii n care structurile de date trebuie s se modifice ca form sau ca mrime pe
parcursul programului; astfel de structuri sunt listele, arborii, grafurile i mulimile disjuncte. Deoarece
pentru astfel de structuri numrul de componente i legturile dintre acestea nu sunt dinainte fixate, ele
se creaz dinamic, pe durata execuiei programului.
Variabilele statice sunt toate variabilele declarate n seciunile declarative ale unei funcii sau
ale unui bloc, avnd aceeai durat de via cu cea a funciei sau blocului respectiv n care au fost
declarate.
Variabilele dinamice se genereaz i se distrug dinamic pe parcursul execuiei programului.
Spre deosebire de variabilele statice, variabilele dinamice nu au un nume (identificator), referirea la ele
fcndu-se n mod indirect prin intermediul variabilelor pointer (referin).
O variabil referin (pointer) are ca valoare o adres a unei variabile dinamice. Variabilele
pointer se folosesc pentru a creea variabile dinamice (a aloca memorie pentru acestea) i pentru a le
distruge cnd nu mai sunt necesare (deci pentru a elibera memoria alocat).
O structur de date dinamice este constituit dintr-un numr de elemente referite prin pointeri.
Se pot introduce noi elemente sau suprima cele vechi, n timpul execuiei programului. Adresele reale
ale elementelor nu au importan, deoarece legturiile logice dintre elemente sunt stabilite de pointeri i
nu prin poziia lor relativ din memorie, ca n cazul tablourilor.
Fiecare element al unei structuri dinamice poate conine unul sau mai multe cmpuri cu referire
(pointeri) la alte elemente, care pot fi de orice tip (simplu, tablou, structur sau pointer). De exemplu,
componentele unei listei sunt structuri cu dou cmpuri: o valoare de tip ntreg i un pointer la
urmtoarea component:
struct intrare
{
int valoare ;
struct intrare *urmator ;
};
Crearea efectiv a variabilei dinamice, deci alocarea de memorie, se face utiliznd una dintre
funciile malloc(), calloc() sau new (C++). Distrugerea unei variabile dinamice, deci eliberarea
spaiului de memorie ocupat de aceasta, se realizeaz cu una dintre funciile free() sau delete (C++).
Aceste funcii sunt definite dup cum urmeaz.
void *malloc(size_t dimensiune), aloc un spaiu continuu de dimensiune octei, returnnd un pointer
de tipul void la nceputul blocului alocat, dac s-a realizat alocarea cu succes, sau pointerul NULL dac
nu se poate aloca memorie;
void *calloc (size_t n , size_t dim), aloc spaiu continuu pentru n elemente de date, fiecare necesitnd
dim octei. Spaiul alocat este iniializat cu zero. Dac s-a alocat, returneaz un pointer la nceputul
spaiului alocat, altfel returneaz pointerul NULL.
Cnd se termin operarea cu memoria ce a fost alocat dinamic, cu funciile malloc() sau calloc(),
spaiul de memorie poate fi eliberat (cedat sistemului) prin aplicarea funciei free():
void *free (void *pointer), elibereaz blocul de memorie referit de pointer, ce a fost alocat de una
dintre funciile calloc( ) sau malloc( ).
Aceste funcii se afl n fiierul antet standard <stdlib.h> (i <alloc.h>).
12
n C++ pentru operaii de alocare / eliberare de memorie se utilizeaz operatorii (funciile) new
i delete.
Operatorul new ncearc s creeze un obiect de tipul specificat (nume) prin alocarea, dac e
posibil, de sizeof (nume) octei n memoria disponibil (denumit heap). Durata de alocare e pn la
eliberare, cu operatorul delete sau pn la sfritul programului.
Dac alocarea se face cu succes, new returneaz un pointer ctre aceast zon, altfel un pointer
nul. Trebuie testat pointerul returnat, la fel ca la funcia malloc. Dar, spre deosebire de aceasta, new
calculeaz dimensiunea spaiului de memorie necesar (sizeof(nume)) fr a fi nevoie de utilizarea
explicit a operatorului. De asemenea pointerul returnat este de tipul corect, pointer la nume, fr s
fie necesar prezena operatorului de conversie: (nume *)malloc( sizeof( nume)). Sintaxa sa de utilizare
este:
ptr_nume = new nume;
Respectiv
delete ptr_nume;
Utilizarea variabilelor dinamice asigur o administrare eficient a spaiului de memorie.
care pornete de la o singur variabil, pointerul primul i trei elemente de tip st_litera. ntr-o lista de
tip stiv elementele se pot extrage n ordinea invers introducerii n list. S vedem cum se poate creea
prin program o astfel de structur. n primul rnd declaraiile pentru o astfel de structur vor fi:
struct st_litera
{
char car;
struct litera *urmator;
};
struct st_litera *vr, *primul;
Iniial lista este vid, deci putem scrie:
primul = NULL;
n continuare este nevoie s inserm un element n aceast list. n primul rnd trebuie creat
acest element:
vr = (struct st_litera *) malloc ( sizeof ( struct st_litera));
primul
vr
primul
vr
Z
vr
NULL
Z
Valoarea curent pentru primul este NULL i efectul acestei instruciuni este de a pune NULL la
vr -> urmator. Pentru ca variabila primul s refere primul element din list vom realiza atribuirea:
primul = vr;/* acum i variabila primul va referi adresa referit de vr */
vr
NULL
Z
primul
Pentru a insera un nou element (caracterul Y) n list vom proceda n aceeai manier:
vr = (struct st_lista *) malloc ( sizeof ( struct st_lista ));
vr -> car = Y;
vr -> urmator = primul;
i se obine:
primul
vr
NULL
Y
vr
NULL
Y
n mod asemntor se introduce n lista, de tip stiv, i elementul urmtor pentru a obine lista
iniial, prezentat la nceputul paragrafului.
S utlizm, n continuare, aceast tehnic de inserare pentru a crea o list nlnuit cu o
structur de tip stiv. Vom scrie programul pentru citirea unui ir de caractere, memorarea lor ntr-o
astfel de list i tiprirea lor n ordine invers.
/* stivacar.c - programul construieste o stiva de caractere, pe care apoi o tipareste */
14
#include <stdio.h>
struct st_lista
{
char car;
struct st_lista *urmator;
};
void main()
{
struct st_lista *vr, *primul;
char c;
primul = NULL;
printf("\nProgramul construieste si afiseaza o stiva de caractere.\n");
printf("Introduceti caractere (CTRL-Z, Enter-pentru sfarsit):\n");
fflush(stdin);
while ((c=getchar()) != EOF)
if (c!='\n')
{vr=(struct st_lista *) malloc(sizeof(struct st_lista));
if (vr == NULL)
{ /* daca pointerul returnat de functia malloc() este
NULL */
printf("Memorie plina !!!!!!\n"); /* se tipareste un
mesaj de eroare */
break;
/* si se termina executia ciclului de citire
caractere */
}
vr->car=c;
vr->urmator=primul;
primul=vr;
}
vr=primul; /* initializare pointer curent, vr, pentru a referi
primul caracter */
while (vr != NULL)
{
printf("%c -> ", vr->car);
vr=vr->urmator;
}
printf(" NULL");/* tiparire sfarsit lista caractere sau lista vida daca
primul era NULL */
}
S considerm acum aceeai problem, dar cu condiia de a introduce n list i de a tipri
numai caracterele distincte. Pentru aceast situie vom defini o funcie ntreag, pe care o vom numi
distinct, care va returna valoarea 1 (adevrat) dac caracterul este diferit de restul listei (deci nu apare
ntre caracterele introduse n list pn n acel moment) i va returna 0 (fals) dac caracterul este gsit
n list.
/* stivcdif.c - programul construieste o stiva de caractere distincte, pe care apoi o tipareste */
#include <stdio.h>
15
struct st_lista
{
char car;
struct st_lista *urmator;
};
void main()
{
struct st_lista *vr, *primul;
char c;
int distinct (struct st_lista *prim, char ch);
primul = NULL;
printf("\nProgramul construieste si afiseaza o stiva de caractere.\n");
printf("Introduceti caractere (CTRL-Z, Enter-pentru sfarsit):\n");
fflush(stdin);
while ((c=getchar()) != EOF)
if (c != '\n' && (distinct(primul, c)))
{
vr=(struct st_lista *) malloc(sizeof(struct st_lista));
if (vr == NULL)
{
/* daca pointerul returnat de functia
malloc() este NULL */
printf("Memorie plina !!!!!!\n"); /* mesaj de
eroare */
break; /* si se termina executia ciclului de citire
caractere */
}
vr->car=c;
vr->urmator=primul;
primul=vr;
}
vr=primul;
/* initializare pointer curent, vr, pentru a referi
primul caracter */
while (vr != NULL)
{
printf("%c -> ", vr->car);
vr=vr->urmator;
}
printf(" NULL");/* tiparire sfarsit lista caractere sau lista vida daca
primul era NULL */
}
int distinct (struct st_lista *prim, char ch)
{
struct st_lista *ptr;
int gasit;
ptr=prim;
/* initializare pointer curent cu primul din lista */
gasit=0;
/* presupunem ca nu l-am gasit inca */
while (ptr != NULL && !gasit)
/* cat timp nu s-a terminat lista si nu
l-am gasit */
{
/* se continua parcurgerea listei */
16
gasit=(ptr->car == ch);
ptr=ptr->urmator;
}
return (!gasit);
}
S considerm acum un alt tip de list nlnuit, i anume una de tip FIFO (First In First Out),
adic primul intrat n list este i primul care poate fi extras. O astfel de structur mai este denumit i
coad sau coad (ir) de ateptare, ntruct elementele se introduc pe la un capt i sunt accesibile pe la
cellalt capt, deci la un moment dat este accesibil numai prima component introdus n list.
Vom considera de aceast dat problema construirii unei liste nlnuit format din cuvinte i
apoi tiprirea acestor cuvinte n ordinea n care au fost introduse. Vom defini dou funcii: una pentru
ataarea unui element (cuvnt) la sfritul listei, iar cea de-a doua pentru extragerea, adic afiarea
cuvintelor din lista de tip coad, cu eliberarea spaiului de memorie alocat dinamic pentru aceast
structur.
/* coadacuv.c - programul construieste o coada de cuvinte,
pe care o afiseaza, la terminarea introducerii de cuvinte,
cu eliberarea spatiului alocat pentru aceasta structura */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define SIR_VID ""
#define DIMCV 25
struct st_cuvant
{
char *cuvant;
/* pointer la sirul de caractere (cuvant) */
struct st_cuvant *urmator;
};
int ncl=0;
/* numar de cuvinte afisate pe linie */
struct st_cuvant * atasare_coada (struct st_cuvant *ultim, char *sirc)
{
struct st_cuvant *vr;
vr=(struct st_cuvant *) malloc(sizeof(struct st_cuvant));
if (vr) /* daca s-a putut crea un pointer se continua atasarea */
{
vr->cuvant=strdup(sirc);
vr->urmator=NULL;
ultim->urmator=vr;
}
return (vr);
}
void extragere ( struct st_cuvant * primu)
{
struct st_cuvant *ptr;
while (primu)
{ptr=primu; /* memorat pentru a elibera spatiul referit de el */
17
element, ci numai la succesorul su. Aceast problem se poate rezolva n dou moduri: fie prin
parcurgerea listei de la nceput i determinarea poziiei unde se face inserarea, fie prin inserarea dup
elementul respectiv (curent), urmat de interschimbarea coninuturilor celor dou variabile, mai puin a
legturilor (referinelor).
n primul caz funcia pentru inseare va fi urmtoarea:
void insereaza_inainte (struct st_orice_tip *curent, struct st_orice_tip *nou,
struct st_orice_tip *primul)
{
/* se insereaza elementul nou in fata celui curent */
struct st_orice_tip aici; /* va reprezenta locul unde se insereaza nou */
if ( curent == primul ) /* testeaza daca trebuie inserat in fata primului element */
{
/* daca da, se pune primul in lista */
nou -> urmator = curent;
/* deci curent devine primul */
primul = curent;
}
else
{/* daca nu, i se determina pozitia de inserare in lista */
aici = primul;
/* care va fi intre aici si curent */
while ( aici -> urmator != curent )
aici = aici -> urmator; /* aici va fi predecesorul lui curent*/
aici -> urmator = nou;
/* si se insereaza intre cei doi pointeri */
nou -> urmator = curent;
}
}
Utiliznd cea de-a doua metod, rezut urmtorea funcie:
void insereaza_inainte (struct st_orice_tip *curent, struct st_orice_tip *nou)
{
/* se insereaza elementul nou in fata celui curent , prin inserarea
lui nou dupa curent si apoi se inverseaza valorile referite de acesti
doi pointeri, astfel incat valorea lui nou sa fie in fata lui curent */
tip_info val_info; /* variabila de acelasi tip cu tipul valorilor referite de pointeri */
nou -> urmator = curent -> urmator; /* se insereaza nou dupa curent */
curent -> urmator = nou;
val_info = curent -> info; /* dupa care se interschimba valrile referite de cei */
curent -> info = nou -> info; /* doi pointeri, astfel ca in lista, valorile sa fie in */
nou -> info = val_info;
/* ordinea dorita */
}
Dac lista este foarte mare, cea de-a doua funcie este mai bun, deoarece nu parcurge lista,
ceea ce ar lua destul timp. Dac, ns, lista este scurt, dar fiecare element are asociat mult
informaie, adic fiecare component este destul de mare, este preferabil prima funcie.
Cea de-a doua metod nu poate fi folosit dac structurile asociate componentelor conin i alte
variabile referin (pointeri).
Operaia invers celei de inserare este cea de eliminare din list a unui element. Eliminarea
elementului curent, chiar dac este primul din list, se face astfel:
void eliminare_elem_curent (struct st_orice_tip *curent, struct st_orice_tip *primul)
{
struct st_orice_tip *elimin; /* pointer local la elementul de eliminat (curent)*/
if ( curent == primul )
/* daca trebuie eliminat chiar 'primul' */
19
else
anterior->next=vr; /* este pus dupa 'anterior' */
return (primul);
}
}
Tratarea recursiv a unei liste
Lista este un exemplu de structur de date recursiv (n exemplul anterior, struct st_numar),
ntruct un cmp al ei (next) face referire la definiia structurii ( struct st_numar * ).
n al doilea rnd o list nlnuit poate fi definit recursiv, astfel: lista poate fi vid sau poate
consta dintr-un element ce conine o referin la list. O alt caracteristic de recursivitate a unei liste
este aceea c se pot scrie funcii recursive pentru tratarea listelor. n acest caz corpul funciei conine, n
principiu, definiia anterioar i o instruciune if, care este necesar pentru a separa tratarea operaiilor
necesare unei liste vide, de cealalt situaie corespunztoare tratrii unui singur element al listei i apoi
apelul recursiv al funciei pentru tratarea restului listei. Vom relua problema citirii unui ir de cuvinte
(un text) i afiarea lor n ordinea citirii, utiliznd pentru cele dou operaii, ataare element n list i
afiare un element din list, dou funcii recursive.
/* listrec1.cpp - programul construieste o lista de cuvinte, introduse
de la tastatura, pe care apoi le afiseaza, utilizand functii recursive:
atsare - pentru construire lista, si
afisare - pentru afisare lista;
Varianta 2: elementul din lista contine adresa cuvantului citit,
pentru care se aloca spatiu (strdup) la citire; */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define SIR_VID ""
#define DIMC 25
int nvl=0;
struct st_cuvant
{
char *cuvant; /* pointer la sirul de caractere (cuvant) */
struct st_cuvant *urmator;
};
int atasare (struct st_cuvant * vr)
{ /* citeste un cuvant, creaza o variabila dinamica vr si o ataseaza la lista */
char cuv[DIMC], *pc;
if (vr->urmator==NULL) /* daca referinta este NULL se ceaza un pointer */
{
vr->urmator=(struct st_cuvant *)malloc(sizeof(struct st_cuvant));
if (vr->urmator==NULL)
return (0);
else
{
gets(cuv);
if (!strcmp(cuv,SIR_VID))
22
{vr->urmator=NULL;
return(0);
}
else
{if(!(pc=strdup(cuv)))
{vr->urmator=NULL;
return(0);
}
else
{
vr=vr->urmator;
vr->urmator = NULL;
vr->cuvant=pc;
return (1);
}
}
}
}
else
}
void afisare (struct st_cuvant * curent)
{
if (curent)
{
printf ("%s -> ", curent -> cuvant);
nvl++;
if(nvl%5==0)
printf("\n");
afisare (curent -> urmator);
}
}
void main()
{
struct st_cuvant *primul;
char c[DIMC];
clrscr();
printf("Programul construieste o lista de tip coada cu cuvinte de la"
" tastatura\n");
printf("Programul ia sfarsit cu un cuvant vid, adica RETURN,"
" la inceput de linie.\n");
gets(c);
primul->cuvant=strdup(c);
primul->urmator=NULL;
while (atasare(primul));
afisare (primul);
printf("NULL\n");
}
23
Alte operaii uzuale asupra unei liste simplu nlnuite, de tip coad sau stiv, sunt descrise n
continuare.
Determinarea lungimii unei liste.
typedef struct st_tip_info TINF;
typedef struct st_lista
{ TINF info;
struct st_lista* urm;
} TEl, * TLista, ** ALista; /* tipurile: element lista, lista si adresa lista*/
size_t LungL (TLista L)
{if (!L)
return (0);
return (1+LungL(L->urm));
}
Afiarea coninutului unei liste n ordine invers:
void AfisInv (TLista L)
{ if (!L)
return;
AfiInv (L->urm);
printf ("%s, ", L->info);
/* am presupus ca lista contine cuvinte*/
}
Cutarea unui element n list, care ndeplinete o anumit condiie descris de funcia de
prelucrare (f, de tipul TFPrelEl) care returneaz o anumit valoare transmis ca parametru (Gasit).
TLista PrimEl (TLista L, TFPrelEl f, int Gasit)
/* intoarce adresa primului gasit sau NULL */
{ for (; L != NULL; L = L->urm)
if ( f (L->info) == Gasit )
return L;
return NULL;
}
TLista UltimEl (TLista L, TFPrelEL f, int Gasit)
/* intoarce adresa ultimului gasit sau NULL */
{ TLista ultim = NULL;
for (; L != NULL; L = L->urm)
if ( f (L->info) == Gasit )
ultim = L;
return u;
}
Prototipul funciei transmis ca parametru (pointer la funcie) este urmtorul:
typedef int (* TFPrelEl)(const void *p,...); /* pointer la functie de prelucrare element
lista - p - adresa element -*/
Cutarea unei legturi (adrese) spre o celul:
ALista CautaEl (ALista aL, TFPrelEl f, int Gasit)
24
/* intoarce adresa legaturii spre celula functia primeste adresa unei celule */
{ TLista a = *aL, p = NULL; /* elementul actual, predecesor */
while (a)
{ if (f(&a->info) == Gasit)
break;
p = a; a = p-> urm;
}
if (!p)
return aL;
/* oprire la inceput */
return &(p->urm);
/* oprire in interior */
}
curent=curent->urm;
}
return (numar);
}
Se poate constata c listele circulare dublu nlnuite sunt mai uor de prelucrat (operaii de
eliminare, inserare etc.) dect listele simplu nlnuite, deoarece pentru operaii simple nu este necesar
o parcurgere prealabil. n schimb listele circulare sunt mai voluminoase (deoarece fiecare eleement
conine o referin n plus) i gestionarea lor este mai lent deoarece ea trebuie s actualizeze mai multe
referine (pointeri).
1.3. Arbori
Aceste structuri de tip arbore sunt foarte des ntlnite:
- structura informaiei nregistrat pe un suport magnetic (disc), cu directoare i
subdirectoare, este un arbore generalizat (sau multici, ntruct are mai muli descendeni,
subarbori);
- cuprinsul unei cri (arbore multici);
- organizarea administrativ a societilor comerciale, universiti, etc.;
- structura aparatelor de orice fel (acestea au uniti funcionale, compuse la randul lor din
subansamble);
- programul meciurilor dintr-un turneu eliminatoriu (arbore binar);
- arborele unei expresii aritmetice (arbore binar);
Variabilele referin pot fi folosite pentru reprezentarea de structuri mai generale dect listele.
Astfel pot fi reprezentate grafurile orientate: nodurile grafului sunt reprezentate prin structuri iar arcele
sunt reprezentate prin referine. un caz particula al grafurilor l reprezint arborii. Un arbore este format
dintr-un nod radacin, cruia ii este ataat un numar finit de subarbori. Orice nod dintr-un arbore poate
fi privit ca rdacina a unui (sub)arbore.
Arborele are dou proprieti: substructurile legate la orice nod sunt distincte i exist un nod
numit rdcin din care este accesibil orice nod parcurgnd un numr finit de arce.
Dac un nod a conine o referin la un alt nod b, atunci a este ascendentul lui b, iar b
descendentul lui a. Nodul rdcin nu are ascendeni, iar nodurile terminale, numite i frunze, nu au
descendeni.
Ca i o list un arbore este o structur recursiv, ce poate fi definit astfel: un arbore este fie vid,
fie constituit dintr-un nod ce conine referine la arbori disjunci. Iat un exemplu de arbore.
27
Un arbore multici (denumit i arbore generalizat) este un arbore n care nodurile pot avea mai
mult de 2 subarbori; orice arbore multici poate fi transformat n arbore binar.
Un caz particular al arborilor l constituie arborele binar, caracterizat prin faptul c fiecare nod
are cel mult doi descendeni, deci are referine la cel mult doi subarbori. Exemple de arbori binari sunt:
- arborele genealogic, avnd ca nod rdcin om persoan, iar ca noduri descendente cei doi prini,
nodurile descendente ale acestora cei patru bunici etc.
- rezultatele unui turneu eliminator se reprezint tot printr-un arbore binar, avnd ca nod rdcin pe
ctigtor, nodurile descendente ale acestuia pe cei doi finaliti, urmtoarele noduri descendente
sunt cei patru semifinaliti etc.
ntr-un arbore binar fiecare nod se reprezint printr-o structur avnd minim trei cmpuri: un cmp ce
conine informaia din nod i cele dou referine la cei doi subarbori, notai stng (st) i drept (dr).
struct st_arbore
{
tip_informatie info;
struct st_arbore *st, *dr;
}TNod, *AArb;
Parcurgerea unui arbore binar poate fi definit recursiv pentru fiecare nod al acestuia:
- prelucrarea informaiei din nod;
- parcurgerea subarborele stng;
- parcurgerea subarborele drept;
Modificnd ordinea acestor operaii se pot defini 6 moduri diferite de parcurgere a unui arbore. Dac
ns impunem ca parcurgerea s nceap ntotdeauna cu subarborele stng naintea celui drept rmn
numai 3 moduri de parcurgere (n adncime):
- parcurgere n preordine, denumit i rdcin-stng-dreapta RSD, care prelucreaz mai nti
informaia din nod i apoi parcurge subarborii (bineneles n ordinea stnga-dreapta);
- parcurgere n inordine, sau SRD, n care ordinea este: parcurgere subarbore stng, prelucreaz
nodul, parcuregere subarbore stng;
- parcurgere n postordine, sau SDR, n care ordinea este: parcurgere subarbori dreapta-stnga,
prelucreaz informaia din nod.
S considerm urmtoarea structur de arbore binar, pentru a exemplifica cele trei moduri de
parcurgere (n adncime) a unui astfel de arbore.
28
E B A D C F H G I
- inordine (SRD)
A B C D E F G H I
- postordine (SDR)
A C D B G I H F E
preordine (RSD): + ( * ( a , + ( b , c ) ) , / ( d , e ) )
inordine (SRD): ( ( a * ( b + c ) ) + ( d / e ) )
postordine (SDR): ( ( a , ( b , c ) + ) * , ( d , e ) / ) +
29
Expresiile aritmetice pot fi reprezentate utiliznd arbori binari, respectnd urmtoarele reguli:
- fiecare operaie aritmetic corespunde unui nod neterminal, care conine operaia respectiv,
iar subarborii si reprezint operanzii si, n ordinea, primul operand este cel din stnga i
apoi cel din dreapta;
- fiecare nod terminal conine o variabil sau o constant;
- rdcina conine ultima operaie ce se efectueaz.
Parcurgerea arborelui n postordine va furniza forma polonez asociat unei expresii aritmetice,
ca n exemplul prezentat anterior.
Se pot descrie funcii recursive de parcurgere a unui arbore binar pentru oricare dintre cele trei
moduri. Iat, de exemplu, funcia pentru pacurgerea n preordine (RSD).
void preordine (AArb arbore)
{
if (arbore != NULL)
{
prelucreaza ( arbore);
preordine (arbore -> st);
preordine (arbore -> dr)
}
}
Modificnd ordinea operaiilor se pot obine i celelate dou modri de parcugere. Vom descrie
n continuare alte funcii des utilizate n aceste aplicaii. Prima este o funcie de inserare a unei valori
printre valorile unui arbore binar ordonat (sau de cutare, cum mai este denumit); informaiile asociate
nodurilor pot fi numere, scalari, iruri de carctere ordonate alfabetic sau alte structuri ordonate dup o
anumit cheie. Inserarea unei noi valori presupune adugarea unui nod frunz la arbore. Locul de
inserare depinde de poziia relativ a informaiei acestui nod fa de informaiile deja existente n
arbore. Dac arborele este NULL nseamn c am gsit poziia corespunztoare pentru noul nod, altfel
se compar informaia de inserat cu informaiile referite de arbore: dac sunt mai mici se continu
cutarea n subarborele stng, dac sunt mai mari se continu cutarea n subarborele dreapta.
int inserare (AArb arbore; tip_informatie val)
{
if ( val < arbore -> info )
if (arbore->st)
/* exista aceasta ramura ? */
inserare ( arbore -> st , val ); /* exista, se continua cautarea pe ramura stanga */
else
{
/* se insereaza aici, intrucat ramura nu exista; se creaza nodul */
arbore->st = ( struct st_arbore ) malloc ( struct st_arbore);
if ( arbore->st == NULL)
/* nu mai este memorie disponibila */
return ( 0 );
else
{arbore=arbore->st; /* daca nu este NULL se asociaza valoarea */
arbore -> st = NULL; /* si se initializeaza referintele */
arbore -> dr = NULL;/* stanga si dreapta cu NULL */
arbore -> info = val;
return ( 1 );
}
}
30
else
{
arb->st=(struct st_arbore *) malloc (sizeof(struct st_arbore)) ;
arb=arb->st;
arb->cuvant=cuv;
arb->nrap=1;
arb->st=NULL;
arb->dr=NULL;
}
else if (strcmp(cuv, arb->cuvant) >0 )
if(arb->dr)
actualizare (arb->dr, cuv);
else {
arb->dr=(struct st_arbore *) malloc (sizeof(struct st_arbore)) ;
arb=arb->dr;
arb->cuvant=cuv;
arb->nrap=1;
arb->st=NULL;
arb->dr=NULL;
}
else
arb->nrap=arb->nrap + 1;
}
void tiparire_arbore (struct st_arbore *arbo)
/* functia afiseaza arborele de cautare ce contine cuvinte si
numarul lor de aparitii */
{
if (arbo!=NULL)
{
tiparire_arbore (arbo->st) ; /* tiparire arbore stanga */
printf ("%s (ap: %d)\n", arbo -> cuvant, arbo -> nrap ) ;
nle++;
if (nle%24==0){
printf("Apasati o tasta pentru a continua afisarea!\n");
getch();
}
tiparire_arbore (arbo->dr) ; /* tiparire arbore dreapta */
}
}
void main ()
{struct st_arbore *arb;
char cuvant[LUNGMAX] ;
clrscr();
printf("Introduceti cuvinte, care vor fi apoi tiparite in ordine"
" alfabetica:\n");
gets(cuvant) ;
arb=(struct st_arbore *) malloc (sizeof(struct st_arbore)) ;
/* am presupus ca nu se returneaza de catre malloc
valoarea NULL */
arb->cuvant=strdup(cuvant);
33
arb->nrap=1;
arb->st=NULL;
arb->dr=NULL;
gets(cuvant) ;
while (strcmp(cuvant, ""))
{actualizare (arb, strdup(cuvant));
gets(cuvant);
}
printf("Lista ordonata a cuvintelor (numar aparitii):\n");
tiparire_arbore (arb);
}
return 0;
if (Frunza(a))
return 1;
return NrFrunze (a->st) + NrFrunze (a->dr);
}
Numrul de noduri de pe nivelul n
int NNN (AArb r, int n) /* numar noduri de pe nivelul n */
{ if (!r)
return 0;
if (n == 0)
return 1;
return NNN(r->st, n-1) + NNN(r->dr, n-1);
}
Gsirea valorii maxime dintr-un arbore nevid
int MaxArb (AArb a)
{ int m = a->info, t;
if (a->st != NULL) { t = MaxArb (a->st);
if (t > m) m = t;
}
if (a->dr != NULL) { t = MaxArb (a->dr);
if (t > m) m = t;
}
return m;
}
Determinarea nivelului pe care se gsete o anumit valoare x.
int NivVal (AArb a, int x)
/*(-1 daca nu exista)*/
{ int r;
if (!a) return -1;
if (a->info == x) return 0;
r = NivVal (a->st, x);
if (r < 0)
{ r = NivVal (a->dr, x);
if (r < 0) return -1;
}
return r + 1;
}
O problem (operaie) puin mai delicat, dect cele prezentate anteror, este cea a eliminrii
unui nod dintr-un arbore binar de cutare. Problema const n a determina poziia n arbore a unei chei,
x, ceea ce este simplu ntr-un arbore de cutare, iar apoi s se elimine nodul cu cheia respectiv. n
principiu avem dou situaii, dup cum nodul respectiv are ambii succesori (subarbori), sau numai pe
unul dintre ei. Mai exist i cazul cnd nu are succesori, dar aici eliminarea nu este o problem, doar se
elibereaz spaiul de memorie alocat nodului respectiv i se terge referina din nodul precedent.
n situaia n care nodul de eliminat nu are dect subarborele drept (deci cel stng este vid), dac
a este adresa nodului cu cheia x (cel de eliminat), atunci se atribuie lui a adresa descendentului drept,
35
ceea ce permite pstrarea proprietii arborelui (cea de cutare). Se procedeaz similar i pentru cazul
n care nodul de eliminat nu are dect subarborele stng (deci cel drept este vid), adic se atribuie lui a
adresa descendentului stng. Cele dou situaii sunt prezentate n figura urmtoare.
Din arborele iniial (cel din stnga) a fost eliminat nodul cu cheia 6, i se obine arborele din
dreapta.
S considerm aceeai problem pentru eliminarea nodului ce conine valoarea 4, din arborele
din figura urmtoare (n partea stng nainte de eliminare, i dreapta dup eliminare).
9
Arborele s-a obinut prin mutarea valorii maxime din subarborele stng valorii 4, adic a valorii
3 n locul valorii 4, i eliminarea frunzei pe care a fost valoarea 3. Mai exist i varianta n care
valoarea minim de pe subarborele drept al nodului ce conine valoarea 4, adic valoarea 5 se mut n
locul valorii 4, i se elimin nodul n care a fost valoarea 5. Ambele variante vor furniza acelai arbore
SRD, la listare.
Dac nodul de eliminat, a, cel ce conine cheia x, are ambii subarbori nevizi, atunci procedeul
de eliminare decurge dup cum urmeaz. Se determin cea mai mare valoare din subarborele stng,
adic nodul (frunza) cel mai din dreapta din subarborele stng, ce conine valoarea maxim din acest
subarbore, imediat mai mic dect cheia x, din arbore. Se va pune aceast valoare n locul lui x, iar
36
frunza respectiv se terge. Sau se poate proceda similar pentru subarborele stng, adic s determinm
cea mai mic valoare din acesta, care va fi valoarea imediat mai mare dect x, din arbore. ntruct
algoritmul este recursiv vom defini o funcie recursiv pentru eliminare. ntregul program este
prezentat n continuare.
/* arbelnod.cpp - programul construieste recursiv si tipareste un arbore
de numere intregi, utilizand functiile recursive: 'inserare' si 'listare'.
Arborele contine pe langa valorile intregi ordonate (SRD)
si numarul de aparitii pentru valorile ce se repeta.
Programul permite elimiarea unei anumite valori (chei), adica
un nod din arbore ce contine cheia, pastrand arborele de cautare (SRD). */
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
typedef struct tnod
{int nr;
int
ap;
struct tnod *pstg;
struct tnod *pdr;
} ARB;
int nel = 0;
ARB *e;
void inserare (ARB * &v, int val){
ARB *pc;
int gasit;
if (!v) {v = new ARB; v->nr=val; v->ap=1;
v->pstg=NULL; v->pdr=NULL;}
else if (v->nr > val)
inserare(v->pstg, val);
else if (v->nr < val)
inserare(v->pdr, val);
else v->ap++;
}
void inlocuire_nod(ARB * &n){ // n - nodul de eliminat
// se inlocuieste cu maximul de pe subarborele stang (acum maximul din n),
// care este pe ramura dreapta a lui 'n',
// sau cu minimul de pe subarborele drept (adica minimul din arborele n),
// rezultatul final fiind acelasi, adica un arbore de cautare
if (n->pdr)
inlocuire_nod(n->pdr);
else {e->nr=n->nr;
// e - nodul care se elimina (continutul sau)
e->ap=n->ap; // se actualizeaza valorile lui 'e' cu maximul din
e=n;
// arborele nodului 'n' , ramura dreapta
n=n->pstg; // pastrez legatura la ramura stanga a nodului
free(e);
// pentru care elimin ramura dreapta, a carei
}
// valori s-au mutat in nodul 'eliminat' (inlocuit)
}
void elimina_nod (ARB * &a, int x){// a-arbore, x-valoarea de eliminat
if (!a) printf("\nCheia cautata %d nu este in arbore!\n", x);
else if (x < a->nr)
// se cauta pozitia valorii 'x'
37
39