🧠
Brute Force (Naive Traversal for Each Operation)
💡 Starting with a straightforward implementation helps understand the linked list mechanics and the cost of each operation before optimizing.
Intuition
Implement each operation by traversing the list from the head to the required position every time, without any shortcuts or extra pointers.
Algorithm
- Define a Node class with value and next pointer.
- Maintain a head pointer for the linked list.
- For get(index), traverse from head to index-th node and return its value or -1 if invalid.
- For addAtHead(val), create a new node and point it to current head, then update head.
- For addAtTail(val), traverse to the last node and append the new node.
- For addAtIndex(index, val), traverse to (index-1)-th node and insert new node after it; if index is 0, add at head.
- For deleteAtIndex(index), traverse to (index-1)-th node and remove the next node by adjusting pointers.
💡 The main challenge is correctly traversing and updating pointers without losing nodes or corrupting the list.
class Node:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.head = None
def get(self, index: int) -> int:
curr = self.head
for _ in range(index):
if not curr:
return -1
curr = curr.next
return curr.val if curr else -1
def addAtHead(self, val: int) -> None:
self.head = Node(val, self.head)
def addAtTail(self, val: int) -> None:
if not self.head:
self.head = Node(val)
return
curr = self.head
while curr.next:
curr = curr.next
curr.next = Node(val)
def addAtIndex(self, index: int, val: int) -> None:
if index == 0:
self.addAtHead(val)
return
curr = self.head
for _ in range(index - 1):
if not curr:
return
curr = curr.next
if curr:
curr.next = Node(val, curr.next)
def deleteAtIndex(self, index: int) -> None:
if not self.head:
return
if index == 0:
self.head = self.head.next
return
curr = self.head
for _ in range(index - 1):
if not curr.next:
return
curr = curr.next
if curr.next:
curr.next = curr.next.next
# Driver code to test
if __name__ == '__main__':
linkedList = MyLinkedList()
linkedList.addAtHead(1)
linkedList.addAtTail(3)
linkedList.addAtIndex(1, 2) # linked list becomes 1->2->3
print(linkedList.get(1)) # returns 2
linkedList.deleteAtIndex(1) # now the linked list is 1->3
print(linkedList.get(1)) # returns 3
Line Notes
class Node:Defines the basic building block of the linked list
self.head = NoneInitialize the linked list as empty
for _ in range(index):Traverse node by node to reach the desired index
if not curr:Check for invalid index or end of list
self.head = Node(val, self.head)Insert new node at the head by pointing it to current head
while curr.next:Traverse to the last node for addAtTail
curr.next = Node(val)Append new node at the tail
for _ in range(index - 1):Traverse to node before insertion/deletion point
curr.next = Node(val, curr.next)Insert new node by adjusting pointers
self.head = self.head.nextDelete head node by moving head pointer
curr.next = curr.next.nextDelete node by skipping it in the chain
class MyLinkedList {
private class Node {
int val;
Node next;
Node(int val) { this.val = val; }
}
private Node head;
public MyLinkedList() {
head = null;
}
public int get(int index) {
Node curr = head;
for (int i = 0; i < index; i++) {
if (curr == null) return -1;
curr = curr.next;
}
return curr == null ? -1 : curr.val;
}
public void addAtHead(int val) {
Node newNode = new Node(val);
newNode.next = head;
head = newNode;
}
public void addAtTail(int val) {
if (head == null) {
head = new Node(val);
return;
}
Node curr = head;
while (curr.next != null) {
curr = curr.next;
}
curr.next = new Node(val);
}
public void addAtIndex(int index, int val) {
if (index == 0) {
addAtHead(val);
return;
}
Node curr = head;
for (int i = 0; i < index - 1; i++) {
if (curr == null) return;
curr = curr.next;
}
if (curr != null) {
Node newNode = new Node(val);
newNode.next = curr.next;
curr.next = newNode;
}
}
public void deleteAtIndex(int index) {
if (head == null) return;
if (index == 0) {
head = head.next;
return;
}
Node curr = head;
for (int i = 0; i < index - 1; i++) {
if (curr.next == null) return;
curr = curr.next;
}
if (curr.next != null) {
curr.next = curr.next.next;
}
}
// Main method to test
public static void main(String[] args) {
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1, 2); // linked list becomes 1->2->3
System.out.println(linkedList.get(1)); // returns 2
linkedList.deleteAtIndex(1); // now the linked list is 1->3
System.out.println(linkedList.get(1)); // returns 3
}
}
Line Notes
private class Node {Inner class encapsulates node structure
head = null;Initialize empty linked list
for (int i = 0; i < index; i++) {Traverse to target index for get
if (curr == null) return -1;Handle invalid index gracefully
newNode.next = head;Insert new node at head by linking to current head
while (curr.next != null) {Traverse to last node for addAtTail
curr.next = new Node(val);Append new node at tail
for (int i = 0; i < index - 1; i++) {Traverse to node before insertion/deletion
newNode.next = curr.next;Link new node to next node before insertion
head = head.next;Delete head by moving head pointer
curr.next = curr.next.next;Delete node by skipping it
#include <iostream>
using namespace std;
class MyLinkedList {
struct Node {
int val;
Node* next;
Node(int v) : val(v), next(nullptr) {}
};
Node* head;
public:
MyLinkedList() : head(nullptr) {}
int get(int index) {
Node* curr = head;
for (int i = 0; i < index; i++) {
if (!curr) return -1;
curr = curr->next;
}
return curr ? curr->val : -1;
}
void addAtHead(int val) {
Node* newNode = new Node(val);
newNode->next = head;
head = newNode;
}
void addAtTail(int val) {
if (!head) {
head = new Node(val);
return;
}
Node* curr = head;
while (curr->next) {
curr = curr->next;
}
curr->next = new Node(val);
}
void addAtIndex(int index, int val) {
if (index == 0) {
addAtHead(val);
return;
}
Node* curr = head;
for (int i = 0; i < index - 1; i++) {
if (!curr) return;
curr = curr->next;
}
if (curr) {
Node* newNode = new Node(val);
newNode->next = curr->next;
curr->next = newNode;
}
}
void deleteAtIndex(int index) {
if (!head) return;
if (index == 0) {
Node* temp = head;
head = head->next;
delete temp;
return;
}
Node* curr = head;
for (int i = 0; i < index - 1; i++) {
if (!curr->next) return;
curr = curr->next;
}
if (curr->next) {
Node* temp = curr->next;
curr->next = curr->next->next;
delete temp;
}
}
};
int main() {
MyLinkedList linkedList;
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1, 2); // linked list becomes 1->2->3
cout << linkedList.get(1) << endl; // returns 2
linkedList.deleteAtIndex(1); // now the linked list is 1->3
cout << linkedList.get(1) << endl; // returns 3
return 0;
}
Line Notes
struct Node {Defines node structure with value and pointer
Node* head;Pointer to the start of the linked list
for (int i = 0; i < index; i++) {Traverse to the index-th node for get
if (!curr) return -1;Check for invalid index or end of list
newNode->next = head;Insert new node at head by linking to current head
while (curr->next) {Traverse to last node for addAtTail
curr->next = new Node(val);Append new node at tail
for (int i = 0; i < index - 1; i++) {Traverse to node before insertion/deletion
newNode->next = curr->next;Link new node to next node before insertion
Node* temp = head;Store node to delete to free memory
curr->next = curr->next->next;Delete node by skipping it
class Node {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
class MyLinkedList {
constructor() {
this.head = null;
}
get(index) {
let curr = this.head;
for (let i = 0; i < index; i++) {
if (!curr) return -1;
curr = curr.next;
}
return curr ? curr.val : -1;
}
addAtHead(val) {
this.head = new Node(val, this.head);
}
addAtTail(val) {
if (!this.head) {
this.head = new Node(val);
return;
}
let curr = this.head;
while (curr.next) {
curr = curr.next;
}
curr.next = new Node(val);
}
addAtIndex(index, val) {
if (index === 0) {
this.addAtHead(val);
return;
}
let curr = this.head;
for (let i = 0; i < index - 1; i++) {
if (!curr) return;
curr = curr.next;
}
if (curr) {
curr.next = new Node(val, curr.next);
}
}
deleteAtIndex(index) {
if (!this.head) return;
if (index === 0) {
this.head = this.head.next;
return;
}
let curr = this.head;
for (let i = 0; i < index - 1; i++) {
if (!curr.next) return;
curr = curr.next;
}
if (curr.next) {
curr.next = curr.next.next;
}
}
}
// Test code
const linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1, 2); // linked list becomes 1->2->3
console.log(linkedList.get(1)); // returns 2
linkedList.deleteAtIndex(1); // now the linked list is 1->3
console.log(linkedList.get(1)); // returns 3
Line Notes
class Node {Defines node structure with value and next pointer
this.head = null;Initialize empty linked list
for (let i = 0; i < index; i++) {Traverse to index-th node for get
if (!curr) return -1;Handle invalid index gracefully
this.head = new Node(val, this.head);Insert new node at head by linking to current head
while (curr.next) {Traverse to last node for addAtTail
curr.next = new Node(val);Append new node at tail
for (let i = 0; i < index - 1; i++) {Traverse to node before insertion/deletion
curr.next = new Node(val, curr.next);Insert new node by adjusting pointers
this.head = this.head.next;Delete head node by moving head pointer
curr.next = curr.next.next;Delete node by skipping it
TimeO(n) per operation in worst case
SpaceO(n) for storing nodes
Each operation may require traversing up to n nodes, so time complexity is linear per operation. Space is linear due to storing nodes.
💡 For n=1000, each operation might traverse up to 1000 nodes, which is acceptable but can be slow for large n.
Interview Verdict: Accepted but inefficient for large inputs
This approach works correctly but is slow because it traverses the list for many operations. It is a good starting point to understand the problem.