Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Two Pointers & Sliding Window

Builds on Converging Pointers, which you haven’t finished yet.

P3MediumBuilt on Converging Pointers~4 min speedrun

In-Place Array Rotation (Rotate Array)

Built on
Converging PointersReverse the whole range, then reverse each of the two parts, so the items rotate in place.

Rotate Array (LC 189)(opens leetcode.com)

You get an integer array nums and a non-negative integer k. Shift every item k places to the right; an item pushed past the last index wraps around to the front. Change nums itself: the function returns nothing. k can be larger than the length of nums.

Follow-up: LeetCode asks for as many different solutions as you can find, and whether one of them can work in place with O(1)O(1)O(1) extra space.

Example 1
Input:nums = [1,2,3,4,5,6,7], k = 3
Output:[5,6,7,1,2,3,4]
After 1 move: [7,1,2,3,4,5,6]; after 2: [6,7,1,2,3,4,5]; after 3: [5,6,7,1,2,3,4].

Three-Reversals Algorithm

array ➔ reverse whole, then both parts ➔ rotated array
Spot it when: You must rotate or cyclically shift an array (or a flattened grid) in place, without an extra copy.
Three-Reversals Algorithm
Python
1def rotate_right(nums: list[int], k: int) -> None:
2 """Rotate Array: shift right by k in place with three reversals.
3 Time: O(N) | Space: O(1)
4 """
5 def reverse(lo: int, hi: int) -> None:
6 while lo < hi: # Swap inward from both ends
7 nums[lo], nums[hi] = nums[hi], nums[lo]
8 lo, hi = lo + 1, hi - 1
9 
10 n = len(nums)
11 if n == 0:
12 return
13 k %= n # Rotating by n is a no-op; k >= n wraps around
14 reverse(0, n - 1) # [A | B] -> [B^R | A^R]
15 reverse(0, k - 1) # B^R -> B (the last k items, now in front)
16 reverse(k, n - 1) # A^R -> A
Invariant: After reverse(0, n - 1) the last k items are in front, each block mirrored; the two block reversals only fix the order inside a block.

Rotating right by k moves the last k items (B) in front of the rest (A), and reversing a sequence also reverses the order of its blocks: reverse(A B) = B^R A^R. So reverse(0, n - 1) puts the blocks in the right order, then reverse(0, k - 1) and reverse(k, n - 1) straighten each block, all in place. Do k %= n first, because k can be larger than the array.

See it run

Rotate Array, nums = [1, 2, 3, 4, 5, 6, 7], k = 10

call start · k 10

call k %= n · k 3

call reverse(0, 6) · nums [7, 2, 3, 4, 5, 6, 1]

call reverse(0, 6) · nums [7, 6, 3, 4, 5, 2, 1]

call reverse(0, 6) · nums [7, 6, 5, 4, 3, 2, 1]

call reverse(0, 2) · nums [5, 6, 7, 4, 3, 2, 1]

call reverse(3, 6) · nums [5, 6, 7, 1, 3, 2, 4]

call reverse(3, 6) · nums [5, 6, 7, 1, 2, 3, 4]

call done · answer 5, 6, 7, 1, 2, 3, 4

The top row is nums. The bottom row names the block each value came from, so you can follow the blocks while they move.

  • lo the left end of the range being reversed
  • hi the right end of the range being reversed

nums has 7 items and k = 10. After k is reduced below 7, block A is the first n - k items and block B is the last k items.

k %= n makes k = 3. Without it, reverse(0, 9) would read past the end of nums.

reverse(0, 6): swap nums[0] and nums[6]. lo is now 1 and hi is now 5.

reverse(0, 6): swap nums[1] and nums[5]. lo is now 2 and hi is now 4.

reverse(0, 6): swap nums[2] and nums[4]. The last k items are now in front, each block in mirrored order.

reverse(0, 2): swap nums[0] and nums[2]. Block B is back in its own order.

reverse(3, 6): swap nums[3] and nums[6]. lo is now 4 and hi is now 5.

reverse(3, 6): swap nums[4] and nums[5]. Block A is back in its own order.

The three reversals are finished. nums is [5, 6, 7, 1, 2, 3, 4].

nums
01
12
23
34
45
56
67
block
1 / 9
Gotchas1 of 4

Gotcha 1 of 4

  1. 1
    The trap

    Gotcha 1: Leaving out k %= n

    Mistake
    reverse(0, n - 1)
    Fix
    k %= n
    reverse(0, n - 1)

    What happens: k can be larger than n, the length of nums. Rotating by n gives the same array, so only k % n steps matter. Without k %= n, reverse(0, k - 1) reads past the end of nums. Python raises an IndexError.

    Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 10
    Mistake: raises IndexErrorFix: returns [5, 6, 7, 1, 2, 3, 4]
  2. 2

    Gotcha 2: Rebinding nums to a new list

    Mistake
    nums = nums[-k:] + nums[:-k]
    Fix
    reverse(0, n - 1)
    reverse(0, k - 1)
    reverse(k, n - 1)

    What happens: nums[-k:] + nums[:-k] builds a new list. The assignment points the local name nums at that new list. The caller's list never changes, and LeetCode checks the caller's list.

    Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 3
    Mistake: returns [1, 2, 3, 4, 5, 6, 7]Fix: returns [5, 6, 7, 1, 2, 3, 4]
  3. 3

    Gotcha 3: Reversing the two parts before the whole array

    Mistake
    reverse(0, k - 1)
    reverse(k, n - 1)
    reverse(0, n - 1)
    Fix
    reverse(0, n - 1)
    reverse(0, k - 1)
    reverse(k, n - 1)

    What happens: Reversing the whole array first moves the last k items to the front. Then each part is reversed back into its own order. Reversing the parts first and the whole array last rotates to the left by k instead.

    Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 3
    Mistake: returns [4, 5, 6, 7, 1, 2, 3]Fix: returns [5, 6, 7, 1, 2, 3, 4]
  4. 4

    Gotcha 4: Rotating each row separately in Shift 2D Grid (LC 1260)

    What happens: Shift 2D Grid (LC 1260) moves the last value of each row to the start of the next row. Rotating each row on its own keeps every value in its own row. Read the grid as one list, row by row, and rotate that list. Index i of the list is the cell (i // cols, i % cols).

Not this one if…
  • A square grid must turn by 90 degrees, as in Rotate Image (LC 48): rows become columns, which no shift of the flattened list can do. See Spiral Matrix
  • The data is a linked list, as in Reverse Linked List (LC 206), so there are no indices to swap from both ends. See Linked List Reversal

Deep Dive

Why three reversals rotate the array

Rotating right by k moves the last k items to the front. Call the first n - k items block A and the last k items block B. The array is A B, and the rotated array is B A.

Reversing a whole sequence reads it from the end. So reverse(A B) gives the items of B first, in reversed order, then the items of A, in reversed order. Write this as B^R A^R. The blocks are now in the right places, but each block is mirrored.

Two more reversals fix the mirror. reverse(0, k - 1) turns B^R back into B. reverse(k, n - 1) turns A^R back into A. The array is B A.

Trace nums = [1, 2, 3, 4, 5, 6, 7] with k = 10. After k %= n, k = 3, so A = [1, 2, 3, 4] and B = [5, 6, 7]. reverse(0, 6) gives [7, 6, 5, 4, 3, 2, 1]. reverse(0, 2) gives [5, 6, 7, 4, 3, 2, 1]. reverse(3, 6) gives [5, 6, 7, 1, 2, 3, 4]. Rotate Array (LC 189) gives the same answer for k = 3, the value in its first example.

Each reverse is the two-pointer swap from the Squeeze Ends card: lo and hi move inward and swap until they meet. This card adds only the choice of the three ranges.

Why k %= n comes first

Rotating by n moves every item all the way around, so the array is the same as before. Rotating by k and rotating by k % n give the same array.

Without k %= n, a k larger than n breaks the code. For n = 7 and k = 10, reverse(0, k - 1) is reverse(0, 9), and nums[9] raises an IndexError.

The check if n == 0: return comes before k %= n. In Python, k %= 0 raises a ZeroDivisionError.

When k % n is 0, reverse(0, -1) has lo >= hi at once and does nothing. The other two reversals then undo each other, because the second reverses the whole array again. The array stays the same.

The order of the reversals and the in-place rule

The whole array must be reversed first. If the two parts are reversed first and the whole array last, the result is a rotation to the left by k. For nums = [1, 2, 3, 4, 5, 6, 7] and k = 3, the parts give [3, 2, 1, 7, 6, 5, 4], and the whole reversal gives [4, 5, 6, 7, 1, 2, 3]. This is the correct order for a left rotation. A right rotation by k is the same as a left rotation by n - k.

The function must change the list that the caller passed. The line nums = nums[-k:] + nums[:-k] builds a new list and points the local name nums at it. The caller's list does not change, and the new list also uses O(N) extra space. The swaps nums[lo], nums[hi] = nums[hi], nums[lo] change the original list.

Shift 2D Grid (LC 1260) moves the last value of each row to the start of the next row. Read the grid as one list, row by row. Index i of that list is the cell (i // cols, i % cols). Rotate the list, then write it back. Rotating each row on its own is wrong.

Cost and related problems

The three reversals swap about n / 2 + k / 2 + (n - k) / 2 pairs, which is about n swaps. So the time is O(N) and the space is O(1). For the board, the reversals use 3, 1 and 2 swaps, which is 6 swaps for 7 items.

A copy into a new list is O(N) time and O(N) space. Moving each item to its final place, one cycle at a time, is O(N) time and O(1) space, but it needs the gcd of n and k to count the cycles. The three reversals are shorter to write.

Reverse Words in a String (LC 151) uses the same idea in another shape: reverse the whole text, then reverse each word. The in-place version is Reverse Words in a String II (LC 186).

Rotate List (LC 61) rotates a linked list. A linked list has no indices to swap from both ends, so find the new tail and change the links instead. Rotate Image (LC 48) turns a square grid by 90 degrees. Rows become columns, which no shift of one flat list can do.

60-Second Pitch

"Rotating right by k turns A B into B A, where B is the last k items. Reversing a sequence also reverses the order of its blocks, so reverse(A B) is B^R A^R. I first do k %= n, because k can be larger than the array. Then I reverse the whole array. Then I reverse the first k items, and then the rest. Each block is back in its own order, and the blocks are in the right places. Each reversal swaps pairs from both ends in place. The time is O(N) and the space is O(1)."

Common Questions

What is the time and space complexity?

O(N) time, because the three reversals make about n swaps in total. O(1) extra space, because the swaps happen inside nums.

Why does reversing the whole array first work?

Reversing A B gives B^R A^R. The last k items are now in front, and only the order inside each block is wrong. The two reversals of the blocks fix that.

How do you rotate to the left instead?

Rotate right by n - k, or reverse the two parts first and the whole array last. For [1, 2, 3, 4, 5, 6, 7] and k = 3, the left rotation is [4, 5, 6, 7, 1, 2, 3].

Why does the code need k %= n?

Rotating by n gives the same array, so only k % n matters. If k is larger than n, reverse(0, k - 1) reads past the end of nums and raises an IndexError.

Is there another O(1) space method?

Yes. You can move each item straight to its final index, (i + k) % n, following each cycle until it closes. The number of cycles is the gcd of n and k. It is also O(N) time, but it is harder to write without a mistake.

SWE Reasoning

Spot it

  • The statement asks to rotate or cyclically shift an array by k positions, in place (Rotate Array (LC 189)).
  • A grid can be read as one flat list, and the shift moves values across row ends (Shift 2D Grid (LC 1260)).
  • Extra space must be O(1), so a second array is not allowed.
  • A text must change the order of its blocks and keep the order inside each block (Reverse Words in a String (LC 151)).

The invariant, step by step

  1. The array is A B, where B is the last k items. The goal is B A.
  2. reverse(0, n - 1) gives B^R A^R. The last k items are in front, and each block is mirrored.
  3. reverse(0, k - 1) reverses the first k items, so B^R becomes B.
  4. reverse(k, n - 1) reverses the rest, so A^R becomes A.
  5. The array is B A. Each reverse swaps pairs from both ends, so it changes nums in place and takes O(1) extra space.

Traps

  • No k %= n. For k = 10 and 7 items, reverse(0, 9) raises an IndexError.
  • Reversing the parts first. The result is a left rotation: [4, 5, 6, 7, 1, 2, 3] instead of [5, 6, 7, 1, 2, 3, 4].
  • nums = nums[-k:] + nums[:-k]. The caller's list does not change, and the new list costs O(N) space.
  • No check for an empty array. k %= n with n = 0 raises a ZeroDivisionError.
  • Rotating each row of a grid. Values must move from the end of one row to the start of the next. Rotate the flattened list.

Practice

Canonical LeetCode:Rotate Array (LC 189)Shift 2D Grid (LC 1260)Reverse Words in a String (LC 151)Rotate List (LC 61)
Theory Context•Two Pointers & Sliding Window

Builds on Converging Pointers, which you haven’t finished yet.

P3MediumBuilt on Converging Pointers~4 min speedrun

In-Place Array Rotation (Rotate Array)

Built on
Converging PointersReverse the whole range, then reverse each of the two parts, so the items rotate in place.

Rotate Array (LC 189)(opens leetcode.com)

You get an integer array nums and a non-negative integer k. Shift every item k places to the right; an item pushed past the last index wraps around to the front. Change nums itself: the function returns nothing. k can be larger than the length of nums.

Follow-up: LeetCode asks for as many different solutions as you can find, and whether one of them can work in place with O(1)O(1)O(1) extra space.

Example 1
Input:nums = [1,2,3,4,5,6,7], k = 3
Output:[5,6,7,1,2,3,4]
After 1 move: [7,1,2,3,4,5,6]; after 2: [6,7,1,2,3,4,5]; after 3: [5,6,7,1,2,3,4].

Three-Reversals Algorithm

array ➔ reverse whole, then both parts ➔ rotated array
Spot it when: You must rotate or cyclically shift an array (or a flattened grid) in place, without an extra copy.
Three-Reversals Algorithm
Python
1def rotate_right(nums: list[int], k: int) -> None:
2 """Rotate Array: shift right by k in place with three reversals.
3 Time: O(N) | Space: O(1)
4 """
5 def reverse(lo: int, hi: int) -> None:
6 while lo < hi: # Swap inward from both ends
7 nums[lo], nums[hi] = nums[hi], nums[lo]
8 lo, hi = lo + 1, hi - 1
9 
10 n = len(nums)
11 if n == 0:
12 return
13 k %= n # Rotating by n is a no-op; k >= n wraps around
14 reverse(0, n - 1) # [A | B] -> [B^R | A^R]
15 reverse(0, k - 1) # B^R -> B (the last k items, now in front)
16 reverse(k, n - 1) # A^R -> A
Invariant: After reverse(0, n - 1) the last k items are in front, each block mirrored; the two block reversals only fix the order inside a block.

Rotating right by k moves the last k items (B) in front of the rest (A), and reversing a sequence also reverses the order of its blocks: reverse(A B) = B^R A^R. So reverse(0, n - 1) puts the blocks in the right order, then reverse(0, k - 1) and reverse(k, n - 1) straighten each block, all in place. Do k %= n first, because k can be larger than the array.

See it run

Rotate Array, nums = [1, 2, 3, 4, 5, 6, 7], k = 10

call start · k 10

call k %= n · k 3

call reverse(0, 6) · nums [7, 2, 3, 4, 5, 6, 1]

call reverse(0, 6) · nums [7, 6, 3, 4, 5, 2, 1]

call reverse(0, 6) · nums [7, 6, 5, 4, 3, 2, 1]

call reverse(0, 2) · nums [5, 6, 7, 4, 3, 2, 1]

call reverse(3, 6) · nums [5, 6, 7, 1, 3, 2, 4]

call reverse(3, 6) · nums [5, 6, 7, 1, 2, 3, 4]

call done · answer 5, 6, 7, 1, 2, 3, 4

The top row is nums. The bottom row names the block each value came from, so you can follow the blocks while they move.

  • lo the left end of the range being reversed
  • hi the right end of the range being reversed

nums has 7 items and k = 10. After k is reduced below 7, block A is the first n - k items and block B is the last k items.

k %= n makes k = 3. Without it, reverse(0, 9) would read past the end of nums.

reverse(0, 6): swap nums[0] and nums[6]. lo is now 1 and hi is now 5.

reverse(0, 6): swap nums[1] and nums[5]. lo is now 2 and hi is now 4.

reverse(0, 6): swap nums[2] and nums[4]. The last k items are now in front, each block in mirrored order.

reverse(0, 2): swap nums[0] and nums[2]. Block B is back in its own order.

reverse(3, 6): swap nums[3] and nums[6]. lo is now 4 and hi is now 5.

reverse(3, 6): swap nums[4] and nums[5]. Block A is back in its own order.

The three reversals are finished. nums is [5, 6, 7, 1, 2, 3, 4].

nums
01
12
23
34
45
56
67
block
1 / 9
Gotchas1 of 4

Gotcha 1 of 4

  1. 1
    The trap

    Gotcha 1: Leaving out k %= n

    Mistake
    reverse(0, n - 1)
    Fix
    k %= n
    reverse(0, n - 1)

    What happens: k can be larger than n, the length of nums. Rotating by n gives the same array, so only k % n steps matter. Without k %= n, reverse(0, k - 1) reads past the end of nums. Python raises an IndexError.

    Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 10
    Mistake: raises IndexErrorFix: returns [5, 6, 7, 1, 2, 3, 4]
  2. 2

    Gotcha 2: Rebinding nums to a new list

    Mistake
    nums = nums[-k:] + nums[:-k]
    Fix
    reverse(0, n - 1)
    reverse(0, k - 1)
    reverse(k, n - 1)

    What happens: nums[-k:] + nums[:-k] builds a new list. The assignment points the local name nums at that new list. The caller's list never changes, and LeetCode checks the caller's list.

    Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 3
    Mistake: returns [1, 2, 3, 4, 5, 6, 7]Fix: returns [5, 6, 7, 1, 2, 3, 4]
  3. 3

    Gotcha 3: Reversing the two parts before the whole array

    Mistake
    reverse(0, k - 1)
    reverse(k, n - 1)
    reverse(0, n - 1)
    Fix
    reverse(0, n - 1)
    reverse(0, k - 1)
    reverse(k, n - 1)

    What happens: Reversing the whole array first moves the last k items to the front. Then each part is reversed back into its own order. Reversing the parts first and the whole array last rotates to the left by k instead.

    Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 3
    Mistake: returns [4, 5, 6, 7, 1, 2, 3]Fix: returns [5, 6, 7, 1, 2, 3, 4]
  4. 4

    Gotcha 4: Rotating each row separately in Shift 2D Grid (LC 1260)

    What happens: Shift 2D Grid (LC 1260) moves the last value of each row to the start of the next row. Rotating each row on its own keeps every value in its own row. Read the grid as one list, row by row, and rotate that list. Index i of the list is the cell (i // cols, i % cols).

Not this one if…
  • A square grid must turn by 90 degrees, as in Rotate Image (LC 48): rows become columns, which no shift of the flattened list can do. See Spiral Matrix
  • The data is a linked list, as in Reverse Linked List (LC 206), so there are no indices to swap from both ends. See Linked List Reversal

Deep Dive

Why three reversals rotate the array

Rotating right by k moves the last k items to the front. Call the first n - k items block A and the last k items block B. The array is A B, and the rotated array is B A.

Reversing a whole sequence reads it from the end. So reverse(A B) gives the items of B first, in reversed order, then the items of A, in reversed order. Write this as B^R A^R. The blocks are now in the right places, but each block is mirrored.

Two more reversals fix the mirror. reverse(0, k - 1) turns B^R back into B. reverse(k, n - 1) turns A^R back into A. The array is B A.

Trace nums = [1, 2, 3, 4, 5, 6, 7] with k = 10. After k %= n, k = 3, so A = [1, 2, 3, 4] and B = [5, 6, 7]. reverse(0, 6) gives [7, 6, 5, 4, 3, 2, 1]. reverse(0, 2) gives [5, 6, 7, 4, 3, 2, 1]. reverse(3, 6) gives [5, 6, 7, 1, 2, 3, 4]. Rotate Array (LC 189) gives the same answer for k = 3, the value in its first example.

Each reverse is the two-pointer swap from the Squeeze Ends card: lo and hi move inward and swap until they meet. This card adds only the choice of the three ranges.

Why k %= n comes first

Rotating by n moves every item all the way around, so the array is the same as before. Rotating by k and rotating by k % n give the same array.

Without k %= n, a k larger than n breaks the code. For n = 7 and k = 10, reverse(0, k - 1) is reverse(0, 9), and nums[9] raises an IndexError.

The check if n == 0: return comes before k %= n. In Python, k %= 0 raises a ZeroDivisionError.

When k % n is 0, reverse(0, -1) has lo >= hi at once and does nothing. The other two reversals then undo each other, because the second reverses the whole array again. The array stays the same.

The order of the reversals and the in-place rule

The whole array must be reversed first. If the two parts are reversed first and the whole array last, the result is a rotation to the left by k. For nums = [1, 2, 3, 4, 5, 6, 7] and k = 3, the parts give [3, 2, 1, 7, 6, 5, 4], and the whole reversal gives [4, 5, 6, 7, 1, 2, 3]. This is the correct order for a left rotation. A right rotation by k is the same as a left rotation by n - k.

The function must change the list that the caller passed. The line nums = nums[-k:] + nums[:-k] builds a new list and points the local name nums at it. The caller's list does not change, and the new list also uses O(N) extra space. The swaps nums[lo], nums[hi] = nums[hi], nums[lo] change the original list.

Shift 2D Grid (LC 1260) moves the last value of each row to the start of the next row. Read the grid as one list, row by row. Index i of that list is the cell (i // cols, i % cols). Rotate the list, then write it back. Rotating each row on its own is wrong.

Cost and related problems

The three reversals swap about n / 2 + k / 2 + (n - k) / 2 pairs, which is about n swaps. So the time is O(N) and the space is O(1). For the board, the reversals use 3, 1 and 2 swaps, which is 6 swaps for 7 items.

A copy into a new list is O(N) time and O(N) space. Moving each item to its final place, one cycle at a time, is O(N) time and O(1) space, but it needs the gcd of n and k to count the cycles. The three reversals are shorter to write.

Reverse Words in a String (LC 151) uses the same idea in another shape: reverse the whole text, then reverse each word. The in-place version is Reverse Words in a String II (LC 186).

Rotate List (LC 61) rotates a linked list. A linked list has no indices to swap from both ends, so find the new tail and change the links instead. Rotate Image (LC 48) turns a square grid by 90 degrees. Rows become columns, which no shift of one flat list can do.

60-Second Pitch

"Rotating right by k turns A B into B A, where B is the last k items. Reversing a sequence also reverses the order of its blocks, so reverse(A B) is B^R A^R. I first do k %= n, because k can be larger than the array. Then I reverse the whole array. Then I reverse the first k items, and then the rest. Each block is back in its own order, and the blocks are in the right places. Each reversal swaps pairs from both ends in place. The time is O(N) and the space is O(1)."

Common Questions

What is the time and space complexity?

O(N) time, because the three reversals make about n swaps in total. O(1) extra space, because the swaps happen inside nums.

Why does reversing the whole array first work?

Reversing A B gives B^R A^R. The last k items are now in front, and only the order inside each block is wrong. The two reversals of the blocks fix that.

How do you rotate to the left instead?

Rotate right by n - k, or reverse the two parts first and the whole array last. For [1, 2, 3, 4, 5, 6, 7] and k = 3, the left rotation is [4, 5, 6, 7, 1, 2, 3].

Why does the code need k %= n?

Rotating by n gives the same array, so only k % n matters. If k is larger than n, reverse(0, k - 1) reads past the end of nums and raises an IndexError.

Is there another O(1) space method?

Yes. You can move each item straight to its final index, (i + k) % n, following each cycle until it closes. The number of cycles is the gcd of n and k. It is also O(N) time, but it is harder to write without a mistake.

SWE Reasoning

Spot it

  • The statement asks to rotate or cyclically shift an array by k positions, in place (Rotate Array (LC 189)).
  • A grid can be read as one flat list, and the shift moves values across row ends (Shift 2D Grid (LC 1260)).
  • Extra space must be O(1), so a second array is not allowed.
  • A text must change the order of its blocks and keep the order inside each block (Reverse Words in a String (LC 151)).

The invariant, step by step

  1. The array is A B, where B is the last k items. The goal is B A.
  2. reverse(0, n - 1) gives B^R A^R. The last k items are in front, and each block is mirrored.
  3. reverse(0, k - 1) reverses the first k items, so B^R becomes B.
  4. reverse(k, n - 1) reverses the rest, so A^R becomes A.
  5. The array is B A. Each reverse swaps pairs from both ends, so it changes nums in place and takes O(1) extra space.

Traps

  • No k %= n. For k = 10 and 7 items, reverse(0, 9) raises an IndexError.
  • Reversing the parts first. The result is a left rotation: [4, 5, 6, 7, 1, 2, 3] instead of [5, 6, 7, 1, 2, 3, 4].
  • nums = nums[-k:] + nums[:-k]. The caller's list does not change, and the new list costs O(N) space.
  • No check for an empty array. k %= n with n = 0 raises a ZeroDivisionError.
  • Rotating each row of a grid. Values must move from the end of one row to the start of the next. Rotate the flattened list.

Practice

Canonical LeetCode:Rotate Array (LC 189)Shift 2D Grid (LC 1260)Reverse Words in a String (LC 151)Rotate List (LC 61)
© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•