Question #414MediumDSA

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].

#algorithm#array#rotation#dart#list

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

text
[1, 2, 3, 4, 5]
right by 2:

  • Elements at positions 3-4 (
    text
    [4, 5]
    ) move to front
  • Result:
    text
    [4, 5, 1, 2, 3]

Approach 1: Slicing and Concatenation (Simplest)

Use list slicing to split and rejoin.

dart
List<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:

  1. Reverse entire list
  2. Reverse first K elements
  3. Reverse remaining elements
dart
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--;
  }
}

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

text
[1,2,3,4,5]
rotated by 2:

  1. Reverse all:
    text
    [5,4,3,2,1]
  2. Reverse first 2:
    text
    [4,5,3,2,1]
  3. Reverse last 3:
    text
    [4,5,1,2,3]

Approach 3: Left Rotation

Rotate left instead of right (move elements from front to back).

dart
List<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.

dart
List<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.

dart
void 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

dart
class 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

ApproachTimeSpaceIn-PlaceBest For
SlicingO(n)O(n)General use
ReversalO(n)O(1)Memory constrained
Spread operatorO(n)O(n)Modern Dart
CyclicO(n)O(1)Optimal performance

Edge Cases

dart
void 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

dart
class 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

dart
List<List<int>> rotateImage(List<List<int>> matrix, int degrees) {
  int rotations = (degrees ~/ 90) % 4;
  // Apply rotation logic
}

3. Scheduling Tasks

dart
void rotateSchedule(List<Task> tasks, int days) {
  var rotated = ListRotator.rotateRight(tasks, days);
  return rotated;
}

Interview Tips

Common follow-ups:

  1. "Can you do it in-place?" → Use reversal method (O(1) space)

  2. "What if k is very large?" → Use modulo:

    text
    k = k % list.length

  3. "How to rotate left?" → Rotate right by

    text
    (n - k)
    or use sublist differently

  4. "What if k is negative?" → Treat as left rotation:

    text
    rotateRight(list, -k)
    =
    text
    rotateLeft(list, k)

  5. "Optimize for multiple rotations?" → Combine rotations:

    text
    rotateRight(list, k1 + k2 + k3)


Visualization

text
Original: [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]

Resources