Part 9 · Lesson 35 · read

Building a Linked List

Nodes and pointers (links to the next node), built from scratch.

On this page

  1. A chain of nodes
  2. Building nodes by hand
  3. Traversal and length
  4. Insert at the head, at the tail and at a position
  5. Delete a node
  6. Traced: deleting 3 from 1 → 2 → 3 → 4
  7. The dummy head trick (a fake first node)
  8. Doubly linked lists
  9. Arrays vs linked lists
  10. Practice questions (7)
  11. Make it stick
  12. What's next

A chain of nodes

An array keeps its items side by side in memory. This is why arr[5] is instant. A linked list works in a different way. Each item lives in its own small object called a node. Each node holds two things: its value, and a reference to the next node (a link that tells you where the next node is; many books call it a pointer). You find the items by following the links. It is like a treasure hunt, where each clue tells you where to find the next clue.

TEXT
head │ ▼[ 1 | • ]──▶[ 2 | • ]──▶[ 3 | • ]──▶ null  value next
  • The head is the first node. The head stands for the whole list. If you lose the head, you lose the list.
  • The next of the last node is null (a special value that means “nothing here”). This marks the end.
  • An empty list is simply head === null.

Linked lists are common in interviews because they test one skill in a clean way: changing links without losing any node. You will need the same skill later for trees and graphs.

Building nodes by hand

JavaScript
class ListNode {  constructor(val, next = null) {    this.val = val;      // the data this node holds    this.next = next;    // the next node, or null if this is the last node  }}// 1 -> 2 -> 3const c = new ListNode(3);const b = new ListNode(2, c);const a = new ListNode(1, b);const head = a;                 // the first node stands for the whole listconsole.log(head.val);                 // 1console.log(head.next.val);            // 2console.log(head.next.next.val);       // 3console.log(head.next.next.next);      // null  (the end of the list)

We will use this same ListNode class (with val and next) in every lesson, because it is the class that LeetCode uses. To keep the examples short, we use two helper functions that change an array into a list and a list into an array.

JavaScript
// Two small helper functions used in every example of this part.function fromArray(values) {  const dummy = new ListNode(0);  let tail = dummy;  for (const v of values) { tail.next = new ListNode(v); tail = tail.next; }  return dummy.next;}function toArray(head) {  const out = [];  for (let cur = head; cur !== null; cur = cur.next) out.push(cur.val);  return out;}console.log(toArray(fromArray([4, 5, 6]))); // [4, 5, 6]console.log(toArray(null));                 // []

Traversal and length

There is no list[i]. To visit every node, put a cursor (a moving marker) at the head. Move it with cur = cur.next until it becomes null. All other list code is built on this loop.

JavaScript
function length(head) {  let count = 0;  let cur = head;                 // a cursor (a moving marker) that walks along the list  while (cur !== null) {    count++;    cur = cur.next;               // move to the next node  }  return count;}function contains(head, target) {  for (let cur = head; cur !== null; cur = cur.next) {    if (cur.val === target) return true;  }  return false;}const list = fromArray([4, 5, 6]);console.log(length(list));       // 3console.log(contains(list, 5));  // trueconsole.log(contains(list, 9));  // false

To get the i-th node, you must walk i steps. So access takes O(n) time, not O(1) like an array. This is the price you pay for a linked list.

Insert at the head, at the tail and at a position

JavaScript
// At the head: O(1). The new node points at the old head.function addAtHead(head, val) {  return new ListNode(val, head);          // the new node becomes the head}// At the tail: first walk to the last node, so it is O(n).function addAtTail(head, val) {  const node = new ListNode(val);  if (head === null) return node;  let cur = head;  while (cur.next !== null) cur = cur.next;  cur.next = node;  return head;}// After a node: connect the new node FIRST, and only then cut the old link.function addAfter(node, val) {  node.next = new ListNode(val, node.next);}let h = fromArray([2, 3]);h = addAtHead(h, 1);            // 1 -> 2 -> 3h = addAtTail(h, 4);            // 1 -> 2 -> 3 -> 4addAfter(h.next, 99);           // 1 -> 2 -> 99 -> 3 -> 4console.log(toArray(h));        // [1, 2, 99, 3, 4]
Order matters: connect first, cut second
To insert after node, write node.next = new ListNode(val, node.next). The new node points at the old next node before node.next is changed. If you change node.next first, you lose the rest of the list.
Dry run · Cost of inserting
WhereTimeWhy
at the headO(1)one new node and one link change
at the tail (only a head pointer)O(n)you must walk to the last node first
at the tail (if you also keep a pointer to the tail)O(1)no walk needed
after a node you already holdO(1)two link changes
at index iO(i)walk to the node before position i

Delete a node

To remove a node, make the node before it point to the node after it. Nothing points to the removed node now, so it is not part of the list any more. (JavaScript's garbage collector, the part that cleans up unused memory, will free it.) This means you need the previous node. So you walk with a second marker that stays one step behind, or you stop one node early.

JavaScript
function deleteValue(head, target) {  if (head === null) return null;  if (head.val === target) return head.next;       // we delete the head, so the next node becomes the head  let prev = head;  while (prev.next !== null && prev.next.val !== target) prev = prev.next;  if (prev.next !== null) prev.next = prev.next.next;   // skip over the node: nothing points to it now  return head;}console.log(toArray(deleteValue(fromArray([1, 2, 3, 4]), 3))); // [1, 2, 4]console.log(toArray(deleteValue(fromArray([1, 2, 3, 4]), 1))); // [2, 3, 4]console.log(toArray(deleteValue(fromArray([1, 2, 3, 4]), 9))); // [1, 2, 3, 4]

Traced: deleting 3 from 1 → 2 → 3 → 4

step 1 / 10
Set up
prev = null, cur = head

cur is a cursor (a moving marker) that walks along the list. prev always stays one node behind cur. To remove a node, you must change the node before it.

▶let prev = null, cur = head;2while (cur !== null) {3  if (cur.val === 3) {4    prev.next = cur.next;5    break;6  }7  prev = cur;8  cur = cur.next;9}
Variables
list[1, 2, 3, 4]
prev"null"
cur1
Console
(nothing printed yet)
prev stays one step behind cur. When cur is the node to remove, prev jumps over it. · interactive

The dummy head trick (a fake first node)

Deleting the head needs different code from deleting any other node, because the head has no node before it. This special case causes most linked-list bugs. The fix is a dummy node (a fake extra node) that you put in front of the head. Now every real node has a node before it, including the head. The answer is dummy.next.

JavaScript
function removeValue(head, target) {  const dummy = new ListNode(0, head);     // a fake node in front of the real head  let prev = dummy;  while (prev.next !== null) {    if (prev.next.val === target) prev.next = prev.next.next;   // delete it, but stay on prev, because the new next may match too    else prev = prev.next;  }  return dummy.next;                       // the real head (it may have changed)}console.log(toArray(removeValue(fromArray([6, 6, 1, 6, 2]), 6))); // [1, 2]console.log(toArray(removeValue(fromArray([7, 7]), 7)));          // []

Look at the loop: after a delete, it does not move prev. After you skip a node, the new prev.next might match too (for example, a row of 6s). So you check it again before you move forward.

Doubly linked lists

A doubly linked list gives every node a prev link (a link to the node before it). So you can walk in both directions, and you can remove a node in O(1) when you only have that node. The cost is one extra link in each node. Also, there are more links to keep correct on every change: four link updates to insert, instead of two. The back and forward history of a browser can be built in this way. The LRU cache design (a cache, which is a small store of recent results, that throws away the item used longest ago; it is built from a hash map, which is a lookup table, plus a doubly linked list) also needs this O(1) removal.

JavaScript
class DNode {  constructor(val) { this.val = val; this.prev = null; this.next = null; }}// If you have the node itself, removal is O(1): you do not need to walk to find the node before it.function removeNode(node) {  if (node.prev) node.prev.next = node.next;  if (node.next) node.next.prev = node.prev;  node.prev = node.next = null;             // cut it off completely}const a = new DNode(1), b = new DNode(2), c = new DNode(3);a.next = b; b.prev = a; b.next = c; c.prev = b;     // 1 <-> 2 <-> 3removeNode(b);console.log(a.next.val, c.prev.val);                // 3 1

Arrays vs linked lists

Dry run · The trade-off
OperationArrayLinked list
Access by indexO(1)O(n)
Insert / delete at the frontO(n) (everything moves over)O(1)
Insert / delete in the middle (given the node)O(n)O(1)
Search for a valueO(n)O(n)
Memory per itemjust the valuevalue + a link (more)
Cache friendlinessexcellent (items are side by side)poor (items are spread out)

Cache friendliness means how well the computer's fast CPU cache can load your data. Items that sit side by side are loaded together, so arrays are fast to scan. In everyday JavaScript, an array is almost always the better choice. We study linked lists to learn pointer skills and to understand structures built on them (queues, LRU caches, adjacency lists).

The three common mistakes

  • Losing the rest of the list: you change next before you save the old next node.
  • Forgetting null: cur.next.val crashes when cur.next is null. Check for null before you use .val or .next on a node.
  • Forgetting the head case: use a dummy node, or write separate code for the head.

Practice questions

Draw the list with arrows before you write code. Draw it again after each link change.

Q1Design a linked listMedium

Write a singly linked list (each node has only a next link) with get, addAtHead, addAtTail, addAtIndex and deleteAtIndex.

Example 1
Input: addAtHead(1), addAtTail(3), addAtIndex(1, 2), get(1)
Output: 2
Explanation: The list is 1 → 2 → 3.
Example 2
Input: …then deleteAtIndex(1), get(1)
Output: 3
Explanation: The list is 1 → 3.
Stuck? Open one hint at a time
Hint 1
Store head and size. Every other method can use the same walk to the node before the position.
Hint 2
A dummy node in front of the head removes the special case for index 0.
Hint 3
Check the limits first. get and delete need 0 ≤ index < size. Add needs 0 ≤ index ≤ size.
Show all 2 solutions with explanations
Approach 1Singly linked list with a dummy head
The idea
  1. get: walk index steps from the head.
  2. addAtIndex and deleteAtIndex: walk to the node before the position, then change the links.
  3. addAtHead and addAtTail are just special cases of addAtIndex.
JavaScript
class ListNode { constructor(val, next = null) { this.val = val; this.next = next; } }class MyLinkedList {  constructor() { this.head = null; this.size = 0; }  get(index) {    if (index < 0 || index >= this.size) return -1;    let cur = this.head;    for (let i = 0; i < index; i++) cur = cur.next;    return cur.val;  }  addAtHead(val) { this.addAtIndex(0, val); }  addAtTail(val) { this.addAtIndex(this.size, val); }  addAtIndex(index, val) {    if (index < 0 || index > this.size) return;    const dummy = new ListNode(0, this.head);    let prev = dummy;    for (let i = 0; i < index; i++) prev = prev.next;    prev.next = new ListNode(val, prev.next);    this.head = dummy.next;    this.size++;  }  deleteAtIndex(index) {    if (index < 0 || index >= this.size) return;    const dummy = new ListNode(0, this.head);    let prev = dummy;    for (let i = 0; i < index; i++) prev = prev.next;    prev.next = prev.next.next;    this.head = dummy.next;    this.size--;  }}const L = new MyLinkedList();L.addAtHead(1); L.addAtTail(3); L.addAtIndex(1, 2);console.log(L.get(1));     // 2L.deleteAtIndex(1);console.log(L.get(1));     // 3console.log(L.get(5));     // -1
How it works

In the worst case, every operation is O(n) because of the walk. Adding at the head can be O(1) if you write it as a special case.

Approach 2Keep a tail pointer too
The idea

A tail reference makes addAtTail O(1). The price is that you must keep it correct whenever the last node changes.

JavaScript
class ListNode { constructor(val, next = null) { this.val = val; this.next = next; } }class FastTailList {  constructor() { this.head = null; this.tail = null; this.size = 0; }  addAtHead(val) {    this.head = new ListNode(val, this.head);    if (this.tail === null) this.tail = this.head;       // the first node is also the last node    this.size++;  }  addAtTail(val) {    const node = new ListNode(val);    if (this.tail === null) this.head = this.tail = node;    else { this.tail.next = node; this.tail = node; }     // O(1): no walk needed    this.size++;  }  toArray() { const out = []; for (let c = this.head; c; c = c.next) out.push(c.val); return out; }}const L = new FastTailList();L.addAtTail(2); L.addAtTail(3); L.addAtHead(1);console.log(L.toArray()); // [1, 2, 3]
How it works

To delete the last node, you need the node before it so you can move tail back. So deleting at the tail is still O(n) in a singly linked list.

Which approach should you use?

Use the first one, because that is what the question tests. Mention the tail pointer as a way to make it faster. (LeetCode 707.)

Q2Remove linked list elementsEasy

Remove every node of a linked list that has the value val. Return the new head.

Example 1
Input: head = [1, 2, 6, 3, 4, 5, 6], val = 6
Output: [1, 2, 3, 4, 5]
Explanation: Both 6s are removed.
Example 2
Input: head = [7, 7, 7, 7], val = 7
Output: []
Explanation: Every node matches, including the head.
Example 3
Input: head = [], val = 1
Output: []
Explanation: Nothing to do.
Stuck? Open one hint at a time
Hint 1
You may need to remove the head, again and again. The dummy node is for this special case.
Hint 2
After you skip a node, do not move forward. The next node may match too.
Show all 3 solutions with explanations
Approach 1Dummy head
The idea

Walk with prev, starting at a dummy node. If prev.next matches, delete it. If not, move forward.

JavaScript
function removeElements(head, val) {  const dummy = new ListNode(0, head);  let prev = dummy;  while (prev.next !== null) {    if (prev.next.val === val) prev.next = prev.next.next;    else prev = prev.next;  }  return dummy.next;}console.log(toArray(removeElements(fromArray([1, 2, 6, 3, 4, 5, 6]), 6))); // [1, 2, 3, 4, 5]console.log(toArray(removeElements(fromArray([7, 7, 7, 7]), 7)));          // []console.log(toArray(removeElements(null, 1)));                             // []
How it works

O(n) time, O(1) space.

Approach 2Without a dummy: skip the head first
The idea

Move the head forward while it matches. Then handle the rest of the list with one pointer.

JavaScript
function removeElements(head, val) {  while (head !== null && head.val === val) head = head.next;    // the special case for the head  let cur = head;  while (cur !== null && cur.next !== null) {    if (cur.next.val === val) cur.next = cur.next.next;    else cur = cur.next;  }  return head;}console.log(toArray(removeElements(fromArray([1, 2, 6, 3, 4, 5, 6]), 6))); // [1, 2, 3, 4, 5]console.log(toArray(removeElements(fromArray([7, 7, 7, 7]), 7)));          // []
How it works

The cost is the same, but you must write the head case yourself. The dummy node saves you from writing this code.

Approach 3Recursion
The idea

First clean the rest of the list. Then decide if you keep this node.

JavaScript
function removeElements(head, val) {  if (head === null) return null;  head.next = removeElements(head.next, val);     // the rest of the list, after cleaning  return head.val === val ? head.next : head;     // drop this node if it matches}console.log(toArray(removeElements(fromArray([1, 2, 6, 3, 4, 5, 6]), 6))); // [1, 2, 3, 4, 5]
How it works

It looks neat, but it uses O(n) stack space. A very long list could cause a stack overflow (the call stack, the memory that keeps track of open function calls, runs out of room).

Which approach should you use?

Use the dummy-head loop. (LeetCode 203.)

Q3Delete node in a linked list (given only that node)Medium

You get only one node of a singly linked list (not the head), and it is not the tail. Delete it from the list.

Example 1
Input: head = [4, 5, 1, 9], node = 5
Output: [4, 1, 9]
Explanation: You are given the node with value 5, not the head.
Example 2
Input: head = [4, 5, 1, 9], node = 1
Output: [4, 5, 9]
Explanation: The node is never the tail.
Stuck? Open one hint at a time
Hint 1
You cannot reach the node before it, so you cannot change its link.
Hint 2
What if this node becomes its next node? Copy the next value into it, then skip the next node.
Show the solution with explanations
Approach 1Copy the next node over this one
The idea
  1. Copy node.next.val into node.val.
  2. Set node.next = node.next.next.
JavaScript
function deleteNode(node) {  node.val = node.next.val;  node.next = node.next.next;}const head = fromArray([4, 5, 1, 9]);const five = head.next;deleteNode(five);console.log(toArray(head)); // [4, 1, 9]
How it works

O(1) time and space. It does not delete the given node object. It copies the value of the next node and deletes that next node. The list looks exactly the same afterwards. It works only because the node is never the tail.

Which approach should you use?

This is a trick question, not a real algorithm. (LeetCode 237.)

Q4Insert into a sorted linked listEasy

Insert a value into a linked list that is sorted from small to big. The list must stay sorted. Return the head.

Example 1
Input: head = [1, 3, 5], val = 4
Output: [1, 3, 4, 5]
Explanation: 4 goes between 3 and 5.
Example 2
Input: head = [2, 3], val = 1
Output: [1, 2, 3]
Explanation: A new head.
Example 3
Input: head = [], val = 7
Output: [7]
Explanation: A one-node list.
Stuck? Open one hint at a time
Hint 1
Walk until the next node is not smaller than the value. Then prev is the node to insert after.
Hint 2
A dummy head makes inserting before the first node work the same as any other insert.
Show the solution with explanations
Approach 1Walk to the right place
The idea

Stop with prev on the last node that is smaller than val. Then connect the new node.

JavaScript
function insertSorted(head, val) {  const dummy = new ListNode(0, head);  let prev = dummy;  while (prev.next !== null && prev.next.val < val) prev = prev.next;  prev.next = new ListNode(val, prev.next);        // connect first (the new node points at the old next node)  return dummy.next;}console.log(toArray(insertSorted(fromArray([1, 3, 5]), 4))); // [1, 3, 4, 5]console.log(toArray(insertSorted(fromArray([2, 3]), 1)));    // [1, 2, 3]console.log(toArray(insertSorted(null, 7)));                 // [7]
How it works

O(n) time, O(1) space. This is the main step of insertion sort (sorting by putting each item in its place) on a list.

Which approach should you use?

This is a warm-up for merging two sorted lists in Lesson 37.

Q5Remove duplicates from a sorted listEasy

You get a sorted linked list. Delete the repeated values, so that each value appears only once.

Example 1
Input: head = [1, 1, 2]
Output: [1, 2]
Explanation: The extra 1 is removed.
Example 2
Input: head = [1, 1, 2, 3, 3]
Output: [1, 2, 3]
Explanation: Both extra copies are removed.
Stuck? Open one hint at a time
Hint 1
The list is sorted, so equal values sit next to each other.
Hint 2
Compare a node with its next node. If they are equal, skip the next node and do not move. If not, move forward.
Show the solution with explanations
Approach 1Compare with the next node
The idea

While cur.next exists, delete it if it is equal to cur.

JavaScript
function deleteDuplicates(head) {  let cur = head;  while (cur !== null && cur.next !== null) {    if (cur.val === cur.next.val) cur.next = cur.next.next;    else cur = cur.next;  }  return head;}console.log(toArray(deleteDuplicates(fromArray([1, 1, 2]))));       // [1, 2]console.log(toArray(deleteDuplicates(fromArray([1, 1, 2, 3, 3])))); // [1, 2, 3]console.log(toArray(deleteDuplicates(fromArray([2, 2, 2]))));       // [2]
How it works

O(n) time, O(1) space. You do not need a dummy, because the head is never removed. The first node of a group of equal values stays.

Which approach should you use?

(LeetCode 83.) The harder version (LeetCode 82) removes every value that appears more than once. That version needs a dummy head.

Q6Odd even linked listMedium

Put all the nodes at odd positions together, followed by the nodes at even positions. Keep the original order inside each group. Use only O(1) extra space.

Example 1
Input: head = [1, 2, 3, 4, 5]
Output: [1, 3, 5, 2, 4]
Explanation: The nodes at odd positions come first, then the nodes at even positions.
Example 2
Input: head = [2, 1, 3, 5, 6, 4, 7]
Output: [2, 3, 6, 7, 1, 5, 4]
Explanation: Positions 1, 3, 5, 7 then 2, 4, 6.
Stuck? Open one hint at a time
Hint 1
Change the links to build two chains: one with the odd-position nodes, and one with the even-position nodes.
Hint 2
Remember where the even chain starts, so you can join it to the end of the odd chain.
Hint 3
Use only O(1) extra space by reusing the nodes you already have.
Show all 2 solutions with explanations
Approach 1Copy values into arrays
The idea

Read the values into two arrays, join the two arrays, and build a new list.

JavaScript
function oddEvenList(head) {  const odd = [], even = [];  let i = 1;  for (let c = head; c !== null; c = c.next, i++) (i % 2 === 1 ? odd : even).push(c.val);  return fromArray([...odd, ...even]);}console.log(toArray(oddEvenList(fromArray([1, 2, 3, 4, 5])))); // [1, 3, 5, 2, 4]
How it works

O(n) time, but it uses O(n) extra space and makes new nodes. The question asks for O(1) space by changing the links.

Approach 2Two chains by changing links (O(1) space)
The idea
  1. Set odd = head and even = head.next, and remember evenHead.
  2. While the even node has a next node: set odd.next = even.next, move odd forward, set even.next = odd.next, then move even forward.
  3. At the end, set odd.next = evenHead.
JavaScript
function oddEvenList(head) {  if (head === null || head.next === null) return head;  let odd = head, even = head.next;  const evenHead = even;                    // where the even chain starts  while (even !== null && even.next !== null) {    odd.next = even.next;                   // the odd node jumps over the even node    odd = odd.next;    even.next = odd.next;                   // the even node jumps over the odd node    even = even.next;  }  odd.next = evenHead;                      // join the two chains  return head;}console.log(toArray(oddEvenList(fromArray([1, 2, 3, 4, 5]))));          // [1, 3, 5, 2, 4]console.log(toArray(oddEvenList(fromArray([2, 1, 3, 5, 6, 4, 7]))));    // [2, 3, 6, 7, 1, 5, 4]console.log(toArray(oddEvenList(fromArray([1, 2]))));                   // [1, 2]
How it works

O(n) time, O(1) space. The loop stops when even or even.next is null. This works for lists with an odd length and for lists with an even length.

Which approach should you use?

Use the version that changes the links. It tests whether you can keep track of several pointers at once. (LeetCode 328.)

Q7Convert binary number in a linked list to integerEasy

A linked list holds the bits of a binary number (a number written with only 0 and 1). The first bit is the biggest one. Return its normal decimal value.

Example 1
Input: head = [1, 0, 1]
Output: 5
Explanation: Binary 101 is 5.
Example 2
Input: head = [0]
Output: 0
Explanation: Zero.
Example 3
Input: head = [1, 1, 0, 0]
Output: 12
Explanation: Binary 1100.
Stuck? Open one hint at a time
Hint 1
Read from left to right. Each new bit (a 0 or a 1) doubles the number so far and then adds itself.
Hint 2
Walk through the list once and keep a running number.
Show all 2 solutions with explanations
Approach 1Doubling as you traverse
The idea

Start at 0. For each node, do n = n * 2 + node.val.

JavaScript
function getDecimalValue(head) {  let n = 0;  for (let cur = head; cur !== null; cur = cur.next) n = n * 2 + cur.val;  return n;}console.log(getDecimalValue(fromArray([1, 0, 1])));    // 5console.log(getDecimalValue(fromArray([0])));          // 0console.log(getDecimalValue(fromArray([1, 1, 0, 0]))); // 12
How it works

O(n) time, O(1) space. Going from left to right does not need the length of the list. A linked list does not tell you its length for free.

Approach 2Bit shift
The idea

This is the same idea, written with bit operations. Shift the bits left by one place, then add the new bit with OR.

JavaScript
function getDecimalValue(head) {  let n = 0;  for (let cur = head; cur !== null; cur = cur.next) n = (n << 1) | cur.val;  return n;}console.log(getDecimalValue(fromArray([1, 0, 1, 1]))); // 11
How it works

The cost is the same. n << 1 means the same as n * 2. (JavaScript bit operations work on 32-bit numbers, which is enough here because the list has at most 30 bits. Part 15 covers bit tricks.)

Which approach should you use?

(LeetCode 1290.)

Dry run · Link changes for the common edits
EditLink changesWatch out for
insert after pnew.next = p.next; p.next = newdo not swap these two lines
delete after pp.next = p.next.nextp.next may be null
delete the headhead = head.nextuse a dummy head to avoid this case
delete a node given only itselfcopy next.val, skip nextfails on the tail
swap two adjacent nodesthree link changessave a reference before you overwrite a link

Make it stick

Make it stick — close this page, then
  1. Write the ListNode class and the loop that visits every node.
  2. Explain why, when you insert after a node, you must set the next of the new node before you change the old link.
  3. Explain what problem the dummy head solves, and write the loop that deletes all matching nodes using it.
  4. Name two operations where a linked list is better than an array, and two where it is worse.

Revisit schedule: solve two of the questions again tomorrow, again in 3 days, and once more in a week — without looking at the answers. Each time it will feel easier. That is the memory forming.

What's next

Lesson 36 uses these building blocks for three classic list techniques: reversing a list, finding the middle with a slow pointer and a fast pointer, and finding a cycle (a loop in the list).

Practise on LeetCode

Real interview problems that use exactly what this lesson teaches. Solve them in JavaScript after the practice questions above.

Published by Jainik · Last updated · How these lessons are made

Keep learning

Part of the study guide

Related series