Documente Academic
Documente Profesional
Documente Cultură
12/5/2016 1
Structuri de date
Structuri de date primitive tipurile fundamentale din limbajul C
char, int, float, double, void
Structuri de date abstracte prin gruparea structurilor de datele nrudite, utilizatorul poate defini
structuri agregate care permit operarea uoar cu datele i execuie eficient
Tablouri, Liste, Stive, Cozi, Arbori, Tabele de dispersie, Grafuri, etc.
Structuri de date
Algoritmii lucreaz cu date, care trebuie stocate n memorie sub forma unor structuri de date adecvate
Alegem anumite structuri de date pe baza modului de stocare i a operaiilor permise asupra datelor de ctre acea structur
instructiune;
Complexitate constant timpul de rulare nu depinde de N O(1)
for(i=0; i < N; i++) { Complexitate liniar timpul de rulare este direct proporional cu N
instructiune; O(N)
} Dac N se dubleaz, timpul de rulare se dubleaz
Soluie: definirea unui model matematic pentru structura de date, adic desprirea modelului de comportament al
datei de modul de implementare
Se definete un container care poate reine un numr finit de obiecte
Se definete relaia dintre obiecte din container (ordonare liniar, ierarhic, etc.)
Se definete un set de operaii asupra containerului respectiv asupra obiectelor din container (ex. adaugare de obiect, tergere, cutare, etc.)
Exemple
Stiva are definite urmtoarele operaii: adugare, tergere, testare dac este gol, etc.
i poate fi implementat ca un vector , o list simplu nlnuit, etc.
Structur de cutare are definite urmtoarele operaii: adugare, tergere, cutare, etc.
i poate fi implementat ca un vector , o list simplu nlnuit, arbore, tabel de dispersie, etc.
6
Tipul de dat abstract LIST
Model matematic:
secven de elemente (A0, A1, A2, ..., An-1), unde Ai precede Ai+1, unde 0 <= i <= n-1
Operaii
Inserare
tergere
Cutare
Traversare afiare
Inversare
etc.
Structur liniar
Implementare
Tablou unidimensional
List dinamic
Simplu nlnuit
Dublu nlnuit
Circular 7
Liste simplu nlnuite
Pentru stocarea unei colecii de elemente alternativa la tablouri
Lista dinamic simplu nlnuit
este o nlnuire de elemente (structuri) numite noduri, n care fiecare nod include un pointer (o legtur) la urmtorul nod din lan
Ultimul nod conine un pointer nul
Avantaj fa de tablouri
Flexibilitate mare inserarea i tergerea foarte uoar permite creterea i descreterea listei n mod dinamic
Fiecare adugare de nod presupune o nou alocare a unui spaiu pentru un nod
Fiecare tergere de nod presupune eliberarea zonei de memorie ocupate de nod
Dezavantaj fa de tablouri
Se pierde accesul direct la elemente
n tablou toate elementele sunt accesibile n mod direct prin specificarea indexului
Elementele din list sunt accesibile doar secvenial pornind de la nceputul listei ctre final
Nu beneficiaz de principiul localitii datelor n memoria cache
8
Liste simplu nlnuite
Declararea unui nod Definirea tipului NOD
Exemplu: pentru simplitate nodul conine
un singur ntreg i pointerul ctre urmtorul nod
Eticheta
struct nod { structurii typedef struct nod {
int data; int data;
struct nod *urmator; struct nod *urmator;
}; } NOD;
pointer ctre urmtorul nod
nou 10
Stiv Heap
nou->urmator = prim;
prim = nou; nou 10 10
Inserarea unui nou nod n list
Inserarea nodului nou dup nodul p
nou->urmator = p->urmator;
p->urmator = nou;
11
Inserarea unui nou nod n list
k
Inserarea nodului newVtx pe poziia k
newVtx->next = temp1->next;
temp1->next = newVtx;
12
Inserarea unui nou nod n list
typedef struct nod {
int data;
struct nod *urmator;
} NOD;
p->urmator = c->urmator;
c->urmator = NULL;
free(c);
14
tergere
Operaia de tergere implic Trebuie s meninem pointer la nodul curent i cel precedent
Localizarea nodului de ters (victima)
Modificare nodului precedent pentru a ocoli nodul victim
Eliberarea spaiului ocupat de nodul victim cu free
if (curent == NULL)
return prim; // n NU a fost gasit
if (precedent == NULL)
prim = prim->urmator; // n este in primul nod tergerea primului nod este un caz special
else // n este intr-un alt nod
precedent->urmator = curent->urmator;
free(curent);
return prim;
}
15
Exemplu: liste simplu nlnuite
Inventarul de produse dintr-un depozit
Baza de date cu produsele este stocat ntr-o list simplu nlnuit
Avantaj:
fr limite prestabilite pentru dimensiune alocare dinamic n timpul rulrii
Adugarea i tergerea facil
Meninerea ordonat a produselor prin adugare n poziia corespunztoare
struct part {
int number; Pointer ctre
char name[NAME_LEN + 1]; nodul urmtor
int on_hand;
struct part *next;
};
16
Exemplu: liste simplu nlnuite
Funcia principal
Utilizatorul introduce o opiune i se execut funcia corespunztoare
Se repet ntr-un ciclu pn cnd utilizatorul introduce opiunea q
Se afieaz mesaj de eroare dac opiunea este invalid
int main(void) {
char code;
for (;;) {
printf("Enter operation code: ");
scanf(" %c", &code);
while (getchar() != '\n') /* se ignora restul liniei */
; /* alternativa: fflush(stdin); */
switch (code) {
case 'i': insert(); break;
case 's': search(); break;
case 'u': update(); break;
case 'p': print(); break;
case 'q': return 0;
default: printf("Illegal code\n");
}
printf("\n");
}
}
17
Exemplu: liste simplu nlnuite
Funcia de regsire produs
Cutare pe baza numrului de produs
struct part *find_part(int number) {
struct part *p;
new_node->next = cur;
if (prev == NULL)
inventory = new_node;
else
prev->next = new_node;
} 19
Exemplu: liste simplu nlnuite
Funcia de actualizare
void update(void) {
int number, change;
struct part *p;
Funcia de afiare
void print(void) {
struct part *p;
Eticheta
struct nod_d { structurii typedef struct nod_d {
int data; int data;
struct nod *precedent, struct nod *precedent,
*urmator; *urmator;
}; } NODD;
pointer ctre nodul
precedent i urmtor
Eticheta structurii este esenial
Structura are ca i componenet un pointer la o structur de acelai tip care tocmai se declar / definete
newVtx->next = temp2;
temp2->prev = newVtx;
temp1->next->prev
temp1->next = newVtx;
newVtx->prev = temp1;
23
tergerea din lista dublu nlnuit
k
tergerea nodului del de pe poziia k
del->prev del->next
prev->next = after;
after->prev = prev;
del->next->prev
free(del);
24
Liste nlnuite circulare
De dou tipuri
Liste simplu nlnuite circulare
Liste dublu nlnuite circulare
25
Stiva
Tipul de dat abstract STIVA este o colecie secvenial de elemente asupra crora se impune accesul
n regim LIFO Last In First Out
Elemetele sunt ordonate astfel nct, la orice moment doar cel mai recent element adugat poate fi extras
Alte operaii
size: numrul de elemente din stiv
isEmpty: testare stiv vid
top: returneaz elementul din vrful stivei fr s extrag / tearg elementul
Dac stiva este goal - mesaj de eroare
Implementare
Vector implementare simpl, dar capacitatea maxim este fixat
List simplu nlnuit implementare mai complex, dar capacitatea este flexibil (limitat doar de memoria disponibil)
Utilizare frecven
Vezi alocarea variabilelor locale, recursivitate (creterea stivel la autoapeluri stack frame)
26
push & pop
Push inserare n vrful stivei
temp->next = head;
head = temp;
free(temp);
27
Coada
Tipul de dat abstract COADA este o colecie secvenial de elemente asupra crora se impune
accesul n regim FIFO First In First Out
Elemetele sunt ordonate astfel nct, la orice moment doar elementul cel mai de mult (cel mai vechi) poate fi extras
Alte operaii
size: numrul de elemente din coad
isEmpty: testare coad vid
front: returneaz elementul de la nceputul cozii fr s extrag / tearg elementul
Dac coada este goal - mesaj de eroare
Implementare eficient
Pentru operaii n timp constant O(1)
List dublu nlnuit att inserarea ct i teregerea sunt operaii simple, deoarece ambele capete sunt accesibile direct
List simplu nlnuit accesul este direct doar la un capt, dar pentru eficientizare se menine un pointer i la finalul listei
Ambele capete devin direct accesibile
28
Coada
Enqueue adugare la sfritul cozii
tail->next = temp;
tail = temp;
free(temp);
29
Concluzii pentru liste
Lista este structura potrivit dac sarcina de efectuat implic inserri i tergeri de elemente din
mijlocul (interiorul) listei la care avem deja un pointer
Impic de regul un cost de O(1)
Fa de tablouri unde trebuie copiate toate elementele de dup punctul de inserare pentru a face loc noului element
Dac inserarea i tergerea se efectueaz numai la un singur capt atunci tabloul poate oferi performane mai bune
Listele nu sunt potrivite pentru sarcini care implic acces direct la elemente
Accesul presupune parcurgere --> O(n)
Tabloul poate fi o soluie mai bun pentru astfel de cazuri, NS
dac se dorete inserare / tergere n mijloc, atunci trebuie s apelm la ARBORI
30
Exemplu - stiva
Implementare modular /* stack.h (K. N. King, page 488) */
Fiierul header al modulului interfaa
S ne reamintim de la programare modular ... #ifndef STACK_H
#define STACK_H
void make_empty(void);
bool is_empty(void);
bool is_full(void);
Dou abordri n implementarea stivei void push(int i);
Vector (stack1.c) int pop(void);
List simplu nlnuit (stack2.c) #endif
32
Exemplu stiva ca list nlnuit
Impementarea stack2.c
fiierul surs
/* stack2.c (K. N. King, page 488) */
#include <stdio.h>
#include <stdlib.h> void push(int i) {
#include "stack.h" struct node *new_node = malloc(sizeof(struct node));
if (new_node == NULL)
struct node { terminate("Error in push: stack is full.");
int data;
struct node *next; new_node->data = i;
}; new_node->next = top;
top = new_node;
static struct node *top = NULL; }
bool is_full(void) {
return false; 33
}
Exemplu stiva ca tip de dat abstract (ADT)
Dezavantajul modulului descris precedent
Nu putem avea mai multe instane de stiv
#include <stdio.h>
#include <stdlib.h> void make_empty(Stack s) {
#include "stackADT.h" s->top = 0;
}
#define STACK_SIZE 100
bool is_empty(Stack s) {
struct stack_type { return s->top == 0;
Item contents[STACK_SIZE]; }
int top;
}; bool is_full(Stack s) {
return s->top == STACK_SIZE;
static void terminate(const char *message) { }
printf("%s\n", message);
exit(EXIT_FAILURE); void push(Stack s, Item i) {
} if (is_full(s))
terminate("Error in push: stack is full.");
pointer Stack create(void) { s->contents[s->top++] = i;
Stack s = malloc(sizeof(struct stack_type)); }
if (s == NULL)
terminate("Error in create: stack could not be created."); Item pop(Stack s) {
s->top = 0; if (is_empty(s))
return s; terminate("Error in pop: stack is empty.");
} return s->contents[--s->top];
}
void destroy(Stack s) {
free(s);
} 35
Exemplu stiva ca tip de dat abstract (ADT)
Implementarea tipului de dat abstract stiv folosind un vector alocat dinamic
/* stackADT2.c (K. N. King, page 496) */
void destroy(Stack s) {
#include <stdio.h> modificri free(s->contents);
#include <stdlib.h> free(s);
#include "stackADT2.h" }
#include <stdio.h>
#include <stdlib.h>
#include "stackADT.h"
ADT-ul furnizeaz nivelul corespunztor de abstractizare dac operaiile furnizate sunt naturale dat
fiind descrierea de nivel nalt
38
Tabele de dispersie (tabele de hashing)
Mapeaz domeniul cheilor posibile n spaiul de adrese disponibil (dimensiunea tabelei)
Tabelele de dispersie implementeaz tipul mulime (set) sau tipul dicionar
Ideea de baz
Considerm un tablou de dimnesiunea m
Tablourile au avantajul accesului direct la elemente cost O(1)
Definim o funcie de dispersie care mapeaz cheile la indicii tabelului valori n domeniul {0, , m-1}
Pentru valori ntregi putem avea funcia: h(k) = k mod m
Valoarea k se insereaz / terge / acceseaz la indexul h(k) n tabela de hashing
Inserm valoarea 12 .... h(12) = 12 mod 5 = 2
Coliziune Coliziune !!
Funcia de dispersie returneaz acelai index pentru mai multe valori de intrare (chei)
Soluie
--> tabel de liste nlnuite (n loc de tablou de elemente simple avem tablou de pointeri la liste nlnuite)
Cheia k se insereaz / terge / acceseaz n lista nlnuit de la indexul h(k) n tabela de hashing
39
Tabele de dispersie
Maparea cheilor K n tabela de hashing T, unde T este un tablou cu T[0 m-1] i indexul se calculeaz
pe baza funciei de hashing h: U {0, 1, , m-1}
INSERT(T, x): Inserare elementului x n capul listei de la indexul h(x.k) Dac elementul nu este n tabel O(1)
DELETE(T, x): tergerea elementului din lista de la poziia T[(h(x.k)] elem.prev = elem.next O(1)
SEARCH(T, k): Cutarea elementului cu cheia k n lista de la indexul h(k) Depinde de lungimea listei max O(n)
Cerine
Factorul de impact trebuie s fie subunitar nu se pot stoca mai multe elemente dect locaii disponibile n tabel
Strict mai mic dect 1 altfel unele cutri nu se vor termina niciodat
Dispersie cu distribuie uniform
Minimizare coliziunilor
Trateaz coliziunile prin stocarea elementului nou ntr-o alt locaie nc nefolosit
Gsirea locaiei: folosind secvene de probare
Probare liniar: se caut locaie liber n poziiile succesive pn se gsete o locaie liber
Mergnd circular: la final se ntoarce la nceputul tabelei
( h(k)+i ) mod m
Probare cuadratic: se caut locaie liber n poziii care depind cuadratic de i
Ex. srind peste 1 element apoi 2, apoi 4, etc.
Mergnd circular: la final se ntoarce la nceputul tabelei
( h(k)+c1i+ c1i2 ) mod m
Dispersie dubl: se caut locaie liber n poziii variabile
Pe baza unei funcii de dispersie auxiliar
Cu atene pe alegerea valorilor: h2(k) relativ prim cu m ( h1(k)+i*h2(k) ) mod m
42
Tabele de dispersie
Probare liniar: se caut locaie liber n poziiile succesive
pn se gsete o locaie liber T[(h(k)], T[(h(k) + 1], T[(h(k) + 2], ... , T[m-1], T[0], T[(h(k) - 1]
Mergnd circular: la final se ntoarce la nceputul tabelei
Cutare
Calculeaz h(k), Acceseaz T[h(k)]
Dac locaie liber elementul nu este n tabel
Element regsit n
Dac locaie ocupat
locaie succesiv
Se caut potrivirea dintre cheia cutat i cheia elementului
(prin probarea elementelor succesive - circular)
Dac se gsete potrivire se returneaz elementul
Dac se ajunge la locaie liber elementul nu este n tabel Cutare euat
tergere
Cutare euat,
Cutare urmat de eliminarea elementului gsit dei elementul
problem la cutare ulterioar exist n tabel
44
Tabele de dispersie
Probare liniar: se caut locaie liber n poziiile succesive pn se gsete o locaie liber
Mergnd circular: la final se ntoarce la nceputul tabelei
tergere
Problema metodei de tergere lene: poluarea tabelei
Locaiile cu flagul setat pe ters pot lega mpreun date nenrudite (dpdv. al algoritmului de cutare)
Eficiena cutrii scade ajungem la cutare liniar
Soluie: crearea unei noi tabele de hashing cu datele prin adugarea elementelor existente n tabela veche
45
Tabele de dispersie
Alegerea funciei de dispersie
Metoda divizrii
Dac cheile sunt valori ntregi mari calculm restul modulo m
Unde m este numr prim mare
Dac cheile sunt iruri de caractere tratm stringul ca pe un ntreg
Metoda multiplicrii
n loc de m se folosete o putere a lui 2, ex. 232 sau 264
n funcie de dimensiunea lui size_t)
pentru baz se folosete un numr prim mic (37, 31, 97, ...)
O familie de funcii de dispersie {Hr} este universal dac pentru orice chei distincte 46
x i y, probabilitatea ca r s fie ales astfel nct Hr(x) = Hr(y) este exact 1/m
Tabele de dispersie
Performana operaiilor depinde de facotrul de ncrcare (umplere)
Eficien dac factorul de umplere este meninut la valori mici
Avantaje
Timpul mediu al operaiilor este O(1)
Dac funcia de dispersie este bun i dimensiunea tabeleli adecvat
Performan mai bun dect n cazul arborilor binari de cutare
Dezavantaj
Elementele nu sunt ordonate
47
Surse bibliografice
K. N. King, C Programming A Modern Approach, 2nd edition, W. W. Norton & Co., 2008
Capitolul 19
http://visualgo.net/
48