Documente Academic
Documente Profesional
Documente Cultură
struct Elem
{
Data val; // datele efective memorate
struct Elem* next, *prev; // legatura catre nodul urmator ,
// legatura catre nodul anterior
};
Lista dublu inlantuita
Avantaje:
Dat fiind un nod in lista – se poate naviga in ambele directii
Un nod dintr-o lista simplu inlantuita nu se poate sterge daca nu avem pointer la nodul
predecesor (aux), in cazul listei dublu inlantuite stergerea se face mai usor – deoarece
adresa acestuia e cunoscuta.
Dezavantaje:
E nevoie de spatiu in plus pentru fiecare nod ca sa se stocheze si legatura catre nodul
predecesor.
Inserarile si stergerile de elemente sunt ceva mai laborioase
Lista dublu inlantuita
1. Inserare element la inceputul listei
//main
Node* head = NULL; //creare lista
addAtBeginning(&head,0);
addAtBeginning(&head,1);
addAtBeginning(&head,2);
Lista dublu inlantuita
2. Inserare element oriunde in lista (include cazul – adaugare la inceput /final)
void addAtPos(Node**head, Data v, int pos) //adaug pe pe pozitia pos
{
Node*aux, *headcopy=*head;
int p=0;
void print(Node*head){
Node *headcopy=head;
while (headcopy!=NULL) {
printf("%d",headcopy->val);
headcopy=headcopy->next;
}
} // daca as cunoaste/stoca adresa ultimului element - as putea sa parcurg si in sens invers
//main
int i;
Node* head = NULL;
for (i=0;i<4;i++)
addAtPos(&head,i,i);
print(head);
// 0 1 2 3
addAtPos(&head,7,2);
print(head);
// 0 1 7 2 3
Lista dublu inlantuita
4. Modificarea informatiei stocate intr-un nod (include si cautarea)
void modify(Node*head, Data newdata, int pos) // modific elementul de pe pozitia pos
{
if (head == NULL) return;
Node*aux=head, *headcopy=head;
int p=0;
while (headcopy!=NULL&&p<pos) {
aux=headcopy;
//ma folosesc de auxiliar ca sa tratez cazul in care trebuie sa modific ultimul element, pentru
//ca, atunci nu as putea sa ma refer la aux prin headcopy->prev, pentru ca headcopy e NULL
headcopy=headcopy->next;
p++;
}
aux->val=newdata;
}//complexitate O(n)
OBS: Adaugarea dupa ultimul element, respectiv modificarea acestuia trebuiesc tratate separat
ca sa ma pot folosi de legatura la nodul anterior. Ce as putea face?
-> santinele ->Tema
Lista dublu inlantuita
5. Stergerea unui element (include si cautarea)
void deleteV(Node**head, Data val) //se cauta pentru stergere elementul cu valoarea val
{
if (*head==NULL) return;
Node *headcopy=*head;
if (headcopy->val==val) {
*head=(*head)->next;
if((*head)!=NULL) (*head)->prev=NULL;
free(headcopy);
return;
} // stergere cap lista – tratat separat
Lista dublu inlantuita
// stergere alt tip de elemente
// Node *headcopy=*head;
while (headcopy!=NULL) {
if (headcopy->val!=val){
headcopy=headcopy->next;
}else{
(headcopy->prev)->next=headcopy->next;
if((headcopy->prev)->next!=NULL)
((headcopy->prev)->next)->prev=headcopy->prev;
free(headcopy);
return;
}
}
}
Lista dublu inlantuita
6. Stergerea listei
*head=NULL;
}
Problema: Se da o lista inlantuita prin primul ei element. Se cere un algoritm cat mai
eficient care sa determine daca lista are sau nu bucle/cicluri.
Fie:
m – nr noduri pana la inceputul buclei
n – nr de noduri din bucla
k – nr de noduri dupa care cei 2 pointeri se intalnesc.
Distanta parcursa de fast e de doua ori mai mare decat cea parcursa de slow:
Ducem pointerul slow din nou la inceput si setam viteza pentru amandoi pointerii la
un pas.
Cat timp slow face m pasi - > tot atatia face si fast, dar incepand de la m+k -> ajunge la
m+m+k= m+x*n => amandoi pointerii o sa indice la inceputul buclei
// daca exista bucla vreau sa o elimin
if (slow == fast)
{
slow = head;
while (slow->next != fast->next)
{
slow = slow->next;
fast = fast->next;
}
int main()
{
Node *head = newNode(0);
head->next = newNode(1);
head->next->next = newNode(2);
head->next->next->next = newNode(3);
head->next->next->next->next = newNode(4);
// Creez bucla
head->next->next->next->next->next = head->next->next;
detectAndRemoveLoop(head);
print(head);
return 0;
Stiva
o structura de date abstracta, liniara pentru care atat operatia de inserare a unui
element cat si cea de extragere se realizeaza la un singur capat numit varful stivei.
ultimul element adaugat e primul scos – LIFO (Last In First Out).
Operatii caracteristice
crearea/distrugerea unei stive
inserarea unui element in stiva (push)
scoaterea unui element din stiva (pop)
accesarea elementului din varf (top)
aflarea capacitatii
verificare daca stiva e goala - isEmpty
verificare daca stiva e plina - isFull
Implementare
cu vector
Avantaje - implementarea e simpla, consumul de memorie e redus, viteza mare pentru
operatii (mereu accesez ultimul element prin index)
Dezavantaje - numarul de elemente ar trebui cunoscut (vector static) sau risipa de
memorie daca dimensiunea alocata e mult mai mare decat cea necesara (vector
dinamic), respectiv, timp daca trebuie realocat spatiu
cu liste
Avantaje – numar oarecare de elemente; stocate in locatii neconsecutive
Dezavantaje - consum de memorie suplimentar pentru memorarea legaturilor
Stiva – Implementarea cu lista
struct Elem
{
Data val; // datele efective memorate
struct Elem* next; // legatura catre nodul urmator
};
//main
//o stiva goala se declara - creaza astfel:
//main
int i;
Node* stackTop = NULL;
for (i=0;i<4;i++)
push(&stackTop,i);
Stiva – Implementarea cu lista
int isEmpty(Node*top){
return top==NULL;
}
Daca exista o limita maxima pentru capacitatea stivei se putea testa depasirea
spatiului alocat – inainte de adaugarea un element.
Stiva – Implementarea cu lista
3. Scoaterea unui element (se face din varful stivei )
//main
int i;
Node* stackTop = NULL; //creare stiva
for (i=0;i<4;i++)
push(&stackTop, i);
printf("%d", top(stackTop));
printf("%d", pop(stackTop));
Stiva – Implementarea cu lista
5. Stergerea stivei
void deleteStack(Node**top)
{
Node* topCopy=*top, *temp;
while (topCopy!=NULL){
temp=topCopy;
topCopy=topCopy->next;
free(temp);
}
}
//main
int i;
Node* stackTop = NULL;
for (i=0;i<4;i++)
push(&stackTop,i);
deleteStack(&stackTop);
Stiva – Implementarea cu lista
• Fie n numarul de elemente din stiva
Operatie Complexitate
creare stiva O(1)
push O(1)
pop O(1)
top O(1)
isEmpy O(1)
deleteStack O(n)
struct stack{
Node *top;
int capacity, size;
};
struct DynArrayStack{
int top;
int capacity;
Data*array;
};
unde:
top – pozitia elementului cel mai din varf (final in vector)
capacity – dimensiunea vectorului
array –vectorul in care sunt stocate elementele
Stiva – Implementarea cu vector
1. Crearea stivei
Stack* createStack()
{
Stack *s=(Stack*)malloc(sizeof(Stack));
if (!s) return NULL; // verific ca am alocat spatiu
s->capacity=5; // as fi putut sa transmit capacitatea ca parametru
s->top=-1; //nu am niciun element
s->array= (Data*) malloc(sizeof(Data)*s->capacity);
if (!s->array) return NULL; // verific daca s-a alocat spatiu pentru bufferul stivei
return s;
}
//main
Stack *stack=createStack();
int isFull(Stack*s){
return (s->top==s->capacity-1);
}
int isEmpty(Stack*s){
return (s->top==-1);
}
void increaseCapacity(Stack*s){
s->capacity*=2;
s->array = (Data*)realloc(s->array,s->capacity);
}
//Capacitatea se poate dubla; creste din 2 in 2; etc
//Realocarea de spatiu afecteaza complexitatea pentru push -> O(n) deci ar trebui sa se
//intample cat mai rar => dublarea spatiul e o varianta buna
Stiva – Implementarea cu vector
3. Adaugarea si scoaterea de elemente; aflarea valorii varfului
Data pop(Stack*s){
if (!isEmpty(s)) return s->array[s->top--]; //returnez valoarea varfului si decrementez top
return INT_MIN; //daca stiva e goala returnez o valoare de minim presetata
}
Data top(Stack*s){
if (!isEmpty(s)) return s->array[s->top]; //returnez valoarea varfului
return INT_MIN;
}
Stiva – Implementarea cu vector
4. Stergerea stivei
void deleteStack(Stack**s){
if (*s) {
if ((*s)->array) free((*s)->array); //eliberez spatiul ocupat de buffer
free(*s); //si apoi spatiul ocupat de stiva si o fac NULL
(*s)=NULL;
}
}
//main()
Stack*stack=createStack();
push(stack,1);
push(stack,2);
top(stack);
pop(stack);
deleteStack(&stack);
Stiva – Implementarea cu vector
• Fie n numarul de elemente din stiva
Operatie Complexitate
creare stiva O(1)
push O(1) – in medie*
pop O(1)
top O(1)
isEmpy O(1)
deleteStack O(1)
Concluzii: Implementarea cu vectori e facila si la fel de buna ca cea cu liste, dar – vectorul
necesita locatii succesive de memorie, lista nu => acesta e un dezavantaj daca numarul de
inregistrari e mare.
Tema stiva
1. Concatenati 2 stive. Ce varianta de implementare e mai buna (cu vector sau lista)?
2. Inversati cuvintele dintr-o propozitie folosind o stiva.
3. Se considera un sir de numere intregi. Sa se scrie functia care construieste doua stive (una
cu numere negative si cealalta cu numere pozitive ) ce contin numerele in ordinea initiala –
folosind doar structuri de tip stiva. Indicatie: Se adauga toate elementele intr-o stiva
temporara dupa care se extrag elementele din aceasta si se introduc in stiva
corespunzatoare.
4. Creati o structura de date twoStacks care reprezinta doua stive. Implementarea
pentru twoStacks trebuie sa foloseasca un singur vector pentru stocarea elementelor.
Implementati functiile
push1 –> adauga elementul x in prima stiva
push2 –> adauga elementul x in a doua stiva
pop1 –> scoate element cu returnarea valorii sale din prima stiva
pop2 –> scoate element cu returnarea valorii sale din a doua stiva
Cum implementez twoStack cat mai eficient dpdv al spatiului?
Incep stivele de la 0 si n-1.
Coada
o structura de date abstracta, liniara pentru care operatia de inserare se face la un
capat si operatia de extragere se face la celalalt capat (first in first out sau last in
last out).
avem acces direct la elementul de la inceput, respectiv, sfarsitul cozii.
Operatii caracteristice
Creare/stergere coada
Adaugare/scoatere elemente - enQueue/deQueue
Testarea capacitatii - overflow/underflow
Aflarea valorilor de la inceputul si respectiv finalul cozii - front/rear
Test daca e goala - isEmpty
Aflarea dimensiunii - queueSize
struct Elem
{
Data val; // datele efective memorate
struct Elem* next; // legatura catre nodul urmator
};
struct Q{
Node *front,*rear;
};
Queue* createQueue()
{
Queue *q;
q=(Queue *)malloc(sizeof(Queue));
if (q==NULL) return NULL;
q->front=q->rear=NULL;
return q;
}
//main
Queue* q;
q=createQueue();
Coada – implementarea cu liste
2. Adaugarea unui element
void enQueue(Queue*q,Data v)
{
Node* newNode=(Node*)malloc(sizeof(Node));
newNode->val=v;
newNode->next=NULL;
if (q->rear==NULL) q->rear=newNode;
else{
(q->rear)->next=newNode;
(q->rear)=newNode;
}
//main
Queue* q=createQueue();
for (i=0;i<4;i++)
enQueue(q,i);
Coada – implementarea cu liste
3. Test coada goala si 4. Scoaterea unui element
int isEmpty(Queue*q){
return q->front==NULL;
}
Data deQueue(Queue*q)
{
Data d=0;
Node* newNode;
if (isEmpty(q)) return d;
newNode=q->front;
d=newNode->val;
q->front=(q->front)->next;
free(newNode);
return d;
}
//complexitate O(1)
//main
while (q->front!=NULL)
printf("%d",deQueue(q));
Coada – implementarea cu liste
5. Stergere coada
void deleteQueue(Queue*q){
Node* temp;
while (!isEmpty(q)){
temp=q->front;
q->front=q->front->next;
printf("%d",temp->val);
free(temp);
}
free(q);
}
//complexitate O(n)
Tema coada
• Implementati o coada folosind o lista dublu inlantuita circulara cu santinela.
_______________________________________________________________
• Implementati merge sort folosind lista in loc de vector.