Write a function to rotate a list by K positions to the right. For example, [1,2,3,4,5] rotated by 2 becomes [4,5,1,2,3].
Answer
Overview
List rotation is a common array manipulation problem that tests your understanding of slicing, modulo arithmetic, and in-place operations. Rotating a list by K positions means moving K elements from one end to the other.
Example: Rotate
[1, 2, 3, 4, 5]- Elements at positions 3-4 () move to fronttext
[4, 5] - Result: text
[4, 5, 1, 2, 3]
Approach 1: Slicing and Concatenation (Simplest)
Use list slicing to split and rejoin.
dartList<int> rotateRight(List<int> list, int k) { if (list.isEmpty) return list; // Handle k > list.length k = k % list.length; if (k == 0) return list; // Split at (length - k) and swap return list.sublist(list.length - k) + list.sublist(0, list.length - k); } void main() { print(rotateRight([1, 2, 3, 4, 5], 2)); // [4, 5, 1, 2, 3] print(rotateRight([1, 2, 3, 4, 5], 7)); // [4, 5, 1, 2, 3] (7 % 5 = 2) print(rotateRight([1, 2, 3], 0)); // [1, 2, 3] (no rotation) }
Time Complexity: O(n) - creates new list Space Complexity: O(n) - new list allocation
Pros:
- Simplest and most readable
- One-liner solution
- Easy to understand
Approach 2: In-Place Rotation with Reversal
Reverse portions of the list for in-place rotation.
Algorithm:
- Reverse entire list
- Reverse first K elements
- Reverse remaining elements
dartvoid reverse(List<int> list, int start, int end) { while (start < end) { int temp = list[start]; list[start] = list[end]; list[end] = temp; start++; end--; } } void rotateRightInPlace(List<int> list, int k) { if (list.isEmpty) return; k = k % list.length; if (k == 0) return; int n = list.length; // Step 1: Reverse entire list reverse(list, 0, n - 1); // Step 2: Reverse first k elements reverse(list, 0, k - 1); // Step 3: Reverse remaining elements reverse(list, k, n - 1); } void main() { var nums = [1, 2, 3, 4, 5]; rotateRightInPlace(nums, 2); print(nums); // [4, 5, 1, 2, 3] var nums2 = [1, 2, 3, 4, 5, 6, 7]; rotateRightInPlace(nums2, 3); print(nums2); // [5, 6, 7, 1, 2, 3, 4] }
Time Complexity: O(n) Space Complexity: O(1) - in-place modification
How it works for [1,2,3,4,5]
- Reverse all: text
[5,4,3,2,1] - Reverse first 2: text
[4,5,3,2,1] - Reverse last 3: text
[4,5,1,2,3]
Approach 3: Left Rotation
Rotate left instead of right (move elements from front to back).
dartList<int> rotateLeft(List<int> list, int k) { if (list.isEmpty) return list; k = k % list.length; if (k == 0) return list; return list.sublist(k) + list.sublist(0, k); } void main() { print(rotateLeft([1, 2, 3, 4, 5], 2)); // [3, 4, 5, 1, 2] // Right rotation by k = Left rotation by (n - k) var list = [1, 2, 3, 4, 5]; print(rotateLeft(list, list.length - 2)); // [4, 5, 1, 2, 3] (same as rotateRight by 2) }
Relationship:
- Rotate right by K = Rotate left by (n - K)
- Rotate left by K = Rotate right by (n - K)
Approach 4: Using Collection Methods
Dart-specific approach using spread operator.
dartList<int> rotateRightSpread(List<int> list, int k) { if (list.isEmpty) return list; k = k % list.length; if (k == 0) return list; return [ ...list.skip(list.length - k), ...list.take(list.length - k), ]; } void main() { print(rotateRightSpread([1, 2, 3, 4, 5], 2)); // [4, 5, 1, 2, 3] }
Pros:
- Modern Dart syntax
- Readable
- Functional style
Approach 5: Cyclic Replacements
Optimal in-place algorithm using cyclic swaps.
dartvoid rotateRightCyclic(List<int> list, int k) { if (list.isEmpty) return; int n = list.length; k = k % n; if (k == 0) return; int count = 0; int start = 0; while (count < n) { int current = start; int prev = list[start]; do { int next = (current + k) % n; int temp = list[next]; list[next] = prev; prev = temp; current = next; count++; } while (start != current); start++; } } void main() { var nums = [1, 2, 3, 4, 5, 6]; rotateRightCyclic(nums, 2); print(nums); // [5, 6, 1, 2, 3, 4] }
Time Complexity: O(n) Space Complexity: O(1)
Use case: When memory is extremely constrained.
Complete Solution Class
dartclass ListRotator { // Simple slicing (recommended for most cases) static List<T> rotateRight<T>(List<T> list, int k) { if (list.isEmpty) return list; k = k % list.length; if (k == 0) return list; return list.sublist(list.length - k) + list.sublist(0, list.length - k); } // Rotate left static List<T> rotateLeft<T>(List<T> list, int k) { if (list.isEmpty) return list; k = k % list.length; if (k == 0) return list; return list.sublist(k) + list.sublist(0, k); } // In-place rotation (modifies original) static void rotateRightInPlace(List<int> list, int k) { if (list.isEmpty) return; k = k % list.length; if (k == 0) return; _reverse(list, 0, list.length - 1); _reverse(list, 0, k - 1); _reverse(list, k, list.length - 1); } static void _reverse(List<int> list, int start, int end) { while (start < end) { int temp = list[start]; list[start] = list[end]; list[end] = temp; start++; end--; } } // Rotate multiple times static List<T> rotateMultiple<T>(List<T> list, List<int> rotations) { var result = list; for (int k in rotations) { result = rotateRight(result, k); } return result; } // Convert right to left rotation static List<T> convertRightToLeft<T>(List<T> list, int k) { return rotateLeft(list, list.length - (k % list.length)); } } void main() { var nums = [1, 2, 3, 4, 5]; print(ListRotator.rotateRight(nums, 2)); // [4, 5, 1, 2, 3] print(ListRotator.rotateLeft(nums, 2)); // [3, 4, 5, 1, 2] var mutable = [1, 2, 3, 4, 5]; ListRotator.rotateRightInPlace(mutable, 2); print(mutable); // [4, 5, 1, 2, 3] print(ListRotator.rotateMultiple([1, 2, 3], [1, 1, 1])); // [1, 2, 3] (3 rotations = full circle) }
Comparison
| Approach | Time | Space | In-Place | Best For |
|---|---|---|---|---|
| Slicing | O(n) | O(n) | ❌ | General use |
| Reversal | O(n) | O(1) | ✅ | Memory constrained |
| Spread operator | O(n) | O(n) | ❌ | Modern Dart |
| Cyclic | O(n) | O(1) | ✅ | Optimal performance |
Edge Cases
dartvoid testEdgeCases() { // Empty list assert(rotateRight([], 5).isEmpty); // Single element assert(rotateRight([1], 100) == [1]); // k = 0 (no rotation) assert(rotateRight([1, 2, 3], 0) == [1, 2, 3]); // k = length (full rotation) assert(rotateRight([1, 2, 3], 3) == [1, 2, 3]); // k > length assert(rotateRight([1, 2, 3], 5) == [2, 3, 1]); // 5 % 3 = 2 // k negative (handle as left rotation) // assert(rotateRight([1, 2, 3], -1) == [2, 3, 1]); // Two elements assert(rotateRight([1, 2], 1) == [2, 1]); }
Best Practices
1. Handle k > length with Modulo
dart// ✅ Always normalize k k = k % list.length;
2. Check for Empty List
dart// ✅ Guard clause if (list.isEmpty) return list;
3. Use Slicing for Readability
dart// ✅ Clear and concise List<int> rotate(List<int> list, int k) { k %= list.length; return list.sublist(list.length - k) + list.sublist(0, list.length - k); }
4. Use In-Place for Large Lists
dart// ✅ When memory is a concern void rotate(List<int> list, int k) { // Use reversal method }
Real-World Applications
1. Circular Buffer
dartclass CircularBuffer<T> { final List<T> _buffer; int _position = 0; CircularBuffer(int size) : _buffer = List.filled(size, null as T); void rotate() { _position = (_position + 1) % _buffer.length; } }
2. Image Rotation
dartList<List<int>> rotateImage(List<List<int>> matrix, int degrees) { int rotations = (degrees ~/ 90) % 4; // Apply rotation logic }
3. Scheduling Tasks
dartvoid rotateSchedule(List<Task> tasks, int days) { var rotated = ListRotator.rotateRight(tasks, days); return rotated; }
Interview Tips
Common follow-ups:
-
"Can you do it in-place?" → Use reversal method (O(1) space)
-
"What if k is very large?" → Use modulo:
textk = k % list.length -
"How to rotate left?" → Rotate right by
or use sublist differentlytext(n - k) -
"What if k is negative?" → Treat as left rotation:
=textrotateRight(list, -k)textrotateLeft(list, k) -
"Optimize for multiple rotations?" → Combine rotations:
textrotateRight(list, k1 + k2 + k3)
Visualization
textOriginal: [1, 2, 3, 4, 5] Rotate right by 2: Step 1: Identify split point (length - k = 3) [1, 2, 3] | [4, 5] ^^^^^^^^ ^^^^^^^ Keep here Move to front Step 2: Concatenate [4, 5] + [1, 2, 3] Result: [4, 5, 1, 2, 3]