Enkeltforbundet liste i datastrukturer
โก Smart opsummering
En enkeltkoblet liste er en lineรฆr, ensrettet datastruktur, hvor hver node lagrer data og en enkelt pointer til den nรฆste node, sรฅ gennemlรธbet kun bevรฆger sig fra top til hale, og hukommelse allokeres dynamisk, efterhรฅnden som nye noder tilfรธjes.

Hvad er en enkeltstรฅende liste?
En enkeltstรฅende linket liste er en lineรฆr og ensrettet datastruktur, hvor data gemmes pรฅ noderne, og hver node er forbundet via et link til sin nรฆste node. Hver node indeholder et datafelt og et link til den nรฆste node. Enkeltstรฅende linkede lister kan kun gennemlรธbes i รฉn retning, hvorimod en Dobbeltforbundet liste kan gennemkรธres i begge retninger.
Her er nodestrukturen for en enkeltstรฅende linket liste:
Struktur af en node i en sammenkรฆdet liste
Hvorfor bruge en linket liste frem for et array?
Flere scenarier favoriserer en linket liste frem for en Array:
- Ukendt antal elementer: Nรฅr det nรธdvendige antal elementer ikke er kendt pรฅ kompileringstidspunktet, allokerer en linket liste hukommelse dynamisk, efterhรฅnden som elementer tilfรธjes.
- Tilfรฆldig adgang: Nรฅr tilfรฆldig indekseret adgang ikke er nรธdvendig, er en linket liste et passende valg.
- Indsรฆttelse i midten: Indsรฆttelse midt i et array krรฆver forskydning af elementer. En linket liste tillader indsรฆttelse pรฅ en hvilken som helst position ved kun at omskrive et par pointere.
Operationer af Singly Linked List
En enkeltstรฅende linket liste er god til dynamisk allokering af hukommelse. Den understรธtter standardoperationerne for den linkede liste, dvs. indsรฆttelse, sletning, sรธgning, opdatering, sammenlรฆgning af to lister og gennemgang.
Fรธlgende operationer diskuteres i denne artikel:
- Indfรธring ved hovedet
- Indfรธring ved hale
- Indsรฆttelse efter en node
- Indsรฆttelse fรธr en node
- Slet hovedknuden
- Slet haleknuden
- Sรธg og slet en node
- Gennemgang af den linkede liste
Her er et eksempel pรฅ en linket liste med fire noder.
Eksempel pรฅ en enkeltstรฅende liste
Indsรฆttelse i toppen af โโen enkeltstรฅende linket liste
Dette er en simpel operation. Det er generelt kendt som at pushe til en enkeltstรฅende linket liste. En ny node oprettes og placeres รธverst pรฅ listen.
For at udfรธre denne operation skal du fรธlge to vigtige betingelser:
- Hvis listen er tom, bliver den nyoprettede node hovednoden, og dens nรฆste pointeren er NULL.
- Hvis listen ikke er tom, bliver den nye node hovednoden, og dens nรฆste Markรธren peger pรฅ den forrige hovedknude.
Her er pseudokoden til at indsรฆtte en node i toppen af โโen linket liste:
function insertAtHead(head, value): newNode = Node(value) if head is NULL: head = newNode return head else: newNode.next = head return newNode
Indsรฆttelse ved hovedet
Indsรฆttelse i slutningen af โโen enkeltstรฅende linket liste
Indsรฆttelse af en node i slutningen af โโen linket liste svarer til at indsรฆtte i toppen. Gรฅ til den bageste node, og peg derefter dens nรฆste pegeren til den nye node. Hvis head er NULL, bliver den nye node til head.
Trin 1) Gennemgรฅ indtil nรฆste Pointeren for den aktuelle node bliver NULL.
Trin 2) Opret en ny node med den angivne vรฆrdi.
Trin 3) Tildel den nye knude som den nรฆste knude pรฅ haleknuden.
Pseudokoden til indsรฆttelse i slutningen af โโen enkelt liste:
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
Indfรธring ved halen
Indsรฆttelse efter en node i en enkeltstรฅende linket liste
Indsรฆttelse efter en node har to dele: sรธg efter mรฅlnoden og tilfรธj en ny node efter den. Gennemgรฅ listen, indtil der findes et match, og splejs derefter den nye node ind.
Trin 1) Gennemgรฅ indtil vรฆrdien af โโden aktuelle node er lig med sรธgeelementet.
Trin 2) Indstil den nye nodes nรฆste peger til den aktuelle nodes nรฆste markรธr.
Trin 3) Peg pรฅ den nuvรฆrende nodes nรฆste pegeren til den nye node.
Pseudokode:
function insertAfter(head, value, searchItem): newNode = Node(value) while head.value != searchItem: head = head.next newNode.next = head.next head.next = newNode
Indsรฆttelse af en node efter en node i Singly Linked List
Indsรฆttelse fรธr en node i en enkeltstรฅende linket liste
Dette svarer til indsรฆttelse efter en node. Gรฅ gennem sรธgefeltet, indtil den nรฆste node matcher sรธgevรฆrdien, og indsรฆt derefter den nye node fรธr den.
Trin 1) Kรธr indtil den nรฆste nodes vรฆrdi er lig med sรธgeelementet.
Trin 2) Opret en ny node og indstil dens nรฆste peger til den aktuelle nodes nรฆste.
Trin 3) Peg pรฅ den nuvรฆrende nodes nรฆste til den nye node.
function insertBefore(head, value, searchItem): newNode = Node(value) while head.next.value != searchItem: head = head.next newNode.next = head.next head.next = newNode
Indsรฆttelse af en node fรธr en node i Singly Linked List
Slet overskriften pรฅ den enkeltvis sammenkรฆdede liste
Hovedpointen angives som parameter. Hovednoden fjernes, og den nรฆste node bliver den nye hovednode. Hukommelsen for den slettede node skal frigรธres for at undgรฅ hukommelseslรฆkager.
Trin 1) Tildel den nรฆste node i hovedet som det nye hoved.
Trin 2) Frigรธr den allokerede hukommelse fra den forrige head node.
Trin 3) Returner den nye hovedknude.
function deleteHead(head): temp = head head = head.next free(temp) return head
Sletning af hovedet pรฅ en linket liste
Slet halen af โโden enkeltvis sammenkรฆdede liste
Sletning af halenoden svarer til sletning af hovednoden. Forskellen er, at det er nรธdvendigt at gรฅ til slutningen af โโlisten. I en enkeltstรฅende linket liste er den node, hvis nรฆste pointeren er NULL er haleknuden.
Trin 1) Gรฅ indtil lige fรธr haleknuden. Gem den aktuelle node.
Trin 2) Frigรธr hukommelsen til den nรฆste node (halen).
Trin 3) Sรฆt den nรฆste node i den aktuelle node til NULL.
function deleteTail(head): while head.next.next is not NULL: head = head.next free(head.next) head.next = NULL
Sletning af halen af โโenkeltforbundet liste
Sรธg og slet en node fra en enkeltstรฅende linket liste
Denne funktion udfรธrer to opgaver: sรธgning og sletning. Naviger til slutningen af โโlisten. Hvis der findes en matchende node, skal du fjerne den og linke den forrige nodes sammen igen. nรฆste markรธr.
Trin 1) Gรฅ til slutningen af โโlisten. Kontroller, om den aktuelle node er lig med sรธgenoden.
Trin 2) Hvis der findes et match, gemmes en pointer til den aktuelle node.
Trin 3) nรฆste for den forrige node bliver den nรฆste node for den nuvรฆrende node.
Trin 4) Slet den aktuelle node og frigรธr dens hukommelse.
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รธg og slet en node fra Liste med enkelt lรฆnker
Gennemgรฅ en enkeltstรฅende linket liste
En enkeltstรฅende linket liste understรธtter kun gennemgang fra top til hale. Der er ingen pointer til den forrige node, sรฅ omvendt gennemgang er ikke mulig. Hver node besรธges efter tur, og dens vรฆrdi udskrives, indtil NULL nรฅs.
Trin 1) Gennemlรธb hver node, indtil NULL er nรฅet.
Trin 2) Udskriv vรฆrdien af โโden aktuelle node.
function traverse(head): while head is not NULL: print head.value head = head.next
Eksempel pรฅ enkeltforbundet 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); }
Produktion
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รฅ enkeltforbundet 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()
Produktion
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 af โโenkeltforbundet liste
Der er to typer kompleksitet: tidskompleksitet og rumkompleksitet. Den vรฆrste og gennemsnitlige tidskompleksitet er den samme for en enkeltstรฅende linket liste.
Bedste-case tidskompleksitet:
- Indsรฆttelse ved toppen kan udfรธres i O(1). Ingen gennemgang inden for listen er nรธdvendig.
- Sรธgning og sletning kan udfรธres i O(1), hvis mรฅlelementet er ved hovednoden.
Gennemsnitlig sagstidskompleksitet:
- Indsรฆttelse i en linket liste krรฆver O(n), hvor n er det samlede antal elementer.
- Sรธgning og sletning kan ogsรฅ tage O(n), fordi mรฅlelementet kan befinde sig hvor som helst op til haleknuden.
Rumkompleksitet af enkelttilknyttede lister
En enkeltstรฅende linket liste allokerer dynamisk hukommelse. For at gemme n elementer, den allokerer n hukommelsesenheder. Sรฅ rumkompleksiteten er O(n).
Anvendelser af enkeltstรฅende linkede lister
Enkeltforbundne lister vises mange steder, hvor kun fremadrettet gennemgang og dynamisk hukommelse er nyttige:
- Stakke og kรธer: Underliggende lagring til LIFO-stakke og FIFO-kรธer bygget fra noder.
- Hash-tabelkรฆde: Kollisioner lรธses ved at kรฆde poster sammen i en enkeltstรฅende linket liste pr. bucket.
- Nรฆrliggende lister: Sparse grafer bruger en enkeltstรฅende linket liste over naboer for hvert hjรธrne.
- Symboltabeller: Compilere og fortolkere kรฆder identifikatorer sammen til en enkeltstรฅende linket liste pr. scope.
- Hukommelsesallokatorer: Gratislistetildelere track frie blokke som en enkeltstรฅende linket liste.









