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.

  • ???? Definice: Každý uzel obsahuje hodnotu a ukazatel na další uzel a ukazatel na další uzel posledního uzlu se odkazuje zpět na první uzel, čímž vzniká uzavřený cyklus.
  • 📌 Jádro Operaakce: Vkládání, mazání a procházení se točí kolem aktualizace jednoho nebo dvou ukazatelů na další větu při zachování cyklu.
  • 🛠️ Implementace v jazyce C: Uzly založené na struktuře s vkládáním s podporou malloc a mazáním s volnou podporou pokrývají jak případy na aktuální pozici, tak i případy za uzlem.
  • (Tj. Výhody: Žádné dereference s hodnotou NULL, plynulé přechody mezi konci a dvojitě cyklické varianty, které snižují počet vyhledávání v nejhorším případě na polovinu.
  • ⚠️ Nevýhody: Složitější ovládání smyček, vyšší složitost než u jednoduše propojených seznamů a nekonečné smyčky, pokud je ukončení zapsáno nesprávně.
  • 🎯 Aplikace: Plánování CPU typu round-robin, sítě token-ring, kruhové vyrovnávací paměti, seznamy skladeb médií a jednotky kontinuálního zobrazení.

Kruhový propojený seznam

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.

Kruhový propojený seznam

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.

Kruhový propojený seznam

Basic Operace v kruhových propojených seznamech

Tři základní operace na kruhovém propojeném seznamu jsou:

  1. Vložení
  2. Vymazání a
  3. 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.

Vložení Operavání

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:

Vložení Operavání

(Stávající uzel)

Vložení Operavání

Krok 1) Přerušte stávající odkaz

Vložení Operavání

Krok 2) Vytvořit dopředný odkaz (z nového uzlu na existující uzel)

Vložení Operavání

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:

Vložení Operavání

(Řekněme, že existují pouze dva uzly. Toto je triviální případ)

Vložení Operavání

Krok 1) Odstraňte vnitřní spoj mezi připojenými uzly

Vložení Operavání

Krok 2) Připojte uzel na levé straně k novému uzlu

Vložení Operavání

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:

  1. Přechod k prvnímu uzlu z posledního uzlu.
  2. Mazání od konce vyžaduje pouze jeden krok průchodu, od posledního uzlu k prvnímu uzlu.
  3. Odstraňte propojení mezi posledním uzlem a prvním uzlem.
  4. Propojte poslední uzel s dalším prvkem prvního uzlu.
  5. Uvolněte první uzel.

vymazání Operavání

(Stávající nastavení)

vymazání Operavání

Krok 1) Odstraňte kruhový článek

vymazání Operavání

Krok 2) Odstraňte spojení mezi prvním a dalším, propojte poslední uzel s uzlem následujícím po prvním

vymazání Operavání

Krok 3) Uvolnit / zrušit alokaci prvního uzlu

Smazání po uzlu:

  1. Procházejte, dokud se nenajde další uzel, který má být smazán.
  2. Přejděte k dalšímu uzlu a umístěte ukazatel na předchozí uzel.
  3. Připojte předchozí uzel k uzlu za aktuálním uzlem pomocí jeho dalšího ukazatele.
  4. Uvolněte aktuální (odpojený) uzel.

vymazání Operavání

Krok 1) Řekněme, že potřebujeme odstranit uzel s „VALUE1“.

vymazání Operavání

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).

vymazání Operavání

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.

Procházení kruhového propojeného seznamu

Výhody kruhového propojeného seznamu

Některé z výhod kruhových propojených seznamů jsou:

  1. Žádný požadavek na přiřazení NULL v kódu. Kruhový seznam nikdy neukazuje na ukazatel NULL, pokud není plně uvolněn.
  2. 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.
  3. 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:

  1. Kruhové seznamy jsou složitější než jednotlivě propojené seznamy.
  2. RevObrácení cyklického seznamu je složitější než obrácení jednoduše nebo dvojitě propojeného seznamu.
  3. Pokud není ukončení smyčky ošetřeno pečlivě, kód pro průchod může vstoupit do nekonečné smyčky.
  4. Je těžší najít konec seznamu a zapsat správné podmínky pro řízení smyčky.
  5. 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()
{
...

Jednotlivě propojený seznam

Vysvětlení kódu:

  1. První dva řádky kódu jsou nezbytné zahrnuté hlavičkové soubory.
  2. V další části je definována struktura každého sebereferenčního uzlu. Obsahuje hodnotu a ukazatel stejného typu jako struktura.
  3. Každá instance struktury propojuje s dalšími objekty struktury stejného typu.
  4. Existují různé funkční prototypy pro:
    1. Přidání prvku do prázdného propojeného seznamu
    2. Vkládání na aktuálně ukázal pozice kruhového propojeného seznamu.
    3. Vkládání za konkrétní indexován hodnotu v propojeném seznamu.
    4. Odstranění/smazání po určitém indexován hodnotu v propojeném seznamu.
    5. Odstranění na aktuálně vytyčené pozici kruhového propojeného seznamu
  5. 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)

Jednotlivě propojený seznam

Vysvětlení kódu:

  1. Pro kód addToEmpty alokujte prázdný uzel pomocí funkce malloc().
  2. Umístěte příchozí data do dočasného uzlu.
  3. 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.
  4. 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;
&#8230;

Jednotlivě propojený seznam

Vysvětlení kódu

  1. Pokud je seznam prázdný, předejte jej funkci addToEmpty() a vraťte řízení.
  2. Vytvořte dočasný uzel, který se umístí za aktuální uzel.
  3. Propojte ukazatele, jak je znázorněno na výše uvedeném diagramu.
  4. 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");
...

Jednotlivě propojený seznam

Vysvětlení kódu:

  1. 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í.
  2. V každé iteraci smyčky do-while předchozí ukazatel obsahuje výsledek posledního procházení.
  3. Teprve poté dochází k dalšímu kroku průchodu.
  4. 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)
...

Jednotlivě propojený seznam

Vysvětlení kódu:

  1. 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.
  2. Pokud je cílový uzel nalezen, alokujte nový uzel pro hodnotu, která se má vložit.
  3. Odkaz předchozí uzel k novému uzlu a propojit další ukazatel nového uzlu s proměnnou temp (traversal).
  4. 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)

Jednotlivě propojený seznam

Vysvětlení kódu

  1. Chcete-li odstranit poslední (aktuální) uzel, nejprve zkontrolujte, zda je seznam prázdný. Pokud ano, nelze odstranit žádný prvek.
  2. Dočasná proměnná posune jeden odkaz vpřed.
  3. Propojte poslední ukazatel s uzlem za prvním uzlem.
  4. 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");
...

Jednotlivě propojený seznam

Vysvětlení kódu

  1. Stejně jako u předchozí funkce pro odstranění nejprve zkontrolujte, zda je seznam prázdný. Pokud ano, nelze žádný prvek odstranit.
  2. Dvě ukazatele jsou přiřazeny konkrétní pozice k nalezení prvku, který má být odstraněn.
  3. Ukazatele se posouvají jeden za druhým (dočasné zobrazení předchozích tras).
  4. 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;

Jednotlivě propojený seznam

Vysvětlení programu

  1. Pokud je procházen celý propojený seznam bez nalezení cíle, zobrazí se zpráva „Prvek nenalezen“.
  2. Jinak se element odpojí a uvolní v krocích 3 a 4.
  3. 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).
  4. 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;
    }
}

Jednotlivě propojený seznam

Vysvětlení kódu

  1. Procházení funkcí Peek Traversal není možné, pokud existuje nula uzlů – uživatel musí nejprve alokovat nebo vložit uzel.
  2. 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.
  3. Pokud existuje více než jeden uzel, dočasná funkce vypíše všechny položky až do posledního prvku.
  4. 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.

Nejčastější dotazy

Asistenti umělé inteligence, jako jsou struktury uzlů scaffoldu GitHub Copilot a ChatGPT, vkládací moduly založené na malloc a cyklicky bezpečné smyčky procházení. Vývojáři před sloučením do produkčních datových struktur kontrolují vygenerovaný kód, zda obsahuje správné podmínky ukončení a vyčištění paměti.

Kanály strojového učení používají kruhové vyrovnávací paměti postavené na kruhových propojených seznamech k uchovávání postupných oken streamovaných dat, vzorků vyrovnávací paměti pro agenty posilovacího učení a cyklických front pro pracovníky typu producent-konzument, kteří zásobují trénovací dávky.

Jednoducho propojený seznam končí ukazatelem NULL, zatímco poslední uzel cyklického propojeného seznamu ukazuje zpět na první uzel. Tento uzavřený cyklus odstraňuje kontroly NULL na konci a podporuje nepřetržitý průchod v jediné smyčce.

Kruhový dvojitě propojený seznam má dva ukazatele na uzel – další a předchozí – a oba konce se k sobě vracejí smyčkou. Tato struktura podporuje obousměrný průchod a vyhledávání v nejhorším případě maximálně o polovině délky seznamu.

Floydův algoritmus želvy a zajíce používá dva ukazatele pohybující se různými rychlostmi. Pokud se setkají, existuje cyklus. Probíhá v čase O(n) a s využitím O(1) prostoru navíc a je standardním řešením pro detekci cyklů v rozhovorech.

Vkládání nebo mazání na aktuální pozici v cyklickém propojeném seznamu probíhá za O(1). OperaFunkce cílící na konkrétní hodnotu nebo index běží za O(n), protože pro nalezení cílového uzlu je nutné projít celý seznam.

OperaPlánovače systémů ting je používají pro plánování CPU typu round-robin, sítě token-ring předávají řízení mezi stanicemi, přehrávače médií cyklují mezi seznamy skladeb a vestavěné systémy používají kruhové vyrovnávací paměti podpořené kruhovými seznamy pro streamy senzorů.

Mezi běžné chyby patří zapomenutí aktualizace obou ukazatelů koncových bodů po vložení nebo odstranění, vynechání podmínky ukončení a loo.ping navždy, uvolnění uzlu bez opětovného propojení jeho sousedů a únik paměti při zahození seznamu.

Shrňte tento příspěvek takto: