Circular Linked List: Výhody a nevýhody
⚡ Chytré shrnutí
Kruhové propojené seznamy uspořádávají uzly tak, aby se poslední uzel vracel k prvnímu, což vám dává spojitou strukturu bez hodnot NULL, která je vhodná pro plánování typu round robin, token ringy a jakýkoli pracovní postup, který vyžaduje bezproblémový průchod.
Co je kruhový propojený seznam?
Kruhový propojený seznam je posloupnost uzlů uspořádaných tak, že každý uzel lze znovutracsám pro sebe. Každý „uzel“ je sebereferenční prvek s ukazateli na jeden nebo dva uzly v jeho bezprostředním okolí.
Níže je znázorněn kruhový propojený seznam se 3 uzly.
Zde vidíte, že každý uzel je znovutracsám pro sebe. Výše uvedený příklad je kruhový jednoduše propojený seznam.
Poznámka: Nejjednodušší kruhový propojený seznam je jeden uzel, jehož další ukazatel tracvrací se zpět k sobě, jak je znázorněno níže.
Basic Operace v kruhových propojených seznamech
Tři základní operace na kruhovém propojeném seznamu jsou:
- Vložení
- Vymazání a
- Traverz
- Vkládání je proces umístění uzlu na určené místo v kruhovém propojeném seznamu.
- Odstranění je proces odstranění existujícího uzlu z propojeného seznamu. Uzel lze identifikovat podle výskytu jeho hodnoty nebo podle jeho polohy.
- Procházení cyklického propojeného seznamu je proces zobrazení obsahu celého propojeného seznamu a následného zobrazení jeho obsahu.traczpět ke zdrojovému uzlu.
V následující části je vysvětleno, jak vkládání funguje, a dva možné typy vkládání v kruhovém jednotlivě propojeném seznamu.
Vložení Operavání
Nejprve vytvoříte jeden uzel, jehož další ukazatel ukazuje zpět na sebe, jak je znázorněno níže. Bez tohoto počátečního uzlu se první vložený uzel stane prvním uzlem v seznamu.
Dále jsou dvě možnosti:
- Vložení na aktuální pozici v cyklickém propojeném seznamu. To odpovídá vložení na začátek nebo konec běžného jednoduše propojeného seznamu – v cyklickém propojeném seznamu jsou začátek a konec stejným bodem.
- Vložení za indexovaný uzel. Uzel by měl být identifikován indexovým číslem odpovídajícím jeho hodnotě prvku.
Chcete-li vložit na začátek nebo konec kruhového propojeného seznamu – tedy na pozici, kde byl přidán vůbec první uzel – postupujte takto:
- Budete muset přerušit stávající vlastní propojení se stávajícím uzlem
- Další ukazatel nového uzlu se propojí se stávajícím uzlem.
- Další ukazatel posledního uzlu bude ukazovat na vložený uzel.
POZNÁMKA: Ukazatel, který označuje začátek nebo konec kružnice, lze přiřadit libovolnému uzlu. Procházení se stále vrátí do stejného uzlu, jak bude popsáno dále v tomto článku.
Kroky v (a) i-iii jsou uvedeny níže:
(Stávající uzel)
Krok 1) Přerušte stávající odkaz
Krok 2) Vytvořit dopředný odkaz (z nového uzlu na existující uzel)
Krok 3) Vytvořte smyčkový odkaz na první uzel
Dále vyzkoušíte vložení za uzel.
Například vložte „HODNOTA2“ za uzel obsahující „HODNOTA0“ za předpokladu, že počátečním bodem je uzel s „HODNOTA0“.
- Přerušte propojení mezi prvním a druhým uzlem a umístěte uzel s hodnotou „VALUE2“ mezi ně.
- Další ukazatel prvního uzlu odkazuje na nový uzel a další ukazatel nového uzlu odkazuje na to, co bylo dříve druhým uzlem.
- Zbytek uspořádání zůstává nezměněn. Všechny uzly jsou znovutracsami sobě schopni.
POZNÁMKA: Protože je uspořádání cyklické, je postup vkládání uzlu stejný bez ohledu na zvolenou pozici. Ukazatel, který uzavírá cyklus, se chová jako jakýkoli jiný ukazatel v seznamu.
Toto je zobrazeno níže:
(Řekněme, že existují pouze dva uzly. Toto je triviální případ)
Krok 1) Odstraňte vnitřní spoj mezi připojenými uzly
Krok 2) Připojte uzel na levé straně k novému uzlu
Krok 3) Připojte nový uzel k uzlu na pravé straně.
vymazání Operavání
Předpokládejme kruhový propojený seznam se 3 uzly. Dva případy odstranění jsou:
- Odstranění aktuálního prvku
- Smazání po prvku.
Smazání na začátku/konci:
- Přechod k prvnímu uzlu z posledního uzlu.
- Mazání od konce vyžaduje pouze jeden krok průchodu, od posledního uzlu k prvnímu uzlu.
- Odstraňte propojení mezi posledním uzlem a prvním uzlem.
- Propojte poslední uzel s dalším prvkem prvního uzlu.
- Uvolněte první uzel.
(Stávající nastavení)
Krok 1) Odstraňte kruhový článek
Krok 2) Odstraňte spojení mezi prvním a dalším, propojte poslední uzel s uzlem následujícím po prvním
Krok 3) Uvolnit / zrušit alokaci prvního uzlu
Smazání po uzlu:
- Procházejte, dokud se nenajde další uzel, který má být smazán.
- Přejděte k dalšímu uzlu a umístěte ukazatel na předchozí uzel.
- Připojte předchozí uzel k uzlu za aktuálním uzlem pomocí jeho dalšího ukazatele.
- Uvolněte aktuální (odpojený) uzel.
Krok 1) Řekněme, že potřebujeme odstranit uzel s „VALUE1“.
Krok 2) Odstraňte propojení mezi předchozím uzlem a aktuálním uzlem a poté propojte předchozí uzel přímo s uzlem, na který odkazuje další ukazatel aktuálního uzlu (uzel za VALUE1).
Krok 3) Uvolněte nebo uvolněte aktuální uzel.
Procházení kruhového propojeného seznamu
Chcete-li procházet cyklický propojený seznam od posledního ukazatele, nejprve zkontrolujte, zda je poslední ukazatel NULL. Pokud není NULL, zkontrolujte, zda seznam obsahuje pouze jeden prvek. V opačném případě procházejte seznam s dočasným ukazatelem, dokud se znovu nedostanete k poslednímu ukazateli, jak je znázorněno v animaci níže.
Výhody kruhového propojeného seznamu
Některé z výhod kruhových propojených seznamů jsou:
- Žádný požadavek na přiřazení NULL v kódu. Kruhový seznam nikdy neukazuje na ukazatel NULL, pokud není plně uvolněn.
- Kruhové propojené seznamy jsou výhodné pro operace na konci seznamů, protože začátek a konec se shodují. Algorithms Například plánování typu round robin může čistě procházet procesy ve frontě, aniž by narazilo na visící nebo NULL ukazatele.
- Kruhový propojený seznam stále podporuje všechny běžné operace jednoduše propojeného seznamu. Kruhový dvojitě propojený seznam může dokonce eliminovat potřebu procházení celého seznamu k nalezení prvku – v nejhorším případě se cíl nachází naproti počátečnímu ukazateli, takže je třeba projít maximálně polovinu seznamu.
Nevýhody kruhového propojeného seznamu
Nevýhody použití kruhového propojeného seznamu jsou níže:
- Kruhové seznamy jsou složitější než jednotlivě propojené seznamy.
- RevObrácení cyklického seznamu je složitější než obrácení jednoduše nebo dvojitě propojeného seznamu.
- Pokud není ukončení smyčky ošetřeno pečlivě, kód pro průchod může vstoupit do nekonečné smyčky.
- Je těžší najít konec seznamu a zapsat správné podmínky pro řízení smyčky.
- Vkládání na začátek vyžaduje procházení celého seznamu, aby se dosáhlo posledního uzlu (z hlediska implementace).
Jednotlivě propojený seznam jako kruhový propojený seznam
Doporučujeme vám přečíst si a implementovat níže uvedený kód v jazyce C. Znázorňuje aritmetiku ukazatelů spojenou s kruhovým jednoduše propojeným seznamem.
#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() { ...
Vysvětlení kódu:
- První dva řádky kódu jsou nezbytné zahrnuté hlavičkové soubory.
- V další části je definována struktura každého sebereferenčního uzlu. Obsahuje hodnotu a ukazatel stejného typu jako struktura.
- Každá instance struktury propojuje s dalšími objekty struktury stejného typu.
- Existují různé funkční prototypy pro:
- Přidání prvku do prázdného propojeného seznamu
- Vkládání na aktuálně ukázal pozice kruhového propojeného seznamu.
- Vkládání za konkrétní indexován hodnotu v propojeném seznamu.
- Odstranění/smazání po určitém indexován hodnotu v propojeném seznamu.
- Odstranění na aktuálně vytyčené pozici kruhového propojeného seznamu
- Poslední funkce vytiskne každý prvek kruhovým průchodem v libovolném stavu propojeného seznamu.
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)
Vysvětlení kódu:
- Pro kód addToEmpty alokujte prázdný uzel pomocí funkce malloc().
- Umístěte příchozí data do dočasného uzlu.
- Přiřaďte dočasnému uzlu hodnotu last a nastavte jeho další ukazatel na sebe sama, aby jediný uzel ukazoval zpět na sebe sama.
- Vrátí poslední ukazatel zpět do kontextu main() / aplikace.
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; …
Vysvětlení kódu
- Pokud je seznam prázdný, předejte jej funkci addToEmpty() a vraťte řízení.
- Vytvořte dočasný uzel, který se umístí za aktuální uzel.
- Propojte ukazatele, jak je znázorněno na výše uvedeném diagramu.
- Vrátí poslední ukazatel, který odpovídá vzoru použitému v předchozí funkci.
... 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"); ...
Vysvětlení kódu:
- Pokud je seznam prázdný, ignorujte klíč pro vyhledávání, přidejte aktuální položku jako jediný uzel v seznamu a vraťte řízení.
- V každé iteraci smyčky do-while předchozí ukazatel obsahuje výsledek posledního procházení.
- Teprve poté dochází k dalšímu kroku průchodu.
- Příkaz do-while se ukončí, když jsou nalezena cílová data nebo když temp opět dosáhne posledního ukazatele. Následující blok kódu rozhoduje, co se má s nalezenou položkou udělat.
...
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)
...
Vysvětlení kódu:
- Pokud byl procházen celý seznam, ale položka nebyla nalezena, zobrazí se zpráva „Prvek nenalezen“ a řízení se vrátí volajícímu.
- Pokud je cílový uzel nalezen, alokujte nový uzel pro hodnotu, která se má vložit.
- Odkaz předchozí uzel k novému uzlu a propojit další ukazatel nového uzlu s proměnnou temp (traversal).
- Tím se nový prvek umístí bezprostředně za cílový uzel v cyklickém propojeném seznamu. Řízení se poté vrátí volajícímu.
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)
Vysvětlení kódu
- Chcete-li odstranit poslední (aktuální) uzel, nejprve zkontrolujte, zda je seznam prázdný. Pokud ano, nelze odstranit žádný prvek.
- Dočasná proměnná posune jeden odkaz vpřed.
- Propojte poslední ukazatel s uzlem za prvním uzlem.
- Uvolněte dočasný ukazatel pro uvolnění nepropojeného uzlu.
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"); ...
Vysvětlení kódu
- Stejně jako u předchozí funkce pro odstranění nejprve zkontrolujte, zda je seznam prázdný. Pokud ano, nelze žádný prvek odstranit.
- Dvě ukazatele jsou přiřazeny konkrétní pozice k nalezení prvku, který má být odstraněn.
- Ukazatele se posouvají jeden za druhým (dočasné zobrazení předchozích tras).
- Procházení pokračuje, dokud není nalezen cílový prvek nebo dokud další ukazatel znovu nedosáhne posledního uzlu.
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;
Vysvětlení programu
- Pokud je procházen celý propojený seznam bez nalezení cíle, zobrazí se zpráva „Prvek nenalezen“.
- Jinak se element odpojí a uvolní v krocích 3 a 4.
- Předchozí ukazatel je propojen s uzlem, na který odkazuje další ukazatel dočasné proměnné (uzel následující po uzlu, který je mazán).
- Dočasný ukazatel se poté uvolní.
... 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; } }
Vysvětlení kódu
- Procházení funkcí Peek Traversal není možné, pokud existuje nula uzlů – uživatel musí nejprve alokovat nebo vložit uzel.
- Pokud existuje pouze jeden uzel, není vyžadován žádný průchod – obsah uzlu se vypíše přímo a smyčka while se neprovede.
- Pokud existuje více než jeden uzel, dočasná funkce vypíše všechny položky až do posledního prvku.
- V okamžiku, kdy je dosaženo posledního prvku, smyčka se ukončí a funkce vrátí řízení do main().
Aplikace Circular Linked List
- Implementace kruhového plánování v systémových procesech a kruhového plánování ve vysokorychlostní grafice.
- Plánování token-ringu v počítačových sítích.
- Používá se v zobrazovacích jednotkách, jako jsou digitální nástěnky obchodů, které vyžadují nepřetržitý přenos dat.





























