Spaces:
Sleeping
Sleeping
| LINKED LISTS | |
| ============ | |
| A linked list stores elements in nodes. Each node holds data and a pointer to the next node. | |
| Unlike arrays, nodes are not stored in contiguous memory. | |
| Advantages: O(1) insert/delete at head, dynamic size. | |
| Disadvantages: O(n) access by index, extra memory for pointers. | |
| --- Node Definition --- | |
| class Node: | |
| def __init__(self, data): | |
| self.data = data | |
| self.next = None | |
| --- Singly Linked List --- | |
| Each node points to the next. Last node points to None. | |
| class LinkedList: | |
| def __init__(self): | |
| self.head = None | |
| def insert_at_head(self, data): | |
| node = Node(data) | |
| node.next = self.head | |
| self.head = node | |
| def insert_at_tail(self, data): | |
| node = Node(data) | |
| if not self.head: | |
| self.head = node | |
| return | |
| curr = self.head | |
| while curr.next: | |
| curr = curr.next | |
| curr.next = node | |
| def delete(self, data): | |
| if not self.head: | |
| return | |
| if self.head.data == data: | |
| self.head = self.head.next | |
| return | |
| curr = self.head | |
| while curr.next and curr.next.data != data: | |
| curr = curr.next | |
| if curr.next: | |
| curr.next = curr.next.next | |
| def search(self, data): | |
| curr = self.head | |
| while curr: | |
| if curr.data == data: | |
| return True | |
| curr = curr.next | |
| return False | |
| def print_list(self): | |
| curr = self.head | |
| result = [] | |
| while curr: | |
| result.append(curr.data) | |
| curr = curr.next | |
| print(result) | |
| ll = LinkedList() | |
| ll.insert_at_tail(1) | |
| ll.insert_at_tail(2) | |
| ll.insert_at_tail(3) | |
| ll.insert_at_head(0) | |
| ll.print_list() # [0, 1, 2, 3] | |
| ll.delete(2) | |
| ll.print_list() # [0, 1, 3] | |
| --- Reverse a Linked List --- | |
| Time: O(n). Space: O(1). | |
| def reverse(head): | |
| prev = None | |
| curr = head | |
| while curr: | |
| next_node = curr.next | |
| curr.next = prev | |
| prev = curr | |
| curr = next_node | |
| return prev # new head | |
| --- Detect Cycle (Floyd's Algorithm) --- | |
| Use slow and fast pointers. If they meet, there is a cycle. | |
| Time: O(n). Space: O(1). | |
| def has_cycle(head): | |
| slow = fast = head | |
| while fast and fast.next: | |
| slow = slow.next | |
| fast = fast.next.next | |
| if slow == fast: | |
| return True | |
| return False | |
| --- Find Middle of Linked List --- | |
| Time: O(n). Space: O(1). | |
| def find_middle(head): | |
| slow = fast = head | |
| while fast and fast.next: | |
| slow = slow.next | |
| fast = fast.next.next | |
| return slow # slow is at middle | |
| --- Merge Two Sorted Linked Lists --- | |
| Time: O(n + m). Space: O(1). | |
| def merge_sorted(l1, l2): | |
| dummy = Node(0) | |
| curr = dummy | |
| while l1 and l2: | |
| if l1.data <= l2.data: | |
| curr.next = l1 | |
| l1 = l1.next | |
| else: | |
| curr.next = l2 | |
| l2 = l2.next | |
| curr = curr.next | |
| curr.next = l1 or l2 | |
| return dummy.next | |
| --- Remove Nth Node From End --- | |
| Use two pointers separated by n steps. | |
| Time: O(n). Space: O(1). | |
| def remove_nth_from_end(head, n): | |
| dummy = Node(0) | |
| dummy.next = head | |
| fast = slow = dummy | |
| for _ in range(n + 1): | |
| fast = fast.next | |
| while fast: | |
| slow = slow.next | |
| fast = fast.next | |
| slow.next = slow.next.next | |
| return dummy.next | |
| --- Check if Palindrome --- | |
| Time: O(n). Space: O(1). | |
| def is_palindrome(head): | |
| # Find middle | |
| slow = fast = head | |
| while fast and fast.next: | |
| slow = slow.next | |
| fast = fast.next.next | |
| # Reverse second half | |
| prev = None | |
| curr = slow | |
| while curr: | |
| nxt = curr.next | |
| curr.next = prev | |
| prev = curr | |
| curr = nxt | |
| # Compare halves | |
| left, right = head, prev | |
| while right: | |
| if left.data != right.data: | |
| return False | |
| left = left.next | |
| right = right.next | |
| return True | |
| --- Doubly Linked List --- | |
| Each node has a pointer to both next and previous nodes. | |
| class DNode: | |
| def __init__(self, data): | |
| self.data = data | |
| self.next = None | |
| self.prev = None | |
| class DoublyLinkedList: | |
| def __init__(self): | |
| self.head = None | |
| def insert_at_head(self, data): | |
| node = DNode(data) | |
| if self.head: | |
| self.head.prev = node | |
| node.next = self.head | |
| self.head = node | |
| def delete(self, node): | |
| if node.prev: | |
| node.prev.next = node.next | |
| else: | |
| self.head = node.next | |
| if node.next: | |
| node.next.prev = node.prev | |
| --- Circular Linked List --- | |
| Last node points back to head forming a circle. | |
| def is_circular(head): | |
| if not head: | |
| return False | |
| curr = head.next | |
| while curr and curr != head: | |
| curr = curr.next | |
| return curr == head | |