Bird
Raised Fist0
Interview Prepfast-slow-pointersmediumAmazonFacebookGoogle

Find the Duplicate Number (Floyd on Array)

Choose your preparation mode4 modes available
▶
Steps
setup

Initialize slow and fast pointers

Both slow and fast pointers start at the first element of the array, which is the value at index 0.

💡 Starting both pointers at the same position sets up the initial state for cycle detection.
Line:slow = nums[0] fast = nums[0]
💡 Both pointers begin at the same index, ready to traverse the array to detect a cycle.
📊
Find the Duplicate Number (Floyd on Array) - Watch the Algorithm Execute, Step by Step
Watching the pointers move step-by-step reveals how the cycle detection algorithm works internally, making the abstract concept concrete and easier to grasp.
Step 1/11
·Active fill★Answer cell
setup
1
0
fast
3
1
4
2
2
3
2
4
move_right
1
0
fast
3
1
4
2
slow
2
3
2
4
move_right
1
0
3
1
fast
4
2
slow
2
3
2
4
compare
1
0
3
1
fast
4
2
2
3
2
4
compare
1
0
3
1
fast
4
2
2
3
2
4
setup
1
0
slow
3
1
fast
4
2
2
3
2
4
move_right
1
0
3
1
fast
4
2
slow
2
3
2
4
move_right
1
0
3
1
4
2
slow
2
3
fast
2
4
move_right
1
0
3
1
slow
4
2
2
3
fast
2
4
compare
1
0
3
1
fast
4
2
2
3
2
4
Result: 2
record
1
0
3
1
fast
4
2
2
3
2
4
Result: 2

Key Takeaways

✓ The duplicate number creates a cycle in the array when viewed as pointers to indices.

This insight is hard to see from code alone because the array values are used as indices, which is a non-obvious transformation.

✓ The fast pointer moves twice as fast as the slow pointer to detect the cycle efficiently.

Visualizing the two pointers moving at different speeds clarifies why the cycle detection works and how they eventually meet.

✓ Resetting the slow pointer to the start and moving both pointers one step at a time finds the cycle entry, which is the duplicate number.

This step is subtle in code but clear in visualization, showing how the meeting point after reset reveals the duplicate.

Practice

(1/5)
1. You are given a circular array where each element represents a jump length and direction (positive for forward, negative for backward). The task is to determine if there exists a cycle in the array such that the cycle is longer than one element and all jumps are in the same direction. Which algorithmic approach guarantees an optimal solution for this problem?
easy
A. Greedy approach that tries to jump as far as possible from each index until a cycle is detected or no progress is made.
B. Dynamic programming to store reachable indices and detect cycles by memoization.
C. Brute force simulation from each index checking all possible cycles exhaustively.
D. Fast and slow pointer cycle detection that checks direction consistency and avoids single-element loops.

Solution

  1. Step 1: Understand problem constraints

    The problem requires detecting cycles in a circular array with direction consistency and cycle length > 1.
  2. Step 2: Identify suitable algorithm

    Fast and slow pointer cycle detection efficiently finds cycles in O(n) time while checking direction and cycle length constraints.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Fast and slow pointers detect cycles without exhaustive search [OK]
Hint: Cycle detection with direction check -> fast-slow pointers [OK]
Common Mistakes:
  • Assuming greedy jumps always find cycles
  • Using DP which is inefficient here
  • Brute force is correct but not optimal
2. Consider the following Python code that removes the 2nd node from the end of the list 1 -> 2 -> 3 -> 4 -> 5. What is the printed output after execution?
easy
A. 1 2 4 5
B. 1 2 3 5
C. 1 3 4 5
D. 2 3 4 5

Solution

  1. Step 1: Trace recursion from end

    Recursion returns indices from the end: node 5 returns 1, node 4 returns 2, node 3 returns 3, etc.
  2. Step 2: Identify node to remove

    When idx == n+1 = 3, node 3's next pointer skips node 4, effectively removing node 4.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Output matches list with 4 removed: 1 2 3 5 [OK]
Hint: Recursion index counts from end; remove node at idx = n+1 [OK]
Common Mistakes:
  • Removing the node at idx == n instead of n+1
  • Off-by-one errors in recursion index
  • Confusing which node to skip
3. You are given a singly linked list and an integer k. The task is to split the list into k consecutive parts such that the sizes of the parts differ by at most one, and the earlier parts are larger if sizes differ. Which algorithmic approach best guarantees an optimal solution with minimal passes over the list?
easy
A. Greedy approach that assigns nodes to parts until each part reaches an average size, without pre-counting total nodes.
B. Dynamic programming to find the optimal partition minimizing size differences between parts.
C. Repeatedly remove nodes from the front and append to parts until all nodes are distributed, without precomputing sizes.
D. Calculate total nodes first, then split the list in one pass using precomputed part sizes and carefully breaking links.

Solution

  1. Step 1: Understand problem constraints

    The problem requires splitting into k parts with sizes differing by at most one, favoring earlier parts to be larger.
  2. Step 2: Identify approach that meets constraints efficiently

    Calculating total nodes first allows precise part sizes and a single pass to split, ensuring correctness and efficiency.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Precomputing sizes avoids guesswork and multiple passes [OK]
Hint: Precompute total nodes to split correctly in one pass [OK]
Common Mistakes:
  • Assuming greedy without counting nodes works
  • Trying DP unnecessarily
  • Splitting without breaking links properly
4. Examine the following buggy code snippet for deleting N nodes after skipping M nodes in a linked list. Identify the line that contains the subtle bug causing potential runtime errors or incorrect output.
def delete_n_after_m_buggy(head, M, N):
    current = head
    while current:
        for _ in range(1, M):
            current = current.next
        if current is null:
            break
        temp = current.next
        for _ in range(N):
            if temp is null:
                break
            temp = temp.next
        current.next = temp
        current = temp
    return head
medium
A. Line 13: current.next = temp
B. Line 6: if current is null: break
C. Line 10: for _ in range(N): if temp is null: break
D. Line 4: for _ in range(1, M): current = current.next

Solution

  1. Step 1: Analyze pointer movement in skipping loop

    The loop moves current forward M-1 times without checking if current is null before moving, risking NoneType attribute errors.
  2. Step 2: Identify missing null check

    Without checking current before current = current.next, code may dereference null, causing runtime error.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Adding null check inside skipping loop prevents runtime errors [OK]
Hint: Always check for null before moving pointers in loops [OK]
Common Mistakes:
  • Missing null checks before pointer moves
  • Incorrectly updating next pointers causing cycles
  • Assuming list length always sufficient
5. Suppose the problem is modified so that after deleting N nodes, the deleted nodes can be reinserted later in the list (i.e., nodes can be reused). Which of the following changes to the algorithm is necessary to correctly handle this variant?
hard
A. Use a recursive approach to backtrack and reinsert deleted nodes at correct positions.
B. Maintain a separate data structure to store deleted nodes and reinsert them after traversal.
C. Modify the iterative approach to skip M nodes, delete N nodes, and immediately reattach deleted nodes after the next M nodes.
D. No change needed; the original iterative approach already supports node reuse.

Solution

  1. Step 1: Understand node reuse requirement

    Deleted nodes must be preserved and reinserted later, so they cannot be simply discarded by pointer reassignment.
  2. Step 2: Evaluate algorithm changes

    The original approach loses references to deleted nodes. To reuse, store deleted nodes externally and reinsert after traversal or at correct positions.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Maintaining deleted nodes separately enables controlled reinsertion [OK]
Hint: Reusing nodes requires storing them, not discarding pointers [OK]
Common Mistakes:
  • Assuming original approach supports reuse
  • Trying to reattach nodes immediately without storage
  • Using recursion unnecessarily
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard