Linked List
Remarks
- Linked List is a sequence of node objects where each node store pointers to
- some other node in the sequence
- the data this node is associated with
- Linked List can be used as the underline data structure for many common abstract data types like lists, stacks, queues, etc.
operation | time |
---|---|
push at front | O(1) |
pop at front | O(1) |
push at back | O(1) |
pop at back | O(N) |
access via index | O(N) |
insert in middle | O(N) |
delete in middle | O(N) |
swap two nodes | O(N) |
Common operations on singly linked list.
1 Node class for singly linked list.
class ListNode:
def __init__(self, item: Optional=None):
self.item = item
self.next = None
def __repr__(self) -> str:
return f"{super().__repr__()[:-1]} of {self.item} >"
nodes = list(map(ListNode, ['Head', 'Shoulders', 'Knees', 'Toes']))
for prev, next_ in zip(nodes, nodes[1:]):
prev.next = next_
head = nodes[0]
tail = nodes[-1]
del nodes
2 Traverse the linked list.
def traverse(head: ListNode, verbose: int=0) -> tuple[ListNode, int]:
curr = head
n = 0
while curr:
if verbose: print(curr.item)
if not curr.next: tail = curr
curr = curr.next
n += 1
return tail, n
# def traverse(head, k):
# def traverse(head, val):
assert traverse(head) == (tail, 4)
3 Push and pop node at front.
def push_front(head, node):
node.next = head
return node
def pop_front(head):
return head.next
node = ListNode('Hat')
head = push_front(head, node)
assert head == node and traverse(head)[1] == 5
head = pop_front(head)
assert head == node.next and traverse(head)[1] == 4
4 Push and pop node at back.
def push_back(tail, node):
tail.next = node
return node
def pop_back(head, tail):
curr = head
while curr.next is not tail: curr = curr.next
curr.next = None
tail = curr
return tail
node = ListNode('Shoes')
tail = push_back(tail, node)
assert tail == node and traverse(head)[1] == 5
tail = pop_back(head, tail)
assert traverse(head)[1] == 4 and tail.next == None
5 Insert and delete nodes in the middle.
def get(head, i):
curr = head
prev = None
while curr and i:
i -= 1
prev = curr
curr = curr.next
return prev, curr
def insert(head, i, node):
prev, next_ = get(head, i)
prev.next = node
node.next = next_
def erase(head, i):
prev, curr = get(head, i)
prev.next = curr.next
node = ListNode('Belly Button')
insert(head, 2, node)
assert get(head, 2)[1] == node and traverse(head)[1] == 5
erase(head, 2)
assert get(head, 2)[1] == node.next and traverse(head)[1] == 4
6 Swap nodes.
def swap(head, i, j):
i, j = sorted([i, j])
prev_i, node_i = get(head, i)
prev_j, node_j = get(node_i, j-i)
if prev_i:
prev_i.next = node_j
else:
head = node_j
prev_j.next = node_i
node_i.next, node_j.next = node_j.next, node_i.next
return head
old_head = head
head = swap(head, 1, 0)
head = swap(head, 2, 3)
head = swap(head, 3, 2)
head = swap(head, 0, 1)
assert head == old_head
7 Reverse Linked List.
def reverse(head):
prev = None
trav = head
while trav: trav.next, prev, trav = prev, trav, trav.next
return prev
Common Problems
- Two Pointers
- left, right - Palindrome
- slow, fast - cycle, last k
- Multiple Lists
- Merge, Intersection
- Search and Sorting
- Implement Stack/Queue/Deque etc.
- Doubly Linked List + Hash Table
Sample Questions
Implementation
Operations on Linked List
- 876. Middle of the Linked List
- 19. Remove Nth Node From End of List
- 25. Reverse Nodes in k-Group
- 206. Reverse Linked List
- 92. Reverse Linked List II
- 143. Reorder List
- 21. Merge Two Sorted Lists
- 23. Merge k Sorted Lists
Two Pointers
- 141. Linked List Cycle
- 160. Intersection of Two Linked Lists
- 234. Palindrome Linked List
- 2. Add Two Numbers