Leerpad
Een gelinkte lijst is een datastructuur die een cruciale rol speelt bij gegevensorganisatie en -beheer. Het bevat een reeks knooppunten die op willekeurige locaties in het geheugen zijn opgeslagen, wat efficiënte geheugentoewijzing mogelijk maakt. Elk knooppunt in een gelinkte lijst bevat twee hoofdcomponenten: het gegevensgedeelte en een verwijzing naar het volgende knooppunt in de reeks.
Klinkt dit concept in eerste instantie ingewikkeld? Geen zorgen!
We brengen het terug tot de basis om uit te leggen wat gelinkte lijsten zijn, waarom we ze gebruiken en welke unieke voordelen ze bieden.
Waarom gelinkte lijsten?
Gelinkte lijsten zijn bedacht om verschillende nadelen te ondervangen die horen bij het opslaan van data in gewone lijsten en arrays, zoals hieronder beschreven:
Makkelijk invoegen en verwijderen
In lijsten vereist het invoegen of verwijderen van een element op een andere plek dan het einde dat alle daaropvolgende items opschuiven naar een andere positie. Dit proces heeft een tijdscomplexiteit van O(n) en kan de prestaties aanzienlijk verslechteren, zeker naarmate de lijst groter wordt. Als je nog niet bekend bent met hoe lijsten werken of hoe ze geïmplementeerd zijn, kun je onze tutorial over Python-lijsten lezen.
Gelinkte lijsten werken echter anders. Ze slaan elementen op op verschillende, niet-aangrenzende geheugenlocaties en verbinden ze via pointers naar volgende knooppunten. Dankzij deze structuur kunnen gelinkte lijsten op elke positie elementen toevoegen of verwijderen door simpelweg de links aan te passen om een nieuw element op te nemen of het verwijderde element over te slaan.
Zodra je een directe verwijzing hebt naar het knooppunt op de invoeg- of verwijderplaats, is de bewerking zelf O(1). Het vinden van die positie vereist nog steeds O(n) traverseren, dus het O(1)-voordeel geldt alleen wanneer je al een pointer naar het relevante knooppunt hebt (bijvoorbeeld wanneer je aan het begin van de lijst werkt).
Dynamische grootte
Python-lijsten zijn dynamische arrays, wat betekent dat je de grootte flexibel kunt aanpassen.
Dit proces omvat echter een reeks complexe operaties, waaronder het opnieuw toewijzen van de array aan een nieuw, groter geheugensegment. Zo’n herallocatie is inefficiënt omdat elementen naar een nieuw blok worden gekopieerd, waarbij mogelijk meer ruimte wordt gereserveerd dan direct nodig is.
Daarentegen kunnen gelinkte lijsten dynamisch groeien en krimpen zonder herallocatie of resizing. Dat maakt ze een betere optie voor taken die veel flexibiliteit vereisen.
Geheugenefficiëntie
Lijsten reserveren geheugen voor al hun elementen in één aaneengesloten blok. Als een lijst groter moet worden dan de initiële grootte, moet een nieuw, groter aaneengesloten blok worden gereserveerd en moeten alle bestaande elementen daarnaartoe worden gekopieerd. Dit is tijdrovend en inefficiënt, vooral bij grote lijsten. Als de initiële grootte juist te ruim is ingeschat, gaat ongebruikt geheugen verloren.
Gelinkte lijsten daarentegen reserveren geheugen per element afzonderlijk. Deze structuur leidt tot betere geheugengebruik, omdat geheugen voor nieuwe elementen kan worden toegewezen zodra ze worden toegevoegd.
Wanneer gebruik je gelinkte lijsten?
Hoewel gelinkte lijsten bepaalde voordelen bieden ten opzichte van gewone lijsten en arrays, zoals dynamische grootte en geheugenefficiëntie, hebben ze ook beperkingen. Omdat voor elk element een pointer moet worden opgeslagen om naar het volgende knooppunt te verwijzen, is het geheugengebruik per element hoger bij gelinkte lijsten. Bovendien biedt deze datastructuur geen directe toegang tot data. Toegang tot een element vereist sequentieel traverseren vanaf het begin van de lijst, wat resulteert in een zoektijdscomplexiteit van O(n).
De keuze tussen een gelinkte lijst of een array hangt af van de specifieke behoeften van je toepassing. Gelinkte lijsten zijn het nuttigst wanneer:
- Je vaak veel elementen moet invoegen en verwijderen
- De hoeveelheid data onvoorspelbaar is of vaak verandert
- Directe toegang tot elementen geen vereiste is
- De dataset grote elementen of structuren bevat
Typen gelinkte lijsten
Er zijn drie typen gelinkte lijsten, elk met unieke voordelen voor verschillende scenario’s. Deze typen zijn:
Enkelvoudig gelinkte lijsten

Enkelvoudig gelinkte lijst
Een enkelvoudig gelinkte lijst is het eenvoudigste type gelinkte lijst, waarbij elk knooppunt data bevat en een verwijzing naar het volgende knooppunt in de reeks. Je kunt ze slechts in één richting doorlopen: van de kop (het eerste knooppunt) naar de staart (het laatste knooppunt).
Elk knooppunt in een enkelvoudig gelinkte lijst bestaat doorgaans uit twee delen:
- Data: De daadwerkelijke informatie die in het knooppunt is opgeslagen.
- Volgende pointer: Een verwijzing naar het volgende knooppunt. De volgende pointer van het laatste knooppunt staat meestal op null.
Omdat deze datastructuren slechts in één richting te doorlopen zijn, vereist het benaderen van een specifiek element op waarde of index dat je bij de kop begint en sequentieel door de knooppunten gaat totdat je het gewenste knooppunt vindt. Deze operatie heeft een tijdscomplexiteit van O(n) en is daardoor minder efficiënt voor grote lijsten.
Het invoegen en verwijderen van een knooppunt aan het begin van een enkelvoudig gelinkte lijst is zeer efficiënt met een tijdscomplexiteit van O(1). Invoegen en verwijderen in het midden of aan het einde vereist echter traverseren tot dat punt, wat leidt tot O(n) tijdscomplexiteit.
Door het ontwerp zijn enkelvoudig gelinkte lijsten vooral handig wanneer bewerkingen aan het begin van de lijst plaatsvinden.
Dubbel gelinkte lijsten

Dubbel gelinkte lijst
Een nadeel van enkelvoudig gelinkte lijsten is dat je ze slechts in één richting kunt doorlopen en niet terug kunt stappen naar het vorige knooppunt indien nodig. Deze beperking maakt bewerkingen lastig die bidirectionele navigatie vereisen.
Dubbel gelinkte lijsten lossen dit probleem op door in elk knooppunt een extra pointer op te nemen, zodat de lijst in beide richtingen kan worden doorlopen. Elk knooppunt in een dubbel gelinkte lijst bevat drie elementen: de data, een pointer naar het volgende knooppunt en een pointer naar het vorige knooppunt.
Circulair gelinkte lijsten

Circulair gelinkte lijst
Circulair gelinkte lijsten zijn een speciaal type gelinkte lijst waarbij het laatste knooppunt terugwijst naar het eerste knooppunt, waardoor een cirkel ontstaat. Dat betekent dat, anders dan bij de enkelvoudig en dubbel gelinkte lijsten die we tot nu toe zagen, de circulair gelinkte lijst niet eindigt maar blijft rondgaan.
Door hun cyclische karakter zijn circulair gelinkte lijsten ideaal voor scenario’s die continu in een lus moeten worden doorlopen, zoals bordspellen die van de laatste speler weer naar de eerste gaan, of in computeralgoritmen zoals round-robin-scheduling.
Samenvatting tijdscomplexiteit
Het is handig om in één oogopslag te zien hoe gelinkte lijsten zich verhouden tot Python-lijsten:
| Bewerking | Enkelvoudig gelinkte lijst | Array/Python-lijst |
|---|---|---|
| Toegang op index | O(n) | O(1) |
| Zoeken op waarde | O(n) | O(n) |
| Invoegen aan begin | O(1) | O(n) |
| Invoegen aan einde | O(n) | O(1) geamortiseerd |
| Invoegen in het midden | O(n) | O(n) |
| Verwijderen aan begin | O(1) | O(n) |
| Verwijderen aan einde | O(n) | O(1) geamortiseerd |
De kern: gelinkte lijsten winnen bij invoegen en verwijderen aan de kop (O(1)), maar verliezen op vrijwel alles daarbuiten. Als je niet vaak elementen aan het begin van je datastructuur toevoegt of verwijdert, is een gewone Python-lijst waarschijnlijk de betere keuze.
Hoe maak je een gelinkte lijst in Python
Nu we begrijpen wat gelinkte lijsten zijn, waarom we ze gebruiken en welke varianten er zijn, gaan we ze implementeren in Python. De notebook voor deze tutorial is ook beschikbaar in deze DataLab-workbook; als je een kopie maakt, kun je de code bewerken en uitvoeren. Dit is een fijne optie als je problemen tegenkomt bij het lokaal draaien van de code!
Een knooppunt initialiseren
Zoals we eerder leerden, is een knooppunt een element in de gelinkte lijst dat data en een verwijzing naar het volgende knooppunt in de reeks opslaat. Zo definieer je een knooppunt in Python:
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __repr__(self):
return f"Node({self.data})"
De bovenstaande code initialiseert een knooppunt door twee hoofdtaken uit te voeren: Het attribuut “data” van het knooppunt krijgt een waarde die de daadwerkelijke informatie voorstelt die het knooppunt moet bevatten. Het attribuut “next” stelt het adres van het volgende knooppunt voor. Dit staat nu op None, wat betekent dat het nog niet naar een ander knooppunt in de lijst verwijst. Terwijl we nieuwe knooppunten aan de gelinkte lijst toevoegen, wordt dit attribuut bijgewerkt om naar het volgende knooppunt te wijzen.
Een klasse voor de gelinkte lijst maken
Vervolgens moeten we de klasse voor de gelinkte lijst maken. Deze kapselt alle bewerkingen in voor het beheren van de knooppunten, zoals invoegen en verwijderen. We beginnen met het initialiseren van de gelinkte lijst:
class LinkedList:
def __init__(self):
self.head = None # Initialize head as None
Door self.head op None te zetten, geven we aan dat de gelinkte lijst aanvankelijk leeg is en dat er geen knooppunten zijn waarnaar kan worden verwezen. We gaan de lijst nu vullen door nieuwe knooppunten in te voegen.
Een nieuw knooppunt aan het begin van een gelinkte lijst invoegen
Binnen de klasse LinkedList voegen we een methode toe om een nieuw knooppunt te maken en het aan het begin van de lijst te plaatsen:
def insertAtBeginning(self, new_data):
new_node = Node(new_data) # Create a new node
new_node.next = self.head # Next for new node becomes the current head
self.head = new_node # Head now points to the new node
Elke keer dat je bovenstaande methode aanroept, wordt een nieuw knooppunt gemaakt met de door jou opgegeven data. De volgende pointer van dit knooppunt wordt ingesteld op de huidige kop van de lijst, waardoor dit knooppunt vóór de bestaande knooppunten komt te staan. Tot slot wordt het nieuw aangemaakte knooppunt de kop van de lijst.
We gaan deze gelinkte lijst nu vullen met een reeks woorden om beter te begrijpen hoe de invoegbewerking werkt. Om dit te doen, maken we eerst een methode die is bedoeld om de inhoud van de lijst te doorlopen en te printen:
def printList(self):
temp = self.head # Start from the head of the list
while temp:
print(temp.data,end=' ') # Print the data in the current node
temp = temp.next # Move to the next node
print() # Ensures the output is followed by a new line
De bovenstaande methode print de inhoud van onze gelinkte lijst. Laten we nu de methoden die we hebben gedefinieerd gebruiken om onze lijst te vullen met de woorden: “the quick brown fox”.
if __name__ == '__main__':
# Create a new LinkedList instance
llist = LinkedList()
# Insert each letter at the beginning using the method we created
llist.insertAtBeginning('fox')
llist.insertAtBeginning('brown')
llist.insertAtBeginning('quick')
llist.insertAtBeginning('the')
# Now 'the' is the head of the list, followed by 'quick', then 'brown' and 'fox'
# Print the list
llist.printList()
De bovenstaande code zou de volgende output moeten opleveren:
"the quick brown fox"
Een nieuw knooppunt aan het einde van een gelinkte lijst invoegen
We maken nu een methode insertAtEnd binnen de klasse LinkedList om een nieuw knooppunt aan het einde van de lijst te maken. Als de lijst leeg is, wordt het nieuwe knooppunt de kop van de lijst. Anders wordt het toegevoegd aan het huidige laatste knooppunt in de lijst. Zo werkt dat in de praktijk:
def insertAtEnd(self, new_data):
new_node = Node(new_data)
if self.head is None:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
De bovenstaande methode begint met het maken van een nieuw knooppunt. Vervolgens wordt gecontroleerd of de lijst leeg is; zo ja, dan wordt het nieuwe knooppunt als kop van de lijst ingesteld. Anders doorloopt de methode de lijst om het laatste knooppunt te vinden en zet de pointer van dit knooppunt naar het nieuwe knooppunt.
We moeten deze methode nu opnemen in onze klasse LinkedList en hem gebruiken om een woord aan het einde van onze lijst toe te voegen. Pas hiervoor je main-functie als volgt aan:
if __name__ == '__main__':
llist = LinkedList()
# Insert words at the beginning
llist.insertAtBeginning('fox')
llist.insertAtBeginning('brown')
llist.insertAtBeginning('quick')
llist.insertAtBeginning('the')
# Insert a word at the end
llist.insertAtEnd('jumps')
# Print the list
llist.printList()
Merk op dat we simpelweg de methode insertAtEnd hebben aangeroepen om het woord “jumps” aan het einde van de lijst te printen. De bovenstaande code zou deze output moeten opleveren:
"the quick brown fox jumps"
Een knooppunt aan het begin van een gelinkte lijst verwijderen
Het verwijderen van het eerste knooppunt van een gelinkte lijst is eenvoudig, omdat je alleen de kop van de lijst naar het tweede knooppunt hoeft te laten wijzen. Zo maakt het eerste knooppunt geen deel meer uit van de lijst. Voeg hiervoor de volgende methode toe aan de klasse LinkedList:
def deleteFromBeginning(self):
if self.head is None:
return "The list is empty" # If the list is empty, return this string
self.head = self.head.next # Otherwise, remove the head by making the next node the new head
Een knooppunt aan het einde van een gelinkte lijst verwijderen
Om het laatste knooppunt van een gelinkte lijst te verwijderen, moeten we de lijst doorlopen om het een-na-laatste knooppunt te vinden en diens volgende pointer op None zetten. Zo maakt het laatste knooppunt geen deel meer uit van de lijst. Kopieer en plak de volgende methode in je klasse LinkedList om dit te doen:
def deleteFromEnd(self):
if self.head is None:
return "The list is empty"
if self.head.next is None:
self.head = None # If there's only one node, remove the head by making it None
return
temp = self.head
while temp.next.next: # Otherwise, go to the second-last node
temp = temp.next
temp.next = None # Remove the last node by setting the next pointer of the second-last node to None
De bovenstaande methode controleert eerst of de gelinkte lijst leeg is en geeft in dat geval een bericht terug. Als de lijst één knooppunt bevat, wordt dat knooppunt verwijderd. Bij lijsten met meerdere knooppunten wordt het een-na-laatste knooppunt gevonden en wordt de verwijzing naar het volgende knooppunt op None gezet.
Laten we nu de main-functie bijwerken om elementen aan het begin en einde van de gelinkte lijst te verwijderen:
if __name__ == '__main__':
llist = LinkedList()
# Insert words at the beginning
llist.insertAtBeginning('fox')
llist.insertAtBeginning('brown')
llist.insertAtBeginning('quick')
llist.insertAtBeginning('the')
# Insert a word at the end
llist.insertAtEnd('jumps')
# Print the list before deletion
print("List before deletion:")
llist.printList()
# Deleting nodes from the beginning and end
llist.deleteFromBeginning()
llist.deleteFromEnd()
# Print the list after deletion
print("List after deletion:")
llist.printList()
De bovenstaande code print de lijst vóór en na het verwijderen, zodat je ziet hoe invoegen en verwijderen in gelinkte lijsten werken. Je zou na het uitvoeren van deze code de volgende output moeten zien:
List before deletion:
the quick brown fox jumps
List after deletion:
quick brown fox
Zoeken naar een specifieke waarde in de gelinkte lijst
De laatste bewerking die we in dit hoofdstuk leren, is het ophalen van een specifieke waarde in de gelinkte lijst. Hiervoor moet de methode bij de kop van de lijst beginnen en door elk knooppunt itereren om te controleren of de data van het knooppunt overeenkomt met de gezochte waarde. Hier is een praktische implementatie van deze bewerking:
def search(self, value):
current = self.head # Start with the head of the list
position = 0 # Counter to keep track of the position
while current: # Traverse the list
if current.data == value: # Compare the list's data to the search value
return f"Value '{value}' found at position {position}" # Print the value if a match is found
current = current.next
position += 1
return f"Value '{value}' not found in the list"
Om specifieke waarden te vinden in de gelinkte lijst die we hebben gemaakt, werk je je main-functie bij om de zoekmethode die we zojuist hebben gemaakt op te nemen:
if __name__ == '__main__':
llist = LinkedList()
# Insert words at the beginning
llist.insertAtBeginning('fox')
llist.insertAtBeginning('brown')
llist.insertAtBeginning('quick')
llist.insertAtBeginning('the')
# Insert a word at the end
llist.insertAtEnd('jumps')
# Print the list before deletion
print("List before deletion:")
llist.printList()
# Deleting nodes from beginning and end
llist.deleteFromBeginning()
llist.deleteFromEnd()
# Print the list after deletion
print("List after deletion:")
llist.printList()
# Search for 'quick' and 'lazy' in the list
print(llist.search('quick')) # Expected to find
print(llist.search('lazy')) # Expected not to find
De bovenstaande code levert deze output op:
List before deletion:
the quick brown fox jumps
List after deletion:
quick brown fox
Value 'quick' found at position 0
Value 'lazy' not found in the list
Het woord “quick” is succesvol gevonden in de gelinkte lijst, omdat het op de eerste positie staat. Het woord “lazy” maakt echter geen deel uit van de lijst en is daarom niet gevonden.
Tot slot
Als je tot hier bent gekomen: gefeliciteerd! Je hebt nu een solide begrip van de basisprincipes van gelinkte lijsten, inclusief hun structuur, typen, hoe je elementen toevoegt en verwijdert, en hoe je ze doorloopt.
Maar hier stopt het niet. Gelinkte lijsten zijn slechts het begin van de wereld van datastructuren en algoritmen. Hier zijn enkele mogelijke vervolgstappen om je kennis verder te verdiepen:
Maak je eigen project
Duik in de praktische toepassingen van gelinkte lijsten door ze te integreren in een programmeer- of datascienceproject. Gelinkte lijsten worden gebruikt om bestandssystemen te ontwikkelen, hashtabellen te bouwen en zelfs GPS-navigatiesystemen en bordspellen te maken. Wil je aan de slag met je eigen projecten? Bekijk dan onze gratis begeleide datascienceprojecten die je leren echte problemen op te lossen in Python, R en SQL.
Leer over datastructuren en algoritmen
Het leren van andere datastructuren, zoals bomen, stacks en queues, is een logische volgende stap na gelinkte lijsten. Deze structuren bouwen voort op de principes van gelinkte lijsten en helpen je een breder scala aan computationele problemen efficiënt op te lossen. Bomen en binaire zoekbomen, bijvoorbeeld, breiden het concept van gelinkte lijsten uit naar een hiërarchische vorm, waardoor elk knooppunt met meerdere elementen in de datastructuur kan verbinden.
Klinken deze concepten je nog onbekend? Geen zorgen! Datacamp heeft een volledige cursus over datastructuren en algoritmen in Python die je stap voor stap door deze onderwerpen leidt. Je leert eerst over datastructuren zoals stacks, bomen, hashtabellen, queues en grafen. Naarmate je vordert, krijg je inzicht in zoek- en sorteeralgoritmen, waardoor je een efficiëntere programmeur en probleemoplosser wordt.
Verdiep je in geavanceerde concepten van gelinkte lijsten
In deze tutorial hebben we enkelvoudig gelinkte lijsten geïmplementeerd en bewerkingen behandeld zoals invoegen, verwijderen en traverseren.
Je kunt deze kennis uitbreiden door te leren hoe je dubbel en circulair gelinkte lijsten implementeert. Skip lists zijn een andere uitbreiding van gelinkte lijsten die snellere zoekbewerkingen mogelijk maken door vlottere toegang tot elementen te bieden.
Kennis van deze geavanceerde datastructuren tilt je technische vaardigheden naar een hoger niveau en verbetert je programmeercapaciteiten aanzienlijk, zodat je beter bent voorbereid op complexere uitdagingen in bijvoorbeeld data science, softwareontwikkeling en machine learning engineering.
Wil je liever eerst een meer beginnersvriendelijke introductie tot programmeren voordat je deze geavanceerde topics aanpakt? Bekijk dan onze Python Programmer-carrièreroute. Die biedt een reeks cursussen die je de basis van de taal leren.
Natassha is een data consultant die werkt op het snijvlak van data science en marketing. Ze gelooft dat data, mits verstandig gebruikt, enorme groei kan aanjagen voor individuen en organisaties. Als autodidactisch dataprofessional houdt Natassha ervan om artikelen te schrijven die andere aspirant-data scientists helpen de industrie binnen te komen. Haar artikelen op haar persoonlijke blog en in externe publicaties trekken gemiddeld 200.000 maandelijkse weergaven.

