Compare two linked lists
Walk both lists in lockstep with two pointers; at each step compare values; return false on first mismatch or unequal length. O(min(m,n)) time, O(1) space. Variants: compare for value-equality regardless of node identity; deep-compare lists of lists (recursion); compare cyclic lists (detect cycle with Floyd's, then compare lengths + values).
Comparing two singly-linked lists is the linked-list version of array equality — a clean two-pointer walk.
Basic implementation
function equal(a, b) {
while (a && b) {
if (a.value !== b.value) return false;
a = a.next;
b = b.next;
}
return a === null && b === null; // both ended together
}Time O(min(m, n) + 1), Space O(1).
The a === null && b === null check matters — if one list is shorter, the loop ends but they're not equal.
Deep equality
If values can be objects or nested structures:
function deepEqual(a, b) {
while (a && b) {
if (!deepEq(a.value, b.value)) return false;
a = a.next; b = b.next;
}
return a === null && b === null;
}
function deepEq(x, y) {
if (x === y) return true;
if (typeof x !== "object" || x === null || typeof y !== "object" || y === null) return false;
const ka = Object.keys(x), kb = Object.keys(y);
if (ka.length !== kb.length) return false;
return ka.every((k) => deepEq(x[k], y[k]));
}Cyclic lists
If the lists may have cycles, the walk-to-null approach is an infinite loop. Detect cycles with Floyd's for each list first, then compare lengths (cycle + tail) and walk with bounded steps:
function detectCycle(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next; fast = fast.next.next;
if (slow === fast) return slow; // meeting point
}
return null;
}For cyclic comparison, compute (tail length, cycle length, cycle structure) for both and compare structurally.
By-reference vs by-value
function sameList(a, b) { return a === b; } // reference equality onlyCommon interview clarification: "compare the lists" almost always means value equality. Ask.
Compare two reversed lists (palindrome variant)
A related problem: "is this list a palindrome?" — find midpoint with fast/slow, reverse second half, compare halves, restore.
Edge cases
- Both empty → equal.
- One empty, one non-empty → not equal.
- Same length, different values → not equal.
- Same values, different lengths → not equal.
- Cycle on one only → not equal (the non-cyclic ends; cyclic doesn't).
Linked list vs Array
If the problem allows, copying both lists to arrays makes comparison trivial — but uses O(n) space. The pointer walk is the canonical answer.
Interview framing
"Two-pointer walk: advance both lists in lockstep; on the first value mismatch, return false; when the loop exits, equal iff both pointers are null (same length). O(min(m, n)) time, O(1) space. Watch for the length-mismatch edge case — that's the bug interviewers look for. If values can be objects, swap !== for a deep-equal. If lists can be cyclic, detect cycles first (Floyd's) and compare structurally — naive walk-to-null would loop forever."
Follow-up questions
- •What's the bug in `while (a && b) { ... } return true;` (without the final null check)?
- •How would you handle deep value equality (object values)?
- •How do you compare two cyclic linked lists?
- •Could you do it recursively, and what's the cost?
Common mistakes
- •Returning true at the end of the while loop without checking both are null.
- •Crashing on null values inside the list when using shallow `!==`.
- •Infinite loop on cyclic lists.
- •Confusing reference equality with value equality.
Performance considerations
- •O(min(m,n)) time, O(1) space iteratively. Recursive version is O(n) space (stack). Deep equality multiplies cost by value size.
Edge cases
- •Both empty.
- •Different lengths with shared prefix.
- •Cycles in one or both.
- •Object values requiring deep equality.
Real-world examples
- •Equality checks in immutable list libraries (Immutable.js, Mori).
- •Test framework deep equals on list-shaped data.