Listă circulară legată: Avantaje și dezavantaje
Ce este o listă circulară legată?
O listă circulară legată este o secvență de noduri aranjate astfel încât fiecare nod să poată fi retras la sine. Aici un „nod” este un element autoreferențial cu pointeri către unul sau două noduri din imediata apropiere.
Mai jos este o reprezentare a unei liste circulare legate cu 3 noduri.
Aici, puteți vedea că fiecare nod este retras la el însuși. Exemplul prezentat mai sus este o listă circulară cu legături unice.
Notă: Cea mai simplă listă circulară legată este un nod care urmărește numai la sine, așa cum se arată
pachet de bază Operaîn liste circulare legate
Operațiunile de bază pe o listă circulară legată sunt:
- inserare
- Ștergere și
- traversal
- Inserarea este procesul de plasare a unui nod într-o poziție specificată în lista circulară legată.
- Ștergerea este procesul de eliminare a unui nod existent din lista legată. Nodul poate fi identificat prin apariția valorii sau prin poziția sa.
- Traversarea unei liste circulare legate este procesul de afișare a întregului conținut al listei legate și de revenire la nodul sursă.
În secțiunea următoare, veți înțelege inserarea unui nod și tipurile de inserare posibile într-o listă circulară legată individual.
inserare OperaTION
Inițial, trebuie să creați un nod care să se orienteze către sine, așa cum se arată în această imagine. Fără acest nod, inserarea creează primul nod.
În continuare, există două posibilități:
- Inserarea la poziția curentă a listei circulare legate. Aceasta corespunde inserării la începutul sfârșitului unei liste obișnuite de legături singulare. Într-o listă circulară legată, începutul și sfârșitul sunt același.
- Inserarea după un nod indexat. Nodul ar trebui să fie identificat printr-un număr de index corespunzător valorii elementului său.
Pentru inserarea la începutul/sfârșitul listei circulare legate, adică în poziția în care a fost adăugat primul nod,
- Va trebui să rupeți auto-linkul existent la nodul existent
- Următorul indicator al noului nod se va conecta la nodul existent.
- Următorul indicator al ultimului nod va indica nodul inserat.
NOTĂ: indicatorul care este token master sau începutul/sfârșitul cercului poate fi schimbat. Se va întoarce în continuare la același nod pe o traversare, discutată mai înainte.
Pașii de la (a) i-iii sunt prezentați mai jos:
(Nod existent)
PASUL 1) Rupe legătura existentă
PASUL 2) Creați o legătură directă (de la un nod nou la un nod existent)
PASUL 3) Creați o legătură în buclă către primul nod
În continuare, veți încerca inserarea după un nod.
De exemplu, să inserăm „VALOARE2” după nodul cu „VALOARE0”. Să presupunem că punctul de pornire este nodul cu „VALOARE0”.
- Va trebui să rupeți linia dintre primul și al doilea nod și să plasați nodul cu „VALUE2” între ele.
- Următorul pointer al primului nod trebuie să se conecteze la acest nod, iar următorul pointer al acestui nod trebuie să se conecteze la cel de-al doilea nod anterior.
- Restul aranjamentului rămâne neschimbat. Toate nodurile sunt trasabile către ele însele.
NOTĂ: Deoarece există un aranjament ciclic, inserarea unui nod implică aceeași procedură pentru orice nod. Indicatorul care finalizează un ciclu completează ciclul la fel ca orice alt nod.
Acesta este prezentat mai jos:
(Să spunem că există doar două noduri. Acesta este un caz trivial)
PASUL 1) Scoateți legătura interioară dintre nodurile conectate
PASUL 2) Conectați nodul din partea stângă la noul nod
PASUL 3) Conectați noul nod la nodul din partea dreaptă.
ștergere OperaTION
Să presupunem o listă circulară legată cu 3 noduri. Cazurile de ștergere sunt prezentate mai jos:
- Ștergerea elementului curent
- Ștergerea după un element.
Ștergere la început/sfârșit:
- Traversați la primul nod de la ultimul nod.
- Pentru a șterge de la sfârșit, ar trebui să existe un singur pas de traversare, de la ultimul nod la primul nod.
- Ștergeți legătura dintre ultimul nod și următorul nod.
- Conectați ultimul nod la următorul element al primului nod.
- Eliberați primul nod.
(Configurație existentă)
PASUL 1) Scoateți legătura circulară
PASII 2) Eliminați legătura dintre primul și următorul, legați ultimul nod, la nodul care urmează pe primul
PASUL 3) Eliberați / dealocați primul nod
Ștergerea după un nod:
- Traversați până când următorul nod este nodul care trebuie șters.
- Traversați la următorul nod, plasând un indicator pe nodul anterior.
- Conectați nodul anterior la nodul de după nodul prezent, folosind următorul indicator.
- Eliberați nodul curent (deconectat).
PASUL 1) Să spunem că trebuie să ștergem un nod cu „VALUE1”.
PASUL 2) Eliminați legătura dintre nodul anterior și nodul curent. Conectați nodul său anterior cu următorul nod indicat de nodul curent (cu VALUE1).
PASUL 3) Eliberați sau dezalocați nodul curent.
Traversarea unei liste circulare legate
Pentru a parcurge o listă circulară legată, pornind de la un ultim pointer, verificați dacă ultimul pointer în sine este NULL. Dacă această condiție este falsă, verificați dacă există un singur element. În caz contrar, traversați folosind un indicator temporar până când ultimul indicator este atins din nou, sau de câte ori este necesar, așa cum se arată în GIF de mai jos.
Avantajele listei circulare legate
Unele dintre avantajele listelor circulare legate sunt:
- Nicio cerință pentru o atribuire NULL în cod. Lista circulară nu indică niciodată către un pointer NULL decât dacă este complet dealocată.
- Listele circulare legate sunt avantajoase pentru operațiunile de sfârșit, deoarece începutul și sfârșitul coincid. Algorithms cum ar fi programarea Round Robin poate elimina cu grijă procesele care sunt puse în coadă într-o manieră circulară, fără a întâmpina indicatori de referință suspendați sau NULL.
- Lista circulară legată îndeplinește, de asemenea, toate funcțiile obișnuite ale unei liste conectate unic. De fapt, circular liste dublu legate discutat mai jos poate chiar elimina necesitatea unei traversări pe toată lungimea pentru a localiza un element. Acest element ar fi cel mult doar exact opus față de început, completând doar jumătate din lista legată.
Dezavantajele listei circulare legate
Dezavantajele utilizării unei liste circulare sunt mai jos:
- Listele circulare sunt complexe în comparație cu liste legate individual.
- RevEsa de liste circulare este un complex în comparație cu listele individuale sau duble.
- Dacă nu este tratat cu atenție, atunci codul poate merge într-o buclă infinită.
- Mai greu de găsit sfârșitul listei și controlul buclei.
- Inserând la Start, trebuie să parcurgem lista completă pentru a găsi ultimul nod. (Perspectiva implementării)
Listă legată individual ca listă circulară legată
Sunteți încurajat să încercați să citiți și să implementați codul de mai jos. Prezintă aritmetica pointerului asociat listelor circulare legate.
#include<stdio.h> #include<stdlib.h> struct node { int item; struct node *next; }; struct node* addToEmpty(struct node*,int); struct node *insertCurrent(struct node *, int); struct node *insertAfter(struct node *, int, int); struct node *removeAfter(struct node *, int); struct node *removeCurrent(struct node *); void peek(struct node *); int main() { ...
Explicația codului:
- Primele două linii de cod sunt fișierele de antet incluse necesare.
- Următoarea secțiune descrie structura fiecărui nod autoreferențial. Conține o valoare și un pointer de același tip ca și structura.
- Fiecare structură se leagă în mod repetat la obiecte de structură de același tip.
- Există diferite prototipuri de funcții pentru:
- Adăugarea unui element la o listă conexă goală
- Inserarea la în prezent arătată poziţia unei liste circulare legate.
- Inserarea după un anume indexate valoare din lista legată.
- Eliminarea/Ștergerea după un anumit indexate valoare din lista legată.
- Eliminarea în poziția curentă a unei liste circulare legate
- Ultima funcție imprimă fiecare element printr-o traversare circulară în orice stare a listei legate.
int main() { struct node *last = NULL; last = insertCurrent(last,4); last = removeAfter(last, 4); peek(last); return 0; } struct node* addToEmpty(struct node*last, int data) { struct node *temp = (struct node *)malloc(sizeof( struct node)); temp->item = data; last = temp; last->next = last; return last; } struct node *insertCurrent(struct node *last, int data)
Explicația codului:
- Pentru codul addEmpty, alocă un nod gol folosind funcția malloc.
- Pentru acest nod, plasați datele în variabila temp.
- Atribuiți și legați singura variabilă la variabila temp
- Returnează ultimul element în contextul main() / aplicație.
struct node *insertCurrent(struct node *last, int data) { if(last == NULL) { return addToEmpty(last, data); } struct node *temp = (struct node *)malloc(sizeof( struct node)); temp -> item = data; temp->next = last->next; last->next = temp; return last; } struct node *insertAfter(struct node *last, int data, int item) { struct node *temp = last->next, *prev = temp, *newnode =NULL; …
Explicația codului
- Dacă nu există niciun element de inserat, atunci ar trebui să vă asigurați că adăugați la o listă goală și returnați controlul.
- Creați un element temporar pe care să îl plasați după elementul curent.
- Conectați indicatorii așa cum se arată.
- Întoarceți ultimul indicator ca în funcția anterioară.
... struct node *insertAfter(struct node *last, int data, int item) { struct node *temp = last->next, *prev = temp, *newnode =NULL; if (last == NULL) { return addToEmpty(last, item); } do { prev = temp; temp = temp->next; } while (temp->next != last && temp->item != data ); if(temp->item != data) { printf("Element not found. Please try again"); ...
Explicația codului:
- Dacă nu există niciun element în listă, ignorați datele, adăugați elementul curent ca ultimul element din listă și returnați controlul
- Pentru fiecare iterație din bucla do-while asigurați-vă că există un indicator anterior care conține ultimul rezultat parcurs.
- Numai atunci poate avea loc următoarea traversare.
- Dacă datele sunt găsite, sau temp ajunge înapoi la ultimul pointer, do-while se termină. Următoarea secțiune a codului decide ce trebuie făcut cu elementul.
... if(temp->item != data) { printf("Element not found. Please try again"); return last; } else { newnode = (struct node *)malloc(sizeof(struct node)); newnode->item = item; prev->next = newnode; newnode->next = temp; } return last; } struct node *removeCurrent(struct node *last) ...
Explicația codului:
- Dacă întreaga listă a fost parcursă, dar articolul nu a fost găsit, afișați un mesaj „articol negăsit” și apoi returnați controlul apelantului.
- Dacă a fost găsit un nod și/sau nodul nu este încă ultimul nod, atunci creați un nou nod.
- Link nodul anterior cu noul nod. Conectați nodul curent cu temp (variabila traversală).
- Acest lucru asigură că elementul este plasat după un anumit nod din lista circulară legată. Reveniți la apelant.
struct node *removeCurrent(struct node *last) { if(last == NULL) { printf("Element Not Found"); return NULL; } struct node *temp = last->next; last->next = temp->next; free(temp); return last; } struct node *removeAfter(struct node *last, int data)
Explicația codului
- Pentru a elimina doar ultimul nod (actual), verificați dacă această listă este goală. Dacă este, atunci niciun element nu poate fi îndepărtat.
- Variabila temp parcurge doar o legătură înainte.
- Conectați ultimul indicator cu indicatorul de după primul.
- Eliberați indicatorul de temperatură. Dealocarea ultimului pointer nelegat.
struct node *removeAfter(struct node *last,int data) { struct node *temp = NULL,*prev = NULL; if (last == NULL) { printf("Linked list empty. Cannot remove any element\n"); return NULL; } temp = last->next; prev = temp; do { prev = temp; temp = temp->next; } while (temp->next != last && temp->item != data ); if(temp->item != data) { printf("Element not found"); ...
Explicația codului
- Ca și în cazul funcției anterioare de îndepărtare, verificați dacă nu există niciun element. Dacă acest lucru este adevărat, atunci niciun element nu poate fi eliminat.
- Pointeri li se atribuie poziții specifice pentru a localiza elementul de șters.
- Indicatoarele sunt avansate, unul în spatele celuilalt. (anterior în spatele temperaturii)
- Procesul continuă până când este găsit un element sau următorul element se întoarce la ultimul indicator.
if(temp->item != data) { printf("Element not found"); return last; } else { prev->next = temp->next; free(temp); } return last; } void peek(struct node * last) { struct node *temp = last; if (last == NULL) { return;
Explicația programului
- Dacă elementul a fost găsit după parcurgerea întregii liste legate, este afișat un mesaj de eroare care spune că elementul nu a fost găsit.
- În caz contrar, elementul este deconectat și eliberat în pașii 3 și 4.
- Pointerul anterior este legat de adresa indicată ca „următorul” de elementul care trebuie șters (temp).
- Prin urmare, indicatorul temp este dealocat și eliberat.
... void peek(struct node * last) { struct node *temp = last; if (last == NULL) { return; } if(last -> next == last) { printf("%d-", temp->item); } while (temp != last) { printf("%d-", temp->item); temp = temp->next; } }
Explicația codului
- Privirea sau traversarea nu este posibilă dacă este nevoie de zero. Utilizatorul trebuie să aloce sau să insereze un nod.
- Dacă există un singur nod, nu este nevoie să traversați, conținutul nodului poate fi imprimat, iar bucla while nu se execută.
- Dacă există mai mult de un nod, atunci temp printează tot elementul până la ultimul element.
- În momentul în care ultimul element este atins, bucla se termină, iar funcția returnează apelul la funcția principală.
Aplicații ale listei circulare legate
- Implementarea programării round-robin în procesele de sistem și a programării circulare în grafica de mare viteză.
- Programarea token rings în rețelele de calculatoare.
- Este folosit în unități de afișare, cum ar fi panourile de magazin, care necesită parcurgerea continuă a datelor.