Enkelvoudig gekoppelde lijst in datastructuren

Wat is een enkelvoudig gekoppelde lijst?

Singly Linked List is een lineaire en unidirectionele datastructuur, waarbij gegevens op de knooppunten worden opgeslagen en elk knooppunt via een link met het volgende knooppunt is verbonden. Elk knooppunt bevat een gegevensveld en een link naar het volgende knooppunt. Enkelvoudig gekoppelde lijsten kunnen in slechts één richting worden doorlopen, terwijl dubbel gekoppelde lijsten in beide richtingen kunnen worden doorlopen.

Hier is een knooppuntstructuur van een enkelvoudig gekoppelde lijst:

Structuur van een knooppunt in een gekoppelde lijst
Structuur van een knooppunt in een gekoppelde lijst

Waarom een ​​gekoppelde lijst gebruiken in plaats van een array?

Er zijn verschillende gevallen waarin het beter is om de gekoppelde lijst te gebruiken in plaats van de reeks. Hier zijn enkele scenario's:

  • Onbekend aantal elementen: Als u niet weet hoeveel elementen u tijdens de programmalooptijd moet opslaan, kunt u de gekoppelde lijst gebruiken. Geheugen wordt dynamisch toegewezen terwijl u elementen aan de gekoppelde lijsten toevoegt.
  • Willekeurige toegang: In een scenario waarin u de willekeurige toegang van de elementen niet hoeft te gebruiken, kunt u de gekoppelde lijst gebruiken.
  • Inbrengen in het midden: Invoeging in het midden van een array is een complex taak. Omdat je andere elementen naar rechts moet duwen. Met een gekoppelde lijst kunt u echter een element toevoegen aan elke gewenste positie.

Bewerkingen van een enkelvoudig gekoppelde lijst

Singly Linked List is goed voor het dynamisch toewijzen van geheugen. Het biedt alle basisbewerkingen van de gekoppelde lijst, dwz invoegen, verwijderen, enzarching, bijwerken, twee gekoppelde lijsten samenvoegen, doorlopen, enz.

Hier zullen we het vervolg besprekenwing werking van de gekoppelde lijst:

  • Inbrengen bij het hoofd
  • Inbrengen bij de staart
  • Invoegen na een knooppunt
  • Invoegen vóór een knooppunt
  • Verwijder het hoofdknooppunt
  • Verwijder het staartknooppunt
  • Zoek en verwijder een knooppunt
  • De gekoppelde lijst doorlopen

Hier is een voorbeeld van een gekoppelde lijst met vier knooppunten.

Voorbeeld van een enkelvoudig gekoppelde lijst
Voorbeeld van een enkelvoudig gekoppelde lijst

Invoeging aan het hoofd van een enkelvoudig gekoppelde lijst

Dit is een eenvoudige handeling. Over het algemeen staat dit bekend als het pushen van een enkelvoudig gekoppelde lijst. U moet een nieuw knooppunt maken en dit bovenaan de gekoppelde lijst plaatsen.

Om deze operatie uit te voeren, moeten we aan twee belangrijke voorwaarden voldoen. Zij zijn

  1. Als de lijst leeg is, zal het nieuw aangemaakte knooppunt het hoofdknooppunt zijn, en het volgende knooppunt van het hoofd zal "NULL" zijn.
  2. Als de lijst niet leeg is, is het nieuwe knooppunt het hoofdknooppunt en verwijst het volgende naar het vorige hoofdknooppunt.

Hier is de pseudocode voor het invoegen van een knooppunt aan het hoofd van een gekoppelde lijst:

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

Invoeging aan het einde van een enkelvoudig gekoppelde lijst

Het invoegen van een knooppunt aan het einde van een gekoppelde lijst heeft enkele overeenkomsten met het invoegen in de kop. Het enige wat u hoeft te doen is naar het eindknooppunt of het staartknooppunt te gaan. Wijs vervolgens het “volgende” knooppunt van het eindknooppunt naar het nieuw gemaakte knooppunt. Als het hoofdknooppunt nul is, is het nieuwe knooppunt het hoofdknooppunt.

Dit zijn de stappen:

Stap 1) Beweeg totdat het “volgende” knooppunt van het huidige knooppunt nul wordt.

Stap 2) Maak een nieuw knooppunt met de opgegeven waarde.

Stap 3) Wijs het nieuwe knooppunt toe als het volgende knooppunt van het staartknooppunt.

De pseudocode voor het invoegen van een knooppunt aan de staart van een afzonderlijke lijst:

function insertAtEnd( head, value ):
	newNode = Node(value)
	if head is NULL:
		head = newNode
		return head
	while head.next is not NULL:
		then head = head.next
	head.next = newNode
	newNode.next = NULL
Inbrengen bij de staart
Inbrengen bij de staart

Invoeging na een knooppunt in een Singly Linked List

Invoegen na een knooppunt bestaat uit twee delen: zoeken naar het knooppunt en koppelen na het gezochte knooppunt. We moeten alle knooppunten doorkruisen. Voor elk knooppunt moeten we matchen met het zoekelement. Als er een overeenkomst is, voegen we het nieuwe knooppunt toe na het gezochte knooppunt.

Dit zijn de stappen:

Stap 1) Doorkruis het volgende knooppunt totdat de waarde van het huidige knooppunt gelijk is aan het zoekitem.

Stap 2) De volgende aanwijzer van het nieuwe knooppunt zal de volgende aanwijzer van het huidige knooppunt zijn.

Stap 3) Het volgende knooppunt van het huidige knooppunt zal het nieuwe knooppunt zijn.

Hier is de pseudocode voor het invoegen van een knooppunt na een knooppunt:

function insertAfter( head, value, searchItem ):
	newNode = Node(value)
	while head.value equals searchItem:
		then head = head.next
	newNode.next = head.next.next
	head.next = newNode
Een knooppunt invoegen na een knooppunt in een enkelvoudig gekoppelde lijst
Een knooppunt invoegen na een knooppunt in de Singly Linked List

Invoeging vóór een knooppunt in een Singly Linked List

Deze functie lijkt veel op het invoegen na een knooppunt. We moeten een nieuw knooppunt maken en de gekoppelde lijst doorkruisen totdat het huidige knooppunt overeenkomt met het zoekknooppunt. Daarna zullen we het nieuwe knooppunt toevoegen als het vorige knooppunt van het huidige knooppunt.

Dit zijn de stappen:

Stap 1) Ga door totdat de waarde van het volgende knooppunt gelijk is aan het zoekitem.

Stap 2) Maak een nieuw knooppunt en wijs de “volgende” van het knooppunt toe aan het volgende knooppunt, naast het volgende knooppunt van het huidige knooppunt.

Stap 3) Wijs het nieuwe knooppunt toe als het volgende knooppunt van het huidige knooppunt.

Hier is de pseudocode:

function insertBefore( head, value, searchItem ):
	newNode = Node(value)
	while head.next.value equals searchItem:
		then head = head.next
	newNode.next = head.next.next
	head.next = newNode
Een knooppunt invoegen vóór een knooppunt in een enkelvoudig gekoppelde lijst
Een knooppunt invoegen vóór een knooppunt in de Singly Linked List

Verwijder de kop van de enkelvoudig gekoppelde lijst

In elke functie van de gekoppelde lijst wordt de head-pointer als parameter opgegeven. U moet het hoofdknooppunt verwijderen en het volgende knooppunt van het hoofdknooppunt als het nieuwe hoofd van de gekoppelde lijst maken. We moeten ook het geheugen van het verwijderde knooppunt vrijmaken. Anderwise, wordt het geheugen gemarkeerd als bezet wanneer een ander programma er toegang toe probeert te krijgen.

Hier volgen de stappen voor het verwijderen van de kop van de enkelvoudig gekoppelde lijst:

Stap 1) Wijs het volgende knooppunt van het hoofdknooppunt toe als het nieuwe hoofd.

Stap 2) Maak het toegewezen geheugen vrij door het vorige hoofdknooppunt.

Stap 3) Retourneer het nieuwe hoofdknooppunt.

De pseudocode voor het verwijderen van het hoofdknooppunt:

function deleteHead( head ):
	temp = head
	head = head.next
	free( temp )
	return head
Het hoofd van een gekoppelde lijst verwijderen
De kop van een gekoppelde lijst verwijderen

Verwijder de staart van de afzonderlijk gekoppelde lijst

Het verwijderen van het staartknooppunt is meer bekend met het verwijderen van het hoofdknooppunt. Het verschil is dat we naar het einde van de gekoppelde lijst moeten gaan en deze moeten verwijderen. In de enkelvoudig gekoppelde lijst is het knooppunt met het volgende knooppunt als “NULL” het staartknooppunt.

Hier volgen de stappen voor het verwijderen van het staartknooppunt van de gekoppelde lijst:

Stap 1) Beweeg vóór het staartknooppunt. Sla het huidige knooppunt op.

Stap 2) Maak het geheugen vrij van het volgende knooppunt van het huidige knooppunt.

Stap 3) Stel het volgende knooppunt van het huidige knooppunt in als NULL.

Hier is de pseudocode voor het verwijderen van het staartknooppunt:

function deleteTail( head ):
	while head.next.next is not NULL:
		head = head.next
	free( head.next )
	head.next = NULL
Het verwijderen van de staart van de Singly Linked List
Het verwijderen van de staart van de Singly Linked List

Zoek en verwijder een knooppunt uit een enkelvoudig gekoppelde lijst

Deze functie heeft twee taken: zoeken en verwijderen. We moeten zoeken tot het einde van de afzonderlijk gekoppelde lijsten. Als we een vergelijkbaar knooppunt vinden, zullen we dat verwijderen. Daarna moeten we de linker- en rechterknooppunten van het verwijderde knooppunt samenvoegen of koppelen. Hier zijn de stappen om dit te doen:

Stap 1) Doorloop tot het einde van de gekoppelde lijst. Controleer of het huidige knooppunt gelijk is aan het zoekknooppunt of niet.

Stap 2) Als een knooppunt overeenkomt, slaat u de knooppuntwijzer naar het huidige knooppunt op.

Stap 3) De “volgende” van het vorige knooppunt zal het volgende knooppunt van het huidige knooppunt zijn.

Stap 4) Verwijder het geheugen van het huidige knooppunt en maak het vrij.

Pseudocode voor het zoeken en verwijderen van een knooppunt uit een enkelvoudig gekoppelde lijst:

function searchAndDelete( head, searchItem ):
	while head.next.next is not NULL  and head.next.value is not equals searchItem :
		head = head.next
	head.next = head.next.next
	delete(head.next)
Zoek en verwijder een knooppunt uit de enkelvoudig gekoppelde lijst
Zoek en verwijder een knooppunt uit de Singly Linked List

Doorkruis een enkelvoudig gekoppelde lijst

Enkelvoudig gekoppelde lijst heeft alleen de functionaliteit voor het doorlopen van kop tot staart. Er zijn geen verwijzingspunten naar het vorige knooppunt; daarom kunnen we de Singly Linked List niet in omgekeerde volgorde doorkruisen. We zullen elk knooppunt doorlopen en de waarde van het huidige knooppunt afdrukken totdat we nul krijgen.

Dit zijn de stappen:

Stap 1) Doorkruis elk knooppunt totdat we nul krijgen als het volgende knooppunt.

Stap 2) Druk de waarde van het huidige knooppunt af.

Pseudocode voor het doorkruisen van een enkelvoudig gekoppelde lijst:

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

Voorbeeld van een enkelvoudig gekoppelde lijst in 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);
  }
  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 = new Node();
  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);
      }else{
        currentNode = currentNode->next;
      }
    }
  }
  cout<<"Deleted Node\t"<<searchItem<<endl;
  return;
}
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 = new 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);
}

Output:

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

Voorbeeld van een enkelvoudig gekoppelde lijst in het Python-programma

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.')
    return  
  def insertAtEnd(self,value):
    if self.head is None:
      self.insertAtHead(value)
    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):
    temp = Node()
    if self.head is searchItem:
      temp = self.head
      self.head = self.head.next
    else:
      currentNode = self.head
      while currentNode.next is not None:
        if currentNode.next.data is searchItem:
          temp = currentNode.next
          currentNode.next = currentNode.next.next
        else:
          currentNode = currentNode.next
    print(f'Deleted node\t{searchItem}')
    return
  def insertAfter(self,searchItem,value):
    newNode = Node(data=value)
    temp = self.head
    while temp.next is not None and temp.data is not searchItem:
      temp = temp.next
    newNode.next = temp.next
    temp.next = newNode
    print(f'Inserted {value} after node\t{searchItem}')
    return 
  def insertbefore(self,searchItem,value):
    newNode = Node(data=value)
    temp = self.head
    while temp.next is not None and temp.next.data is not searchItem:
      temp = temp.next
    newNode.next = temp.next
    temp.next = newNode
    print(f'Inserted {value} before node\t{searchItem}')
    return
  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()

Output:

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

complexvan de Singly Linked List

Er zijn twee soorten complexity: tijd complexity en ruimte complexiteit. De slechtste en gemiddelde casustijd complexDit is hetzelfde voor de Singly Linked List.

De tijd complexvoor het beste geval:

  • Inbrengen in het hoofd kan in O(1). Omdat we niet binnen de gekoppelde lijst hoeven te bladeren.
  • Zoek- en verwijderbewerkingen kunnen worden uitgevoerd in O(1) als het zoekelement aanwezig is in het hoofdknooppunt.

De tijd complexvoor het gemiddelde geval:

  • Voor het invoegen in een gekoppelde lijst is O(n) nodig als er “n”-elementen aanwezig zijn in de enkelvoudig gekoppelde lijst.
  • Zoeken en verwijderen kan ook O(n) vereisen, omdat het zoekelement aanwezig kan zijn in het staartknooppunt. In dat geval moet u de hele lijst doorlopen.

Ruimte Complexvan de Singly Linked List

Singly Linked List wijst dynamisch geheugen toe. Als we n elementen willen opslaan, wordt er n geheugeneenheid toegewezen. Dus de ruimte complexiteit is O(n).