Enkeltkoblet liste i datastrukturer

โšก Smart oppsummering

En enkeltlenket liste er en lineรฆr, ensrettet datastruktur der hver node lagrer data og en enkelt peker til den neste noden, slik at traverseringen bare beveger seg fra topp til hale og minne allokeres dynamisk etter hvert som nye noder legges til.

  • ๐Ÿงฉ Nodestruktur: Hver node inneholder ett datafelt og ett neste pekeren til den fรธlgende noden; halenodens neste pekeren er NULL.
  • ๐Ÿ“ฆ Liste vs. array: Enkeltlenkede lister foretrekkes nรฅr elementantallet er ukjent, tilfeldig tilgang ikke er nรธdvendig, og innsetting midt i listen er vanlig.
  • โž• Innsettinger: Noder kan legges til ved hodet, ved halen, etter en matchet node eller fรธr en matchet node ved hjelp av omskrivninger av neste peker.
  • โž– Slettinger: Hvis du fjerner hodet, halen eller en sรธkt node, oppdateres nabopekere og frigjรธr det frigjorte minnet for รฅ unngรฅ lekkasjer.
  • ๐Ÿ” Gjennomgang: Bare fremovergรฅende traversering stรธttes fordi det ikke finnes noen tidligere peker, sรฅ det er ikke mulig รฅ gรฅ bakover i en enkeltlenket liste.
  • ๐Ÿ’ป C++ og Python Code: Komplette implementeringer viser rutiner for innsetting, sletting, sรธk og gjennomgang med kjรธrbar utdata.
  • ๐Ÿ“Š kompleksitet: Innsetting eller sletting av hode er O(1); sรธk og andre innsettinger og slettinger er O(n); romkompleksitet er O(n).

Enkeltlenket liste

Hva er en enkeltkoblet liste?

En enkeltlenket liste er en lineรฆr og enveis datastruktur der data lagres pรฅ nodene, og hver node er koblet til sin neste node via en lenke. Hver node inneholder et datafelt og en lenke til den neste noden. Enkeltlenkede lister kan bare navigeres i รฉn retning, mens en Dobbeltkoblet liste kan krysses i begge retninger.

Her er nodestrukturen til en enkeltkoblet liste:

Strukturen til en node i en koblet liste

Strukturen til en node i en koblet liste

Hvorfor bruke en lenket liste over en array?

Flere scenarier favoriserer en lenket liste fremfor en Array:

  • Ukjent antall elementer: Nรฅr det nรธdvendige elementantallet ikke er kjent ved kompileringstidspunktet, tildeler en lenket liste minne dynamisk etter hvert som elementer legges til.
  • Tilfeldig tilgang: Nรฅr tilfeldig indeksert tilgang ikke er nรธdvendig, er en lenket liste et passende valg.
  • Innsetting i midten: Innsetting midt i en matrise krever forskyvning av elementer. En lenket liste tillater innsetting pรฅ en hvilken som helst posisjon ved รฅ omskrive bare noen fรฅ pekere.

Operasjoner av Singly Linked List

En enkeltkoblet liste er bra for dynamisk allokering av minne. Den stรธtter standardoperasjonene til den koblede listen, dvs. innsetting, sletting, sรธking, oppdatering, sammenslรฅing av to lister og gjennomgang.

Fรธlgende operasjoner diskuteres i denne artikkelen:

  • Innsetting ved hodet
  • Innsetting ved halen
  • Setter inn etter en node
  • Setter inn fรธr en node
  • Slett hodenoden
  • Slett haleknuten
  • Sรธk og slett en node
  • Gรฅ gjennom den koblede listen

Her er et eksempel pรฅ en lenket liste med fire noder.

Eksempel pรฅ en enkeltkoblet liste

Eksempel pรฅ en enkeltkoblet liste

Innsetting i toppen av en enkeltkoblet liste

Dette er en enkel operasjon. Det er generelt kjent som รฅ pushe pรฅ en enkeltkoblet liste. En ny node opprettes og plasseres รธverst i listen.

For รฅ utfรธre denne operasjonen, fรธlg to viktige betingelser:

  1. Hvis listen er tom, blir den nyopprettede noden hovednoden, og dens neste pekeren er NULL.
  2. Hvis listen ikke er tom, blir den nye noden hovednoden, og dens neste Pekeren peker pรฅ den forrige hodenoden.

Her er pseudokoden for รฅ sette inn en node รธverst i en lenket liste:

function insertAtHead(head, value):
  newNode = Node(value)
  if head is NULL:
    head = newNode
    return head
  else:
    newNode.next = head
    return newNode

Innsetting ved hodet

Innsetting ved hodet

Innsetting pรฅ slutten av en enkeltkoblet liste

ร… sette inn en node pรฅ slutten av en lenket liste er likt รฅ sette inn i toppen. Gรฅ til halenoden, og pek deretter dens neste pekeren til den nye noden. Hvis hodet er NULL, blir den nye noden hodet.

Trinn 1) Gรฅ gjennom til neste Pekeren til den gjeldende noden blir NULL.

Trinn 2) Opprett en ny node med den angitte verdien.

Trinn 3) Tilordne den nye noden som den neste noden i halenoden.

Pseudokoden for รฅ sette inn i slutten av en enkeltliste:

function insertAtEnd(head, value):
  newNode = Node(value)
  if head is NULL:
    head = newNode
    return head
  while head.next is not NULL:
    head = head.next
  head.next = newNode
  newNode.next = NULL

Innsetting ved halen

Innsetting ved halen

Innsetting etter en node i en enkeltkoblet liste

ร… sette inn etter en node har to deler: sรธk etter mรฅlnoden og legg til en ny node etter den. Bla gjennom listen til du finner et treff, og skjรธt deretter den nye noden inn.

Trinn 1) Gรฅ gjennom til verdien til gjeldende node er lik sรธkeelementet.

Trinn 2) Angi den nye nodens neste pekeren til den gjeldende nodens neste pekeren.

Trinn 3) Peke pรฅ gjeldende nodes neste pekeren til den nye noden.

Pseudokode:

function insertAfter(head, value, searchItem):
  newNode = Node(value)
  while head.value != searchItem:
    head = head.next
  newNode.next = head.next
  head.next = newNode

Sette inn en node etter en node i enkeltlenket liste

Sette inn en node etter en node i Singly Linked List

Innsetting fรธr en node i en enkeltkoblet liste

Dette ligner pรฅ innsetting etter en node. Gรฅ gjennom den til neste node samsvarer med sรธkeverdien, og sett deretter inn den nye noden fรธr den.

Trinn 1) Gรฅ til neste nodes verdi er lik sรธkeelementet.

Trinn 2) Opprett en ny node og sett dens neste pekeren til den gjeldende nodens neste.

Trinn 3) Peke pรฅ gjeldende nodes neste til den nye noden.

function insertBefore(head, value, searchItem):
  newNode = Node(value)
  while head.next.value != searchItem:
    head = head.next
  newNode.next = head.next
  head.next = newNode

Sette inn en node fรธr en node i enkeltlenket liste

Sette inn en node fรธr en node i Singly Linked List

Slett overskriften pรฅ den enkeltkoblede listen

Hodepekeren er oppgitt som parameter. Hodenoden fjernes, og den neste noden blir den nye hodenoden. Minnet til den slettede noden mรฅ frigjรธres for รฅ unngรฅ minnelekkasjer.

Trinn 1) Tilordne den neste noden i hodet som det nye hodet.

Trinn 2) Frigjรธr det tildelte minnet til den forrige head-noden.

Trinn 3) Returner den nye hodenoden.

function deleteHead(head):
  temp = head
  head = head.next
  free(temp)
  return head

Sletting av hodet pรฅ en koblet liste

Sletter hodet til en koblet liste

Slett halen av den enkeltkoblede listen

ร… slette halenoden ligner pรฅ รฅ slette hodenoden. Forskjellen er at det kreves traversering til slutten av listen. I en enkeltkoblet liste er noden hvis neste pekeren er NULL er halenoden.

Trinn 1) Traverser til rett fรธr halenoden. Lagre gjeldende node.

Trinn 2) Frigjรธr minnet til neste node (halen).

Trinn 3) Sett den neste noden i den gjeldende noden til NULL.

function deleteTail(head):
  while head.next.next is not NULL:
    head = head.next
  free(head.next)
  head.next = NULL

Sletter halen av enkeltlenket liste

Sletter halen av enkeltlenket liste

Sรธk etter og slett en node fra en enkeltkoblet liste

Denne funksjonen utfรธrer to oppgaver: sรธke og slette. Bla gjennom til slutten av listen. Hvis en samsvarende node blir funnet, fjern den og koble den forrige nodens til pรฅ nytt. neste pekeren.

Trinn 1) Gรฅ til slutten av listen. Sjekk om gjeldende node er lik sรธkenoden.

Trinn 2) Hvis det finnes et treff, lagrer du en peker til den gjeldende noden.

Trinn 3) Ocuco neste til den forrige noden blir den neste noden til den nรฅvรฆrende noden.

Trinn 4) Slett den gjeldende noden og frigjรธr minnet.

function searchAndDelete(head, searchItem):
  while head.next.next is not NULL and head.next.value != searchItem:
    head = head.next
  temp = head.next
  head.next = head.next.next
  free(temp)

Sรธk og slett en node fra enkeltlenket liste

Sรธk og slett en node fra Liste over enkeltlenkede

Gรฅ gjennom en enkeltkoblet liste

En enkeltlenket liste stรธtter bare traversering fra topp til hale. Det finnes ingen peker til forrige node, sรฅ omvendt traversering er ikke mulig. Hver node besรธkes etter tur, og verdien skrives ut til NULL nรฅs.

Trinn 1) Gรฅ gjennom hver node til NULL er nรฅdd.

Trinn 2) Skriv ut verdien til gjeldende node.

function traverse(head):
  while head is not NULL:
    print head.value
    head = head.next

Eksempel pรฅ enkeltlenket liste i C++

#include<iostream>
using namespace std;
struct Node{
  int data;
  struct Node *next;
};
void insertAtHead(Node* &head, int value){
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  if(head != NULL){
    newNode->next = head;
  }
  head = newNode;
  cout<<"Added "<<newNode->data<<" at the front"<<endl;
}
void insertEnd(Node* &head, int value){
  if(head == NULL){
    insertAtHead(head, value);
    return;
  }
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  Node *temp = head;
  while(temp->next != NULL){
    temp = temp->next;
  }
  temp->next = newNode;
  cout<<"Added "<<newNode->data<<" at the end"<<endl;
}
void searchAndDelete(Node **headPtr, int searchItem){
  Node *temp = NULL;
  if((*headPtr)->data == searchItem){
    temp = *headPtr;
    *headPtr = (*headPtr)->next;
    free(temp);
  } else {
    Node *currentNode = *headPtr;
    while(currentNode->next != NULL){
      if(currentNode->next->data == searchItem){
        temp = currentNode->next;
        currentNode->next = currentNode->next->next;
        free(temp);
        break;
      } else {
        currentNode = currentNode->next;
      }
    }
  }
  cout<<"Deleted Node\t"<<searchItem<<endl;
}
void insertAfter(Node* &headPtr, int searchItem, int value){
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  Node *head = headPtr;
  while(head->next != NULL && head->data != searchItem){
    head = head->next;
  }
  newNode->next = head->next;
  head->next = newNode;
  cout<<"Inserted "<<value<<" after node\t"<<searchItem<<endl;
}
void insertBefore(Node* &headPtr, int searchItem, int value){
  Node* newNode = new Node();
  newNode->data = value;
  newNode->next = NULL;
  Node *head = headPtr;
  while(head->next != NULL && head->next->data != searchItem){
    head = head->next;
  }
  newNode->next = head->next;
  head->next = newNode;
  cout<<"Inserted "<<value<<" before node\t"<<searchItem<<endl;
}
void traverse(Node *headPointer){
  Node* tempNode = headPointer;
  cout<<"Traversal from head:\t";
  while(tempNode != NULL){
    cout<<tempNode->data;
    if(tempNode->next)
      cout<<" --> ";
    tempNode = tempNode->next;
  }
  cout<<endl;
}
int main(){
  Node *head = NULL;
  insertAtHead(head, 5);
  insertAtHead(head, 6);
  insertAtHead(head, 7);
  insertEnd(head, 9);
  traverse(head);
  searchAndDelete(&head, 6);
  traverse(head);
  insertAfter(head, 7, 10);
  insertBefore(head, 9, 11);
  traverse(head);
}

Produksjon

Added 5 at the front
Added 6 at the front
Added 7 at the front
Added 9 at the end
Traversal from head:    7 --> 6 --> 5 --> 9
Deleted Node    6
Traversal from head:    7 --> 5 --> 9
Inserted 10 after node  7
Inserted 11 before node 9
Traversal from head:    7 --> 10 --> 5 --> 11 --> 9

Eksempel pรฅ enkeltlenket liste i Python

class Node:
  def __init__(self, data=None, next=None):
    self.data = data
    self.next = next
class SinglyLinkedList:
  def __init__(self):
    self.head = None
  def insertAtHead(self, value):
    newNode = Node(data=value)
    if self.head is not None:
      newNode.next = self.head
    self.head = newNode
    print(f'Added {newNode.data} at the front.')
  def insertAtEnd(self, value):
    if self.head is None:
      self.insertAtHead(value)
      return
    newNode = Node(value)
    temp = self.head
    while temp.next is not None:
      temp = temp.next
    temp.next = newNode
    print(f'Added {newNode.data} at the end.')
  def searchAndDelete(self, searchItem):
    if self.head is None:
      return
    if self.head.data == searchItem:
      self.head = self.head.next
      print(f'Deleted node\t{searchItem}')
      return
    currentNode = self.head
    while currentNode.next is not None:
      if currentNode.next.data == searchItem:
        currentNode.next = currentNode.next.next
        print(f'Deleted node\t{searchItem}')
        return
      currentNode = currentNode.next
  def insertAfter(self, searchItem, value):
    newNode = Node(data=value)
    temp = self.head
    while temp.next is not None and temp.data != searchItem:
      temp = temp.next
    newNode.next = temp.next
    temp.next = newNode
    print(f'Inserted {value} after node\t{searchItem}')
  def insertBefore(self, searchItem, value):
    newNode = Node(data=value)
    temp = self.head
    while temp.next is not None and temp.next.data != searchItem:
      temp = temp.next
    newNode.next = temp.next
    temp.next = newNode
    print(f'Inserted {value} before node\t{searchItem}')
  def traverse(self):
    temp = self.head
    print("Traversing from head:\t", end="")
    while temp:
      print("{}\t".format(temp.data), end="")
      temp = temp.next
    print()
singlyLinkedList = SinglyLinkedList()
singlyLinkedList.insertAtHead(5)
singlyLinkedList.insertAtHead(6)
singlyLinkedList.insertAtHead(7)
singlyLinkedList.insertAtEnd(9)
singlyLinkedList.traverse()
singlyLinkedList.searchAndDelete(6)
singlyLinkedList.traverse()
singlyLinkedList.insertAfter(7, 10)
singlyLinkedList.insertBefore(9, 11)
singlyLinkedList.traverse()

Produksjon

Added 5 at the front.
Added 6 at the front.
Added 7 at the front.
Added 9 at the end.
Traversing from head:   7       6       5       9
Deleted node    6
Traversing from head:   7       5       9
Inserted 10 after node  7
Inserted 11 before node 9
Traversing from head:   7       10      5       11      9

Kompleksiteten til enkeltlenkede liste

Det finnes to typer kompleksitet: tidskompleksitet og romkompleksitet. Den verste og gjennomsnittlige tidskompleksiteten er den samme for en enkeltlenket liste.

Best-case tidskompleksitet:

  • Innsetting ved hodet kan gjรธres i O(1). Ingen traversering innenfor listen er nรธdvendig.
  • Sรธk og sletting kan gjรธres i O(1) hvis mรฅlelementet er ved hovednoden.

Gjennomsnittlig sakstidskompleksitet:

  • Innsetting i en lenket liste tar O(n), hvor n er det totale antallet elementer.
  • Sรธk og sletting kan ogsรฅ ta O(n), fordi mรฅlelementet kan befinne seg hvor som helst opp til halenoden.

Romkompleksitet av enkeltlenket liste

En enkeltkoblet liste allokerer minne dynamisk. For รฅ lagre n elementer, tildeler den n minneenheter. Sรฅ romkompleksiteten er O(n).

Bruksomrรฅder for enkeltlenket liste

Enkeltkoblede lister vises mange steder der kun fremoverrettet traversering og dynamisk minne er nyttige:

  • Stabler og kรธer: Underliggende lagring for LIFO-stabler og FIFO-kรธer bygget fra noder.
  • Kjeding av hash-tabeller: Kollisjoner lรธses ved รฅ kjede sammen oppfรธringer i en enkeltkoblet liste per bรธtte.
  • Nรฆrhetslister: Sparse grafer bruker en enkeltkoblet liste over naboer for hvert hjรธrne.
  • Symboltabeller: Kompilatorer og tolker kjeder identifikatorer til en enkeltkoblet liste per omfang.
  • Minneallokatorer: Gratislistetildelere track frie blokker som en enkeltlenket liste.

Spรธrsmรฅl og svar

Enkeltkoblede lister kjeder treningseksempler, minibatcher og ledige minneblokker i AI-rammeverk, noe som muliggjรธr dynamiske kรธer for strรธmming av inndata og lรฅsefrie datapipeliner som skaleres med modellens etterspรธrsel.

Ja. GitHub Copilot og GPT kan produsere en fullstendig enkeltlenket liste i C. C++, Java, Pythoneller JavaSkript, inkludert innsetting, sletting, reversering, syklusdeteksjon og enhetstester.

En enkeltlenket liste har รฉn nestepeker og beveger seg bare fremover. En dobbeltlenket liste har bรฅde neste- og forrigepekere og beveger seg i begge retninger, men bruker mer minne per node.

Vanlige bruksomrรฅder inkluderer stakk- og kรธimplementeringer, hash-tabellkjedening, tilstรธtende lister for sparsomme grafer, symboltabeller i kompilatorer, frilistetildelere og angrehistorikk i lette editorer.

Innsetting eller sletting ved hodet er O(1). Innsetting ved halen, sรธk, innsetting ved en posisjon og sletting av en spesifikk node koster alle O(n) fordi traversering er nรธdvendig fra hodet.

Lenkede lister vokser og krymper under kjรธretid, setter inn eller sletter i O(1) nรฅr posisjonen er kjent, og trenger aldri sammenhengende minne. Arrayer tilbyr O(1) tilfeldig tilgang og bedre hurtigbufferlokalitet.

Gรฅ gjennom listen med tre pekere, prev, curr og next. Lagre curr.next pรฅ hvert trinn, pek curr.next pรฅ prev, og flytt prev og curr fremover. Returner prev som den nye overskriften.

Floyds skilpadde-og-hare-algoritme bruker to pekere som beveger seg med ulik hastighet. Hvis de noen gang mรธtes, inneholder listen en syklus. Ellers nรฅr den raske pekeren NULL, og ingen syklus eksisterer.

Oppsummer dette innlegget med: