Design your implementation of the linked list. You can choose to use a singly or doubly linked list.
A node in a singly linked list should have two attributes: val and next. val is the value of the current node, and next is a pointer/reference to the next node.
If you want to use the doubly linked list, you will need one more attribute prev to indicate the previous node in the linked list. Assume all nodes in the linked list are 0-indexed.
Implement the MyLinkedList class:
MyLinkedList() Initializes the MyLinkedList object.int get(int index) Get the value of the indexth node in the linked list. If the index is invalid, return -1.void addAtHead(int val) Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.void addAtTail(int val) Append a node of value val as the last element of the linked list.void addAtIndex(int index, int val) Add a node of value val before the indexth node in the linked list. If index equals the length of the linked list, the node will be appended to the end of the linked list. If index is greater than the length, the node will not be inserted.void deleteAtIndex(int index) Delete the indexth node in the linked list, if the index is valid.Example 1:
Input ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"] [[], [1], [3], [1, 2], [1], [1], [1]] Output [null, null, null, null, 2, null, 3] Explanation MyLinkedList myLinkedList = new MyLinkedList(); myLinkedList.addAtHead(1); myLinkedList.addAtTail(3); myLinkedList.addAtIndex(1, 2); // linked list becomes 1->2->3 myLinkedList.get(1); // return 2 myLinkedList.deleteAtIndex(1); // now the linked list is 1->3 myLinkedList.get(1); // return 3
Constraints:
0 <= index, val <= 10002000 calls will be made to get, addAtHead, addAtTail, addAtIndex and deleteAtIndex.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to creating a linked list involves manually managing each connection. It's like physically linking paperclips together, one by one, to form a chain. We directly manipulate the links to add, remove, or find specific paperclips.
Here's how the algorithm would work step-by-step:
class Node:
def __init__(self, value):
self.value = value
self.next = None
class MyLinkedList:
def __init__(self):
self.head = None
self.size_of_linked_list = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size_of_linked_list:
return -1
current_node = self.head
for _ in range(index):
current_node = current_node.next
return current_node.value
def addAtHead(self, value: int) -> None:
new_node = Node(value)
new_node.next = self.head
self.head = new_node
self.size_of_linked_list += 1
def addAtTail(self, value: int) -> None:
new_node = Node(value)
# Handle empty list case
if not self.head:
self.head = new_node
self.size_of_linked_list += 1
return
current_node = self.head
while current_node.next:
current_node = current_node.next
current_node.next = new_node
self.size_of_linked_list += 1
def addAtIndex(self, index: int, value: int) -> None:
if index < 0 or index > self.size_of_linked_list:
return
if index == 0:
self.addAtHead(value)
return
new_node = Node(value)
current_node = self.head
# Traverse up to node before target index
for _ in range(index - 1):
current_node = current_node.next
new_node.next = current_node.next
current_node.next = new_node
self.size_of_linked_list += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size_of_linked_list:
return
# Handle deleting head node
if index == 0:
self.head = self.head.next
self.size_of_linked_list -= 1
return
current_node = self.head
# Traverse to the node BEFORE the one to delete
for _ in range(index - 1):
current_node = current_node.next
# Correct the pointers to remove the node
current_node.next = current_node.next.next
self.size_of_linked_list -= 1We will create a custom list data structure. This involves defining a way to link items together, and then providing operations to add, remove, and retrieve items from that list, while keeping track of the list's connections.
Here's how the algorithm would work step-by-step:
class Node:
def __init__(self, value):
self.value = value
self.next = None
class MyLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
current_node = self.head
for _ in range(index):
current_node = current_node.next
return current_node.value
def addAtHead(self, value: int) -> None:
new_node = Node(value)
if self.head is None:
self.head = new_node
self.tail = new_node
else:
new_node.next = self.head
self.head = new_node
self.size += 1
def addAtTail(self, value: int) -> None:
new_node = Node(value)
if self.tail is None:
self.head = new_node
self.tail = new_node
else:
self.tail.next = new_node
self.tail = new_node
self.size += 1
def addAtIndex(self, index: int, value: int) -> None:
if index < 0 or index > self.size:
return
if index == 0:
self.addAtHead(value)
return
if index == self.size:
self.addAtTail(value)
return
new_node = Node(value)
current_node = self.head
for _ in range(index - 1):
current_node = current_node.next
# Insert the new node in the linked list
new_node.next = current_node.next
current_node.next = new_node
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
if index == 0:
# Handle deleting the head
self.head = self.head.next
if self.head is None:
self.tail = None
self.size -= 1
return
current_node = self.head
for _ in range(index - 1):
current_node = current_node.next
# Handle deleting the last node
if current_node.next == self.tail:
current_node.next = None
self.tail = current_node
else:
current_node.next = current_node.next.next
self.size -= 1| Case | How to Handle |
|---|---|
| Adding to the head of an empty list | The head and tail pointers should both point to the new node |
| Adding to the tail of an empty list | The head and tail pointers should both point to the new node |
| Deleting the only element in the list | The head and tail pointers should both be set to null |
| Deleting the head element | The head pointer should be updated to the next node, and previous head's next pointer must be set to null |
| Deleting the tail element | The tail pointer should be updated to the previous node, and previous tail's previous pointer must be set to null |
| Deleting an element in the middle of the list | The previous node's next pointer and the next node's previous pointer should point to each other, bypassing the deleted node |
| Index out of bounds for get, addAtIndex, deleteAtIndex | Return -1 for get, do nothing for addAtIndex and deleteAtIndex if index is out of bounds |
| Integer overflow when calculating list size or index | Ensure list size is tracked with appropriate data type, and index calculations avoid overflow |