class Node: def __init__(self, data): self.data = data self.prev = None self.next = None class DoublyLinkedList: def __init__(self): self.head = None def append(self, data): new_node = Node(data) if not self.head: self.head = new_node return last = self.head while last.next: last = last.next last.next = new_node new_node.prev = last def print_list(self): current = self.head result = [] while current: result.append(str(current.data)) current = current.next print(" <-> ".join(result) + " <-> null") dl = DoublyLinkedList() dl.append(10) dl.append(20) dl.append(30) dl.print_list()
The append method adds nodes at the end. The print_list method prints nodes from head to tail separated by <-> . So the output shows 10 linked to 20 linked to 30, ending with null.
class Node: def __init__(self, data): self.data = data self.prev = None self.next = None class DoublyLinkedList: def __init__(self): self.head = None def prepend(self, data): new_node = Node(data) new_node.next = self.head if self.head: self.head.prev = new_node self.head = new_node def print_list(self): current = self.head result = [] while current: result.append(str(current.data)) current = current.next print(" <-> ".join(result) + " <-> null") dl = DoublyLinkedList() dl.prepend(5) dl.prepend(15) dl.prepend(25) dl.print_list()
Each prepend adds a new node before the current head. The last prepended node (25) becomes the new head. So the list is 25 linked to 15 linked to 5, ending with null.
class Node: def __init__(self, data): self.data = data self.prev = None self.next = None class DoublyLinkedList: def __init__(self): self.head = None def append(self, data): new_node = Node(data) if not self.head: self.head = new_node return last = self.head while last.next: last = last.next last.next = new_node new_node.prev = last def remove(self, data): current = self.head while current: if current.data == data: if current.prev: current.prev.next = current.next else: self.head = current.next if current.next: current.next.prev = current.prev return current = current.next def print_list(self): current = self.head result = [] while current: result.append(str(current.data)) current = current.next print(" <-> ".join(result) + " <-> null") dl = DoublyLinkedList() dl.append(10) dl.append(20) dl.append(30) dl.remove(20) dl.print_list()
The bug is fixed by updating the else block inside remove: when removing the head node, it sets self.head = current.next correctly. This ensures the head is updated properly and no AttributeError occurs.
class Node: def __init__(self, data): self.data = data self.prev = None self.next = None class DoublyLinkedList: def __init__(self): self.head = None def append(self, data): new_node = Node(data) if not self.head: self.head = new_node return last = self.head while last.next: last = last.next last.next = new_node new_node.prev = last def reverse(self): current = self.head temp = None while current: temp = current.prev current.prev = current.next current.next = temp current = current.prev if temp: self.head = temp.prev def print_list(self): current = self.head result = [] while current: result.append(str(current.data)) current = current.next print(" <-> ".join(result) + " <-> null") dl = DoublyLinkedList() dl.append(1) dl.append(2) dl.append(3) dl.append(4) dl.reverse() dl.print_list()
The reverse method swaps the next and prev pointers for each node. After the loop, head is updated to the last node processed. So the list order is reversed: 4 linked to 3 linked to 2 linked to 1, ending with null.
Each node in a doubly linked list stores two pointers: one to the previous node and one to the next node. A singly linked list node stores only one pointer to the next node. Therefore, doubly linked lists use more memory per node.