Dobbelt linket liste: C++, Python (Kodeeksempel)

Hvad er en dobbeltlinket liste?

I en dobbelt linket liste har hver node links til både den forrige og næste node. Hver node består af tre elementer: en indeholder dataene, og to andre er den næste og den forrige nodes pointere. Disse to pointere hjælper os med at gå frem eller tilbage fra en bestemt knude.

Her er den grundlæggende struktur for den dobbeltforbundne liste.

Strukturen af ​​en dobbeltforbundet liste
Opbygning af en dobbeltforbundet liste

Hver linket liste har en hoved- og halenode. Hovedknuden har ingen "prev" (forrige pointer)-knude, og haleknuden har ingen "næste" knude.

Her er nogle vigtige udtryk for en dobbeltlinket liste:

  • forrige: Hver node er knyttet til dens tidligere node. Det bruges som en pointer eller et link.
  • Næste: Hver node er knyttet til dens næste node. Det bruges som en pointer eller et link.
  • dato: Dette bruges til at gemme data i en node. "Data" kan indeholde andre Datastrukturer inde i den. For eksempel kan streng, ordbog, sæt, hashmap osv. gemmes i "Data".

Her er den grundlæggende struktur for en enkelt node i den dobbeltforbundne liste:

Struktur af en node i en dobbeltforbundet liste

Struktur af en node i en dobbelt linket liste

Operationer af dobbeltforbundet liste

Operationerne af en dobbelt-linket liste omfatter tilføjelse og sletning af noder, indsættelse og fjernelse af noder og gennemgang af den linkede liste fra top til bund.

Her er listen over operationer, vi kan implementere ved hjælp af den dobbelt-linkede liste:

  • Indsættelse foran
  • Indsættelse i halen eller sidste knude
  • Indsættelse efter en node
  • Indsættelse før en node
  • Sletning forfra
  • Sletning fra halen
  • Søg og slet en node
  • Kør hoved til hale
  • Kør hale til hoved

Vi vil se implementeringen og pseudokoden for alle operationerne ovenfor.

Indsættelse foran Doubly Linked List

Indsættelse foran betyder, at vi skal oprette en node i den linkede liste og placere den i begyndelsen af ​​den linkede liste.

For eksempel er der en given node "15". Vi skal tilføje dette til hovedknuden.

Her er to vigtige forhold, mens du udfører denne operation:

  1. Den nye knude vil være hovedknuden, hvis der ikke er nogen knude på listen med dobbelt lænke.
  2. Hvis der allerede er en hovedknude, vil det tidligere hoved blive erstattet af den nye knude.

Her er pseudokoden for denne operation:

function insertAtFront (ListHead, value):
  newNode = Node()
  newNode.value = value
  ListHead.prev = NewNode
  NewNode.next = ListHead
  newNode.prev = NULL
  return ListHead
Indsættelse i Front Node
Indsættelse i frontnode

Indsættelse i slutningen af ​​dobbeltforbundet liste

"Indsættelse i slutningen" betyder, at vi opretter en node i den linkede liste og placerer den i slutningen.

For at udføre dette kan vi bruge to metoder:

  • Metode 1: Begynd at krydse fra hovedet af den dobbeltforbundne liste, indtil "næste" bliver nul. Forbind derefter den nye node med den "næste".
  • Metode 2: Tag den sidste knude på listen med dobbelt kæder. Derefter vil den "næste" node i den sidste node linke til den nye node. Nu vil den nye knude være haleknuden.

Her er pseudokoden til indsættelse ved haleknuden:

function insertAtTail(ListHead, value):
  newNode = Node()
  newNode.value = value
  newNode.next = NULL
  while ListHead.next is not NULL:
	then ListHead = ListHead.next
  newNode.prev = ListHead
  ListHead.next = newNode
  return ListHead
Indsættelse i slutningen af ​​den sammenkædede liste

Indsættelse i slutningen af ​​den linkede liste

Indsættelse efter en node

Lad os sige, at vi har en eksisterende dobbeltlinket liste som følgende:

Indsættelse efter en node

Vi ønsker at indsætte en given node, der vil blive linket efter noden, som har værdien "12".

Her er trinene til dette:

Trin 1) Gå fra hovedet til den sidste knude. Tjek hvilken node der har værdien "12".

Trin 2) Opret en ny node og tildel den som den næste pointer for node "12". Den "næste" node i den nye node vil være 15.

Her er pseudo-koden til at indsætte en node efter en node i dobbelt linket liste:

function insertAfter(ListHead, searchItem, value):
  List = ListHead
  NewNode = Node()
  NewNode.value = value
  while List.value is not equal searchItem
	then List = ListHead.next
  List = List.next
  NewNode.next = List.next
  NewNode.prev = List
  List.next = NewNode
Indsættelse efter en node

Indsættelse efter en node

Indsættelse før en node

Denne operation ligner mere "indsættelsen efter en node". Vi skal søge efter en specifik nodes værdi for at udføre dette. Så vil vi oprette en ny node og indsætte den før den søgte node.

Lad os sige, at vi ønsker at indsætte en given node "15" før noden "12". Så her er trinene til at gøre det:

Trin 1) Gå gennem den sammenkædede liste fra hovedknuden til haleknuden.

Trin 2) Kontroller, om den næste pointer for den aktuelle node har værdien "12" eller ej.

Trin 3) Indsæt den nye node som den "næste" node for den aktuelle node.

Her er pseudokoden til at indsætte en node før en node i dobbelt linket liste:

function insertBefore(ListHead, searchItem, value):
  List = ListHead
  NewNode = Node()
  NewNode.value = value
  while List.next.value is not equal searchItem
	then List = ListHead.next
  NewNode.next = List.next
  NewNode.prev = List
  List.next = NewNode
Indsættelse af en node før en node

Indsættelse af en node før en node

Slet hovedet på den dobbeltforbundne liste

Hovedknuden i den dobbeltforbundne liste, der ikke har nogen tidligere knude. Så den næste pointer vil være den nye hovedknude, hvis vi ønsker at slette hovedknuden. Vi skal også frigøre hukommelsen optaget af en node, mens vi sletter en node.

Her er trinene til at slette hovednoden:

Trin 1) Tildel en variabel til den aktuelle hovedknude.

Trin 2) Besøg den "næste" knude på den aktuelle hovedknude, og gør "forrige" (forrige markør) node som ''NULL'. Det betyder, at vi afbryder den anden node fra den første node.

Trin 3) Frigør den optagede hukommelse af den forrige hovedknude.

Her er pseudokoden til at slette hovedet fra en dobbelt linket liste:

function deleteHead(ListHead):
  PrevHead = ListHead
  ListHead = ListHead.next
  ListHead.prev = NULL
  PrevHead.next = NULL
  free memory(PrevHead)
  return ListHead
Sletning af hovedknuden

Sletter hovedknudepunktet

Det er nødvendigt at slette den tildelte hukommelse efter at have udført enhver form for sletning. Ellers vil hukommelsen for den slettede blok forblive optaget under hele programmets køretid. Ingen anden applikation kan bruge det hukommelsessegment.

Slet halen af ​​den dobbeltforbundne liste.

Denne operation er lidt det samme som sletningen af ​​hovedet. Her skal vi i stedet for hovedet slette halen. For at identificere en node som en haleknude, bør vi kontrollere, om den næste pointer eller næste node er nul eller ej. Efter at have slettet halen, skal vi frigøre hukommelsen.

Denne operation er også kendt som "sletningen bagfra".

Her er trinene til at gøre dette:

Trin 1) Kør indtil haleknuden på den dobbeltforbundne liste.

Trin 2) Tildel en variabel eller pointer til haleknuden.

Trin 3) Gør den "næste" node til NULL og frigør hukommelsen til haleknuden.

Her er pseudokoden til sletning af haleknuden:

function DeleteTail( ListHead ):
  head = ListHead
  while ListHead.next is not NULL:
	ListHead = ListHead.next
  Tail = ListHead.next
  ListHead.next = NULL
  free memory( Tail )
  return head

Slet den dobbeltforbundne hale

Søg og slet en node fra listen med dobbelt lænke

Denne operation giver os mulighed for at søge efter en specifik nodedata og slette den node. Vi skal udføre en lineær søgning, da den linkede liste er en lineær datastruktur. Efter sletning skal vi også frigøre hukommelsen.

Her er trinene til at søge og slette en node på listen med dobbelt link:

Trin 1) Gå gennem den linkede liste fra hovedet, indtil nodens værdi er lig med søgeelementet.

Trin 2) Tildel en variabel "deleteNode" til den matchede node.

Trin 3) Tildel den forrige node af "deleteNode" til den næste node.

Trin 4) Frigør hukommelsen til "deleteNode"

Her er pseudokoden til at søge og slette en node fra en linket liste:

function SearchAndDelete(ListHead, searchItem):
  head = ListHead
  while ListHead.next.value not equals searchItem:
	head = head.next
  deleteNode = head.next
  head.next = head.next.next
  head.next.prev = head
  deleteNode.next, deleteNode.next = NULL
  free memory(deleteNode)
  return ListHead
Søg og slet Operation

Søg og slet handling

Gennemse en dobbeltforbundet liste fra fremadrettet

At krydse fra hovedknuden og iterere over den næste knude, indtil vi finder "NULL". Mens vi krydser hver node, kan vi udskrive værdien af ​​noden. Her er trinene til at krydse forfra (retning fremad):

Trin 1) Tildel en pointer eller variabel til den aktuelle hovedknude.

Trin 2) Gentag til den næste knude i hovedet, indtil du får "NULL"

Trin 3) Udskriv nodens data i hver iteration.

Trin 4) Returner hovedknuden.

Her er pseudokoden til at krydse en dobbeltforbundet liste fra forsiden:

function traverseFromFront(ListHead):
  head = ListHead
  while head not equals to NULL:
	print head.data
	head = head.next
  return ListHead

Her er returneringen ikke obligatorisk. Men det er en god praksis at returnere hovedknuden efter operationerne.

Gå gennem en dobbeltforbundet liste bagfra

Denne operation er det omvendte af traversen forfra. Fremgangsmåden er den samme med en lille forskel. Vi skal krydse til endeknudepunktet og derefter krydse fra endeknudepunktet til det forreste knudepunkt.

Her er trinene til at krydse en dobbelt linket liste fra bagsiden:

Trin 1) Traverse indtil vi når haleknuden.

Trin 2) Fra haleknuden vil vi krydse, indtil vi får den forrige knude som "NULL". "Prev" (forrige markør) vil være nul for hovedknuden

Trin 3) Ved hver iteration udskriver vi nodedataene.

Her er pseudokoden til at krydse bagfra:

function traverseFromBack(ListHead):
  head = ListHead
  while head not equals NULL:
	head = head.next
  tail = head
  while tail not equal to NULL:
	print tail.value
	tail = tail.prev
  return ListHead

Forskellen mellem enkelt- og dobbeltforbundet liste

Den største forskel mellem Singly og Double Linked-listen er antallet af links.

Forskellen mellem enkelt- og dobbeltforbundet liste

Her er forskellen mellem noderne på en enkelt lænket liste og dobbelt lænket listes nodestruktur:

Felt Enkeltforbundet liste Dobbeltforbundet liste
Struktur Enkeltforbundet liste har et datafelt og et link til den næste node. Dobbelt linket liste har et datafelt og to links. En for den forrige node og en anden for den næste node.
Traversal Den kan kun krydse fra hoved til hale. Den kan køre både frem og tilbage.
Hukommelse Optager mindre hukommelse. Det optager mere hukommelse end en enkelt-linket liste.
Tilgængelighed Singly Linked Lists er mindre effektive, da de kun bruger ét link til den næste node. Der er intet link til den forrige node. Dobbeltlinkede lister er mere effektive end enkeltlinkede lister.

Dobbeltforbundet liste i C++

#include<iostream>
using namespace std;
  struct node{
  int data;
  struct node *next;
  struct node *prev;
  };
void insertFront(node* &listHead, int value){
  node* newNode = new node();
  newNode->data = value;
  newNode->prev = NULL;
  newNode->next = NULL;
  if(listHead!=NULL)
  {
	listHead->prev = newNode;
  	newNode->next = listHead;
  }
  
  listHead = newNode;
  cout<<"Added "<<value<<" at the front"<<endl;
  }
void insertEnd(node * &listHead, int value){
  if(listHead==NULL)
  {
  	insertFront(listHead,value);
  }
  node* newNode = new node();
  newNode->data = value;
  newNode->prev = NULL;
  newNode->next = NULL;
  node *head = listHead;
  while(head->next!=NULL){
  head = head->next;
  }
  head->next = newNode;
  newNode->prev = head;
  cout<<"Added "<<value<<" at the end"<<endl;
  }
void insertAfter(node * &listHead, int searchValue,int value){
  node* newNode = new node();
  newNode->data = value;
  newNode->prev = NULL;	
  newNode->next = NULL;
  node *head = listHead;	
  while(head->next!=NULL &&  head->data!=searchValue){	
  head = head->next;	
  }	
  newNode->next = head->next;	
  head->next = newNode;	
  newNode->prev = head;	
  if(newNode->next !=NULL){	
  newNode->next->prev = newNode;	
  }	
  cout<<"Inserted "<<value<<" after node "<<searchValue<<endl;
  }
void insertBefore(node * &listHead, int searchValue,int value){
  node* newNode = new node();
  newNode->data = value;
  newNode->prev = NULL;	
  newNode->next = NULL;	
  node *head = listHead;	
  while(head->next!=NULL &&  head->next->data!=searchValue){	
  head = head->next;	
  }	
  newNode->next = head->next;	
  head->next = newNode;	
  newNode->prev = head;	
  if(newNode->next !=NULL){	
  newNode->next->prev = newNode;	
  }	
  cout<<"Inserted "<<value<<" before node "<<searchValue<<endl;	
  }
void traverseFromFront(node *listHead){
  node* head = new node();
  head = listHead;
  cout<<"Traversal from head:\t";	
  while(head!=NULL){	
  cout<<head->data<<"\t ";	
  head = head->next;	
  }	
  cout<<endl;
  }	
void traverseFromEnd(node *listHead){
  node* head = new node();
  head = listHead;	
  cout<<"Traversal from head:\t";	
  while(head->next!=NULL){	
  head = head->next;	
  }	
  node *tail = head;	
  while(tail!=NULL){	
  cout<<tail->data<<"\t";	
  tail = tail->prev;	
  }	
  cout<<endl;	
  }
void searchAndDelete(node **listHead, int searchItem){
  node* head = new node();
  head = (*listHead);
  while(head->next!=NULL &&  head->data!=searchItem){
  head = head->next;
  }
  if(*listHead == NULL || head == NULL) return;
  if((*listHead)->data == head->data){
  	*listHead = head->next;
  }
  if(head->next != NULL){
  	head->next->prev = head->prev;
  }
  
  if(head->prev != NULL){
  	head->prev->next = head->next;
  }
  free(head);
  cout<<"Deleted Node\t"<<searchItem<<endl;
  return;
  }
int main(){
  node *head = NULL;
  insertFront(head,5);
  insertFront(head,6);
  insertFront(head,7);
  insertEnd(head,9);
  insertEnd(head,10);
  insertAfter(head,5,11);
  insertBefore(head,5,20);
  traverseFromFront(head);
  traverseFromEnd(head);
  searchAndDelete(&head,7);
  traverseFromFront(head);
  traverseFromEnd(head);
}

Produktion

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

Dobbeltforbundet liste i Python

class node:
  def __init__(self,data = None, prev=None, next = None):
    self.data = data
    self.next = next
    self.prev = prev
class DoublyLinkedList:
  def __init__(self):
    self.head = None

  def insertFront(self, val):
    newNode = node(data=val)
    newNode.next = self.head
    if self.head is not None:
      self.head.prev = newNode
    self.head = newNode
    print("Added {} at the front".format(val))

  def insertEnd(self,val):
    newNode = node(data=val)
    if self.head is None:
      self.insertFront(val)
    
    temp = self.head
    while temp.next is not None:
      temp = temp.next
    temp.next = newNode
    newNode.prev = temp
    print("Added {} at the end".format(val))

  def traverseFromFront(self):
    temp = self.head
    print("Traversing from head:\t",end="")
    while temp:
      print("{}\t".format(temp.data),end="")
      temp = temp.next
    print()

  def traverseFromEnd(self):
    temp = self.head
    print("Traversing from Tail:\t",end="")
    while temp.next is not None:
      temp = temp.next
    tail = temp
    while tail is not None:
      print("{}\t".format(tail.data),end="")
      tail = tail.prev
    print()
  
  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
    newNode.prev = temp

    if newNode.next is not None:
      newNode.next.prev = newNode
    print("Inserted {} after node {}".format(value,searchItem))

  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
    newNode.prev = temp

    if newNode.next is not None:
      newNode.next.prev = newNode
    print("Inserted {} before node {}".format(value,searchItem))

  def searchAndDelete(self,searchItem):
    temp = self.head

    while temp.next is not None and temp.data is not searchItem:
      temp = temp.next
    
    if self.head is None or temp is None:
      return

    if self.head.data is temp.data:
      self.head = temp.next

    if temp.next is not None:
      temp.next.prev = temp.prev
    
    if temp.prev is not None:
      temp.prev.next = temp.next
    
    print("Deleted Node\t{}".format(searchItem))
    return

doublyLinkedList = DoublyLinkedList()
doublyLinkedList.insertFront(5)
doublyLinkedList.insertFront(6)
doublyLinkedList.insertFront(7)
doublyLinkedList.insertEnd(9)
doublyLinkedList.insertEnd(10)
doublyLinkedList.insertAfter(5, 11)
doublyLinkedList.insertBefore(5, 20)
doublyLinkedList.traverseFromFront()
doublyLinkedList.traverseFromEnd()
doublyLinkedList.searchAndDelete(7)
doublyLinkedList.traverseFromFront()
doublyLinkedList.traverseFromEnd()

Produktion

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

Kompleksiteten af ​​dobbeltforbundet liste

Generelt er tidskompleksitet opdelt i tre typer.

De er:

  1. Bedste Sag
  2. Gennemsnitlig sag
  3. Værste tilfælde

Tidskompleksitet i bedste tilfælde for dobbeltforbundet liste:

  1. Indsættelse i hoved eller hale vil koste O(1). Fordi vi ikke behøver at krydse inde i den linkede liste. Hovedet og haleviseren kan give os adgang til hoved- og haleknuden med O(1) tidskompleksitet.
  2. Sletning ved hovedet eller halen vil koste O(1).
  3. Søgning i en knude vil have tidskompleksiteten som O(1). Fordi målknuden kan være hovedknuden.

Tidskompleksitet i det gennemsnitlige tilfælde for dobbeltforbundet liste:

  1. Indsættelse ved hovedet eller halen vil have tidskompleksiteten af ​​omkostningerne O(1).
  2. Sletning ved hovedet eller halen vil have tidskompleksiteten af ​​omkostningerne O(1).
  3. Søgning i en knude vil have tidskompleksiteten af ​​O(n). Fordi søgeelementet kan ligge hvor som helst mellem den linkede liste. Her er "n" den samlede node, der er til stede i den sammenkædede liste.

Den værst tænkelige tidskompleksitet af den dobbeltforbundne liste vil være den samme som den gennemsnitlige sag.

Hukommelseskompleksitet af dobbeltforbundet liste

Hukommelseskompleksitet er O(n), hvor "n" er det samlede antal noder. Mens vi implementerer den sammenkædede liste, skal vi frigøre den hukommelse, vi brugte. Ellers vil det for en større sammenkædet liste forårsage hukommelseslækager.

Resumé

  • En sammenkædet liste er en slags lineær datastruktur, en samling af data repræsenteret på en lineær måde.
  • En dobbelt linket liste er en type linket liste, hvor en node har links til både den forrige og næste node.
  • Dobbelt linket liste indeholder alle operationer som at tilføje en node, slette en node, indsætte en node efter eller før en anden node og at krydse den linkede liste fra hoved til hale.
  • Dobbelt linket liste har et datafelt og to links. En for den forrige node og en anden for den næste node.
  • Kompleksiteten af ​​dobbeltforbundet liste Bedste sag, gennemsnitlig sag
  • Og Worst Case.