questions M-R
This commit is contained in:
222
backend/data/questions/rotate-array.yaml
Normal file
222
backend/data/questions/rotate-array.yaml
Normal file
@@ -0,0 +1,222 @@
|
||||
title: Rotate Array
|
||||
slug: rotate-array
|
||||
difficulty: medium
|
||||
leetcode_id: 189
|
||||
leetcode_url: https://leetcode.com/problems/rotate-array/
|
||||
categories:
|
||||
- arrays
|
||||
- two-pointers
|
||||
- math
|
||||
patterns:
|
||||
- two-pointers
|
||||
|
||||
description: |
|
||||
Given an integer array `nums`, rotate the array to the right by `k` steps, where `k` is non-negative.
|
||||
|
||||
**Note:** You must modify the array *in-place* with O(1) extra space.
|
||||
|
||||
constraints: |
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `-2^31 <= nums[i] <= 2^31 - 1`
|
||||
- `0 <= k <= 10^5`
|
||||
|
||||
examples:
|
||||
- input: "nums = [1,2,3,4,5,6,7], k = 3"
|
||||
output: "[5,6,7,1,2,3,4]"
|
||||
explanation: "Rotate 1 step to the right: [7,1,2,3,4,5,6]. Rotate 2 steps: [6,7,1,2,3,4,5]. Rotate 3 steps: [5,6,7,1,2,3,4]."
|
||||
- input: "nums = [-1,-100,3,99], k = 2"
|
||||
output: "[3,99,-1,-100]"
|
||||
explanation: "Rotate 1 step to the right: [99,-1,-100,3]. Rotate 2 steps: [3,99,-1,-100]."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
Imagine a deck of cards on a table. Rotating right by `k` means taking the last `k` cards and moving them to the front.
|
||||
|
||||
The key insight is that **rotation is really just rearranging two segments** of the array. After rotating right by `k`, the array becomes: `[last k elements] + [first n-k elements]`.
|
||||
|
||||
But how do we achieve this rearrangement *in-place* without extra space? Here's the elegant trick: **three reversals**.
|
||||
|
||||
Think of it like this: if you reverse the entire array, then reverse the first `k` elements, then reverse the remaining elements, you end up with the rotated result. It's like flipping a string of beads, then adjusting the two halves.
|
||||
|
||||
For `[1,2,3,4,5,6,7]` with `k=3`:
|
||||
- Reverse all: `[7,6,5,4,3,2,1]`
|
||||
- Reverse first 3: `[5,6,7,4,3,2,1]`
|
||||
- Reverse last 4: `[5,6,7,1,2,3,4]`
|
||||
|
||||
This works because reversing is like "flipping" segments, and the right sequence of flips puts everything in the correct rotated position.
|
||||
|
||||
approach: |
|
||||
We solve this using the **Reverse Three Times** approach:
|
||||
|
||||
**Step 1: Handle edge cases**
|
||||
|
||||
- If `k` is larger than the array length, rotating by `n` steps returns the array to its original position
|
||||
- Use `k = k % n` to get the effective rotation amount
|
||||
- If `k` becomes `0`, no rotation needed
|
||||
|
||||
|
||||
|
||||
**Step 2: Reverse the entire array**
|
||||
|
||||
- Reverse all elements from index `0` to `n-1`
|
||||
- This puts the last `k` elements at the front, but in reverse order
|
||||
- The first `n-k` elements are now at the back, also in reverse order
|
||||
|
||||
|
||||
|
||||
**Step 3: Reverse the first k elements**
|
||||
|
||||
- Reverse elements from index `0` to `k-1`
|
||||
- This corrects the order of what will be the first part of the result
|
||||
|
||||
|
||||
|
||||
**Step 4: Reverse the remaining elements**
|
||||
|
||||
- Reverse elements from index `k` to `n-1`
|
||||
- This corrects the order of the second part of the result
|
||||
|
||||
|
||||
|
||||
**Why it works:** Each element ends up in its correct rotated position after the three reversals. The reverse operation is in-place (using two pointers swapping from ends toward the middle), so we use O(1) extra space.
|
||||
|
||||
common_pitfalls:
|
||||
- title: Forgetting to Handle k > n
|
||||
description: |
|
||||
If `k` is larger than the array length, you might go out of bounds or produce incorrect results.
|
||||
|
||||
For example, with `nums = [1,2]` and `k = 3`, rotating right by 3 is the same as rotating by 1 (since rotating by 2 returns to original).
|
||||
|
||||
Always use `k = k % n` at the start to normalize `k` to a value within bounds.
|
||||
wrong_approach: "Using k directly without modulo"
|
||||
correct_approach: "Normalize with k = k % n before processing"
|
||||
|
||||
- title: Using Extra Space with Slicing
|
||||
description: |
|
||||
A tempting solution is: `nums[:] = nums[-k:] + nums[:-k]`
|
||||
|
||||
While this produces the correct result, it creates a new array to hold the concatenated slices, using **O(n) extra space**. The problem explicitly asks for O(1) space.
|
||||
|
||||
The reverse approach modifies the array in-place using only a few pointer variables.
|
||||
wrong_approach: "Array slicing and concatenation"
|
||||
correct_approach: "In-place reversal using two pointers"
|
||||
|
||||
- title: One-by-One Rotation
|
||||
description: |
|
||||
Another intuitive approach is to rotate by 1 step, `k` times:
|
||||
- Store last element
|
||||
- Shift all elements right by 1
|
||||
- Place stored element at front
|
||||
- Repeat `k` times
|
||||
|
||||
This is correct but has **O(n * k) time complexity**. With `n = k = 10^5`, that's 10 billion operations — a guaranteed TLE.
|
||||
|
||||
The triple-reverse approach is O(n) regardless of `k`.
|
||||
wrong_approach: "Rotate one step at a time, k times"
|
||||
correct_approach: "Triple reverse for O(n) time"
|
||||
|
||||
key_takeaways:
|
||||
- "**Reversal trick**: Three reversals can achieve rotation in O(n) time and O(1) space — a classic technique"
|
||||
- "**Modulo for cycles**: When dealing with rotations or circular operations, `k % n` normalizes the operation"
|
||||
- "**In-place operations**: Two-pointer swapping from both ends is the standard way to reverse in-place"
|
||||
- "**Pattern recognition**: This same reversal technique works for rotating strings and other sequence problems"
|
||||
|
||||
time_complexity: "O(n). Each element is visited at most twice (once per reversal it participates in)."
|
||||
space_complexity: "O(1). We only use a few variables for indices and swapping, regardless of input size."
|
||||
|
||||
solutions:
|
||||
- approach_name: Reverse Three Times
|
||||
is_optimal: true
|
||||
code: |
|
||||
def rotate(nums: list[int], k: int) -> None:
|
||||
"""
|
||||
Rotate array in-place using triple reversal.
|
||||
"""
|
||||
n = len(nums)
|
||||
# Normalize k to handle cases where k > n
|
||||
k = k % n
|
||||
|
||||
# Helper function to reverse a portion of the array
|
||||
def reverse(left: int, right: int) -> None:
|
||||
while left < right:
|
||||
nums[left], nums[right] = nums[right], nums[left]
|
||||
left += 1
|
||||
right -= 1
|
||||
|
||||
# Step 1: Reverse the entire array
|
||||
reverse(0, n - 1)
|
||||
# Step 2: Reverse the first k elements
|
||||
reverse(0, k - 1)
|
||||
# Step 3: Reverse the remaining elements
|
||||
reverse(k, n - 1)
|
||||
explanation: |
|
||||
**Time Complexity:** O(n) — Each element is moved at most twice.
|
||||
|
||||
**Space Complexity:** O(1) — Only a few variables for indices.
|
||||
|
||||
The triple-reverse technique elegantly achieves rotation without extra arrays. By reversing the whole array, then reversing two subarrays, each element lands in its correct rotated position.
|
||||
|
||||
- approach_name: Cyclic Replacements
|
||||
is_optimal: true
|
||||
code: |
|
||||
def rotate(nums: list[int], k: int) -> None:
|
||||
"""
|
||||
Rotate using cyclic replacements - each element jumps to its final position.
|
||||
"""
|
||||
n = len(nums)
|
||||
k = k % n
|
||||
if k == 0:
|
||||
return
|
||||
|
||||
count = 0 # Track how many elements we've moved
|
||||
start = 0
|
||||
|
||||
while count < n:
|
||||
current = start
|
||||
prev = nums[start]
|
||||
|
||||
# Follow the cycle until we return to start
|
||||
while True:
|
||||
next_idx = (current + k) % n
|
||||
# Swap: place prev at next_idx, save what was there
|
||||
nums[next_idx], prev = prev, nums[next_idx]
|
||||
current = next_idx
|
||||
count += 1
|
||||
|
||||
# If we've returned to start, this cycle is complete
|
||||
if current == start:
|
||||
break
|
||||
|
||||
# Move to next starting position for next cycle
|
||||
start += 1
|
||||
explanation: |
|
||||
**Time Complexity:** O(n) — Each element is moved exactly once.
|
||||
|
||||
**Space Complexity:** O(1) — Only tracking indices and one temp value.
|
||||
|
||||
This approach directly places each element at its final position by following cycles. We start at index 0, move the element to its destination `(0 + k) % n`, then move whatever was there to its destination, and so on until we return to the start. If we haven't moved all elements, we start a new cycle from the next index.
|
||||
|
||||
- approach_name: Extra Array (Not Optimal for Space)
|
||||
is_optimal: false
|
||||
code: |
|
||||
def rotate(nums: list[int], k: int) -> None:
|
||||
"""
|
||||
Rotate using an extra array - simple but uses O(n) space.
|
||||
"""
|
||||
n = len(nums)
|
||||
k = k % n
|
||||
|
||||
# Create a rotated copy
|
||||
rotated = [0] * n
|
||||
for i in range(n):
|
||||
rotated[(i + k) % n] = nums[i]
|
||||
|
||||
# Copy back to original array
|
||||
for i in range(n):
|
||||
nums[i] = rotated[i]
|
||||
explanation: |
|
||||
**Time Complexity:** O(n) — Two passes through the array.
|
||||
|
||||
**Space Complexity:** O(n) — Extra array to store rotated result.
|
||||
|
||||
This straightforward approach creates a new array where each element is placed directly at its rotated position. While correct and easy to understand, it uses O(n) extra space, which doesn't meet the follow-up challenge of O(1) space. Useful for understanding the problem before optimizing.
|
||||
Reference in New Issue
Block a user