Linked List¶
Table of Contents¶
- 160. Intersection of Two Linked Lists (Easy)
- 206. Reverse Linked List (Easy)
- 234. Palindrome Linked List (Easy)
- 141. Linked List Cycle (Easy)
- 142. Linked List Cycle II (Medium)
- 21. Merge Two Sorted Lists (Easy)
- 2. Add Two Numbers (Medium)
- 19. Remove Nth Node From End of List (Medium)
- 24. Swap Nodes in Pairs (Medium)
- 25. Reverse Nodes in k-Group (Hard)
- 138. Copy List with Random Pointer (Medium)
- 148. Sort List (Medium)
- 23. Merge k Sorted Lists (Hard)
- 146. LRU Cache (Medium)
160. Intersection of Two Linked Lists¶
-
LeetCode | LeetCode CH (Easy)
-
Tags: hash table, linked list, two pointers
- Find the node at which the intersection of two singly linked lists begins.
graph LR
a1((a1)) --> a2((a2))
a2 --> c1((c1))
b1((b1)) --> b2((b2))
b2 --> b3((b3))
b3 --> c1
c1 --> c2((c2))
c2 --> c3((c3))
from typing import Optional
from template import ListNode
# Hash Set
def getIntersectionNodeHash(
headA: ListNode, headB: ListNode
) -> Optional[ListNode]:
if not headA or not headB:
return None
visited = set()
cur = headA
while cur:
visited.add(cur)
cur = cur.next
cur = headB
while cur:
if cur in visited:
return cur
cur = cur.next
return None
# Two Pointers
def getIntersectionNodeTP(
headA: ListNode, headB: ListNode
) -> Optional[ListNode]:
if not headA or not headB:
return None
a, b = headA, headB
while a != b:
a = a.next if a else headB
b = b.next if b else headA
return a
listA = [4, 1, 8, 4, 5]
listB = [5, 6, 1, 8, 4, 5]
headA = ListNode.create(listA)
print(headA)
# 4 -> 1 -> 8 -> 4 -> 5
headB = ListNode.create(listB)
print(headB)
# 5 -> 6 -> 1 -> 8 -> 4 -> 5
headA.intersect(headB, 8)
print(getIntersectionNodeHash(headA, headB))
# 8 -> 4 -> 5
print(getIntersectionNodeTP(headA, headB))
# 8 -> 4 -> 5
206. Reverse Linked List¶
-
LeetCode | LeetCode CH (Easy)
-
Tags: linked list, recursion
- Reverse a singly linked list.
graph LR
A((1)) --> B((2))
B --> C((3))
C --> D((4))
D --> E((5))
graph RL
E((5)) --> D((4))
D --> C((3))
C --> B((2))
B --> A((1))
from typing import Optional
from template import ListNode
# Iterative
def reverseListIterative(head: Optional[ListNode]) -> Optional[ListNode]:
cur = head
prev = None
while cur:
temp = cur.next
cur.next = prev
prev = cur
cur = temp
return prev
# Recursive
def reverseListRecursive(head: Optional[ListNode]) -> Optional[ListNode]:
def reverse(cur, prev):
if not cur:
return prev
temp = cur.next
cur.next = prev
return reverse(temp, cur)
return reverse(head, None)
nums = [1, 2, 3, 4, 5]
head1 = ListNode.create(nums)
print(head1)
# 1 -> 2 -> 3 -> 4 -> 5
print(reverseListIterative(head1))
# 5 -> 4 -> 3 -> 2 -> 1
head2 = ListNode.create(nums)
print(reverseListRecursive(head2))
# 5 -> 4 -> 3 -> 2 -> 1
234. Palindrome Linked List¶
-
LeetCode | LeetCode CH (Easy)
-
Tags: linked list, two pointers, stack, recursion
from typing import Optional
from template import ListNode
# Linked List
def isPalindrome(head: Optional[ListNode]) -> bool:
if not head or not head.next:
return True
def middle(node):
fast, slow = node, node
while fast and fast.next:
fast = fast.next.next
slow = slow.next
return slow
def reverse(node):
cur, pre = node, None
while cur:
nxt = cur.next
cur.next = pre
pre = cur
cur = nxt
return pre
mid1 = head
mid2 = reverse(middle(head))
while mid2:
if mid1.val != mid2.val:
return False
mid1 = mid1.next
mid2 = mid2.next
return True
head = ListNode().create([1, 2, 2, 1])
print(isPalindrome(head)) # True
141. Linked List Cycle¶
-
LeetCode | LeetCode CH (Easy)
-
Tags: hash table, linked list, two pointers
- Determine if a linked list has a cycle in it.
graph LR
A((3)) --> B((2))
B --> C((0))
C --> D((4))
graph LR
A((3)) --> B((2))
B --> C((0))
C --> D((4))
D --> B
from typing import Optional
from template import ListNode
def hasCycle(head: Optional[ListNode]) -> bool:
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
print(hasCycle(ListNode.create([3, 2, 0, -4]))) # False
print(hasCycle(ListNode.create([3, 2, 0, -4], 1))) # True
#include <iostream>
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
bool hasCycle(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (fast == slow) return true;
}
return false;
}
};
142. Linked List Cycle II¶
-
LeetCode | LeetCode CH (Medium)
-
Tags: hash table, linked list, two pointers
- Given a linked list, return the node where the cycle begins. If there is no cycle, return
None
.
graph LR
A[3] --> B[2]
B --> C[0]
C --> D[-4]
D --> B
from typing import Optional
from template import ListNode
def detectCycle(head: Optional[ListNode]) -> Optional[ListNode]:
slow, fast = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if slow == fast:
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
return None
head1 = ListNode.create([3, 2, 0, -4], 1)
print(detectCycle(head1).val) # 2
head2 = ListNode.create([3, 2, 0, -4])
print(detectCycle(head2)) # None
#include <iostream>
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
ListNode* detectCycle(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (fast == slow) {
slow = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return nullptr;
}
};
21. Merge Two Sorted Lists¶
-
LeetCode | LeetCode CH (Easy)
-
Tags: linked list, recursion
- Merge the two lists into one sorted list.
from typing import Optional
from template import ListNode
# Linked List
def mergeTwoLists(
list1: Optional[ListNode], list2: Optional[ListNode]
) -> Optional[ListNode]:
dummy = ListNode()
cur = dummy
while list1 and list2:
if list1.val < list2.val:
cur.next = list1
list1 = list1.next
else:
cur.next = list2
list2 = list2.next
cur = cur.next
if list1:
cur.next = list1
elif list2:
cur.next = list2
return dummy.next
list1 = ListNode.create([1, 2, 4])
list2 = ListNode.create([1, 3, 4])
print(mergeTwoLists(list1, list2))
# 1 -> 1 -> 2 -> 3 -> 4 -> 4
struct ListNode {
int val;
ListNode* next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode* next) : val(x), next(next) {}
};
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode dummy;
ListNode* cur = &dummy;
while (list1 && list2) {
if (list1->val < list2->val) {
cur->next = list1;
list1 = list1->next;
} else {
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
cur->next = list1 ? list1 : list2;
return dummy.next;
}
};
2. Add Two Numbers¶
-
LeetCode | LeetCode CH (Medium)
-
Tags: linked list, math, recursion
- Represent the sum of two numbers as a linked list.
from typing import Optional
from template import ListNode
# Linked List
def addTwoNumbers(
l1: Optional[ListNode], l2: Optional[ListNode]
) -> Optional[ListNode]:
dummy = ListNode()
cur = dummy
carry = 0
while l1 or l2:
v1 = l1.val if l1 else 0
v2 = l2.val if l2 else 0
carry, val = divmod(v1 + v2 + carry, 10)
cur.next = ListNode(val)
cur = cur.next
if l1:
l1 = l1.next
if l2:
l2 = l2.next
if carry:
cur.next = ListNode(val=carry)
return dummy.next
l1 = ListNode.create([2, 4, 3])
l2 = ListNode.create([5, 6, 4])
print(addTwoNumbers(l1, l2)) # 7 -> 0 -> 8
struct ListNode {
int val;
ListNode* next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode* next) : val(x), next(next) {}
};
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode dummy;
ListNode* cur = &dummy;
int carry = 0;
while (l1 || l2 || carry) {
if (l1) {
carry += l1->val;
l1 = l1->next;
}
if (l2) {
carry += l2->val;
l2 = l2->next;
}
cur->next = new ListNode(carry % 10);
cur = cur->next;
carry /= 10;
}
return dummy.next;
}
};
19. Remove Nth Node From End of List¶
-
LeetCode | LeetCode CH (Medium)
-
Tags: linked list, two pointers
- Given the
head
of a linked list, remove then-th
node from the end of the list and return its head.
from typing import Optional
from template import ListNode
# Linked List
def removeNthFromEnd(head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0, head)
fast, slow = dummy, dummy
for _ in range(n):
fast = fast.next
while fast.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
head = [1, 2, 3, 4, 5]
n = 2
head = ListNode.create(head)
print(head) # 1 -> 2 -> 3 -> 4 -> 5
print(removeNthFromEnd(head, n)) # 1 -> 2 -> 3 -> 5
24. Swap Nodes in Pairs¶
-
LeetCode | LeetCode CH (Medium)
-
Tags: linked list, recursion
- Given a linked list, swap every two adjacent nodes and return its head.
from typing import Optional
from template import ListNode
# Linked List
def swapPairs(head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0, head)
n0 = dummy
n1 = dummy.next
while n1 and n1.next:
n2 = n1.next
n3 = n2.next
n0.next = n2
n2.next = n1
n1.next = n3
n0 = n1
n1 = n3
return dummy.next
nums = [1, 2, 3, 4, 5]
head = ListNode.create(nums)
print(head)
# 1 -> 2 -> 3 -> 4 -> 5
print(swapPairs(head))
# 2 -> 1 -> 4 -> 3 -> 5
25. Reverse Nodes in k-Group¶
-
LeetCode | LeetCode CH (Hard)
-
Tags: linked list, recursion
from typing import Optional
from template import ListNode
# Linked List
def reverseKGroup(head: Optional[ListNode], k: int) -> Optional[ListNode]:
n = 0
cur = head
while cur:
n += 1
cur = cur.next
p0 = dummy = ListNode(next=head)
pre = None
cur = head
while n >= k:
n -= k
for _ in range(k):
nxt = cur.next
cur.next = pre
pre = cur
cur = nxt
nxt = p0.next
nxt.next = cur
p0.next = pre
p0 = nxt
return dummy.next
if __name__ == "__main__":
head = [1, 2, 3, 4, 5]
k = 2
head = ListNode.create(head)
print(head) # 1 -> 2 -> 3 -> 4 -> 5
print(reverseKGroup(head, k)) # 2 -> 1 -> 4 -> 3 -> 5
138. Copy List with Random Pointer¶
-
LeetCode | LeetCode CH (Medium)
-
Tags: hash table, linked list
from typing import Optional
class Node:
def __init__(self, x: int, next: "Node" = None, random: "Node" = None):
self.val = int(x)
self.next = next
self.random = random
def copyRandomList(head: "Optional[Node]") -> "Optional[Node]":
if not head:
return None
# Copy nodes and link them together
cur = head
while cur:
new_node = Node(cur.val)
new_node.next = cur.next
cur.next = new_node
cur = new_node.next
# Copy random pointers
cur = head
while cur:
cur.next.random = cur.random.next if cur.random else None
cur = cur.next.next
# Separate the original and copied lists
cur = head
new_head = head.next
while cur:
new_node = cur.next
cur.next = new_node.next
new_node.next = new_node.next.next if new_node.next else None
cur = cur.next
return new_head
148. Sort List¶
-
LeetCode | LeetCode CH (Medium)
-
Tags: linked list, two pointers, divide and conquer, sorting, merge sort
from typing import Optional
from template import ListNode
# Linked List
def sortListSort(head: Optional[ListNode]) -> Optional[ListNode]:
nums = []
while head:
nums.append(head.val)
head = head.next
dummy = ListNode()
cur = dummy
nums.sort()
for num in nums:
cur.next = ListNode(val=num)
cur = cur.next
return dummy.next
# Linked List
def sortListDivideConquer(head: Optional[ListNode]) -> Optional[ListNode]:
def middle(node):
fast, slow = node, node
while fast and fast.next:
pre = slow
slow = slow.next
fast = fast.next.next
pre.next = None
return slow
def merge_two_lists(l1, l2):
dummy = ListNode()
cur = dummy
while l1 and l2:
if l1.val < l2.val:
cur.next = l1
l1 = l1.next
else:
cur.next = l2
l2 = l2.next
cur = cur.next
cur.next = l1 if l1 else l2
return dummy.next
if not head or not head.next:
return head
head2 = middle(head)
head = sortListDivideConquer(head)
head2 = sortListDivideConquer(head2)
return merge_two_lists(head, head2)
head = ListNode().create([4, 2, 1, 3])
print(head) # 4 -> 2 -> 1 -> 3
print(sortListSort(head)) # 1 -> 2 -> 3 -> 4
print(sortListDivideConquer(head)) # 1 -> 2 -> 3 -> 4
23. Merge k Sorted Lists¶
-
LeetCode | LeetCode CH (Hard)
-
Tags: linked list, divide and conquer, heap priority queue, merge sort
- Prerequisite: 21. Merge Two Sorted Lists
- Video explanation: 23. Merge K Sorted Lists - NeetCode
import copy
import heapq
from typing import List, Optional
from template import ListNode
# Divide and Conquer
def mergeKListsDC(lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if not lists or len(lists) == 0:
return None
def mergeTwo(l1, l2):
dummy = ListNode()
cur = dummy
while l1 and l2:
if l1.val < l2.val:
cur.next = l1
l1 = l1.next
else:
cur.next = l2
l2 = l2.next
cur = cur.next
cur.next = l1 if l1 else l2
return dummy.next
while len(lists) > 1:
merged = []
for i in range(0, len(lists), 2):
l1 = lists[i]
l2 = lists[i + 1] if i + 1 < len(lists) else None
merged.append(mergeTwo(l1, l2))
lists = merged
return lists[0]
# Heap - Merge k Sorted
def mergeKLists(lists: List[Optional[ListNode]]) -> Optional[ListNode]:
dummy = ListNode()
cur = dummy
minHeap = [] # (val, idx, node)
for idx, head in enumerate(lists):
if head:
heapq.heappush(minHeap, (head.val, idx, head))
while minHeap:
_, idx, node = heapq.heappop(minHeap)
cur.next = node
cur = cur.next
node = node.next
if node:
heapq.heappush(minHeap, (node.val, idx, node))
return dummy.next
n1 = ListNode.create([1, 4, 5])
n2 = ListNode.create([1, 3, 4])
n3 = ListNode.create([2, 6])
lists = [n1, n2, n3]
lists1 = copy.deepcopy(lists)
lists2 = copy.deepcopy(lists)
print(mergeKListsDC(lists1))
# 1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 5 -> 6
print(mergeKLists(lists2))
# 1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 5 -> 6
146. LRU Cache¶
-
LeetCode | LeetCode CH (Medium)
-
Tags: hash table, linked list, design, doubly linked list
- Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.
- lru
Data structure | Description |
---|---|
Doubly Linked List | To store the key-value pairs. |
Hash Map | To store the key-node pairs. |
from collections import OrderedDict
# Doubly Linked List
class Node:
def __init__(self, key=0, val=0):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cap = capacity
self.cache = {}
self.head = Node()
self.tail = Node()
self.head.next = self.tail
self.tail.prev = self.head
def remove(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def add_to_last(self, node):
self.tail.prev.next = node
node.prev = self.tail.prev
node.next = self.tail
self.tail.prev = node
def move_to_last(self, node):
self.remove(node)
self.add_to_last(node)
def get(self, key: int) -> int:
if key not in self.cache:
return -1
node = self.cache[key]
self.move_to_last(node)
return node.val
def put(self, key: int, value: int) -> None:
if key in self.cache:
node = self.cache[key]
node.val = value
self.move_to_last(node)
return None
if len(self.cache) == self.cap:
del self.cache[self.head.next.key]
self.remove(self.head.next)
node = Node(key=key, val=value)
self.cache[key] = node
self.add_to_last(node)
# OrderedDict
class LRUCacheOrderedDict:
def __init__(self, capacity: int):
self.cache = OrderedDict()
self.cap = capacity
def get(self, key: int):
if key not in self.cache:
return -1
self.cache.move_to_end(key, last=True)
return self.cache[key]
def put(self, key: int, value: int):
if key in self.cache:
self.cache.move_to_end(key, last=True)
elif len(self.cache) >= self.cap:
self.cache.popitem(last=False)
self.cache[key] = value
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
assert cache.get(1) == 1
cache.put(3, 3)
assert cache.get(2) == -1
cache.put(4, 4)
assert cache.get(1) == -1
assert cache.get(3) == 3
assert cache.get(4) == 4
cache = LRUCacheOrderedDict(2)
cache.put(1, 1)
cache.put(2, 2)
assert cache.get(1) == 1
cache.put(3, 3)
assert cache.get(2) == -1
cache.put(4, 4)
assert cache.get(1) == -1
assert cache.get(3) == 3
assert cache.get(4) == 4
print("LRU Cache passed")
print("LRU Cache Ordered Dict passed")
#include <iostream>
#include <unordered_map>
using namespace std;
class Node {
public:
int key;
int val;
Node *prev;
Node *next;
Node(int k = 0, int v = 0) : key(k), val(v), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
int cap;
unordered_map<int, Node *> cache;
Node *head;
Node *tail;
void remove(Node *node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
void insert_to_last(Node *node) {
tail->prev->next = node;
node->prev = tail->prev;
tail->prev = node;
node->next = tail;
}
void move_to_last(Node *node) {
remove(node);
insert_to_last(node);
}
public:
LRUCache(int capacity) {
this->cap = capacity;
head = new Node();
tail = new Node();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if (cache.find(key) != cache.end()) {
Node *node = cache[key];
move_to_last(node);
return node->val;
}
return -1;
}
void put(int key, int value) {
if (cache.find(key) != cache.end()) {
Node *node = cache[key];
node->val = value;
move_to_last(node);
} else {
Node *newNode = new Node(key, value);
cache[key] = newNode;
insert_to_last(newNode);
if ((int)cache.size() > cap) {
Node *removed = head->next;
remove(removed);
cache.erase(removed->key);
delete removed;
}
}
}
};
int main() {
LRUCache lru(2);
lru.put(1, 1);
lru.put(2, 2);
cout << lru.get(1) << endl; // 1
lru.put(3, 3);
cout << lru.get(2) << endl; // -1
lru.put(4, 4);
cout << lru.get(1) << endl; // -1
cout << lru.get(3) << endl; // 3
cout << lru.get(4) << endl; // 4
return 0;
}