CoreTechED

Loading

Learn to quickly find multiple left rotations of an array in C: naive, doubled array, and modular indexing approaches. Master O(n) per query rotation.

Quickly Find Multiple Left Rotations of Array in C

Quickly Find Multiple Left Rotations of an Array in C: A Comprehensive Tutorial

Given an array of size n and multiple values k representing the number of left rotations, the task is to efficiently find the rotated array for each query. Instead of rotating the array for each query separately (which would be O(n × q)), we can preprocess the array to answer each query in O(n) time (for printing) with O(1) overhead.

This tutorial provides an in-depth exploration of quickly finding multiple left rotations: naive repeated rotation, doubled array preprocessing, and space-optimized modular indexing approaches, with practical implementation, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is the Problem?

Given an array and multiple queries k1, k2, k3, ..., kq, find the array after left rotating by each k positions.

Example:

text

Array: [1, 3, 5, 7, 9] Queries: k = 2, 3, 4 Left rotate by 2: [5, 7, 9, 1, 3] Left rotate by 3: [7, 9, 1, 3, 5] Left rotate by 4: [9, 1, 3, 5, 7]

Why This Problem Matters

  • Multiple queries: Efficient handling of repeated rotation requests.
  • Preprocessing: Trade-off between space and time.
  • Algorithm design: Demonstrates the power of precomputation.
  • Real-world applications: Circular buffers, data streaming, cyclic scheduling.
  • Interview preparation: Common coding interview question.

Key Insight

After k left rotations, the element at position i in the rotated array is arr[(i + k) % n]. This simple observation enables O(1) rotation lookup.

Approaches Overview

ApproachPreprocessingPer QuerySpace
Naive repeated rotationNoneO(n)O(n)
Doubled arrayO(n)O(n)O(n)
Modular indexingNoneO(n)O(1)

Syntax & Core Mechanics

Approach 1: Naive Repeated Rotation

For each query, rotate the array by k positions.

c

#include <stdio.h> #include <stdlib.h> #include <string.h> void reverse(int arr[], int start, int end) { while (start < end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } void leftRotate(int arr[], int n, int k) { k %= n; if (k == 0) return; reverse(arr, 0, k - 1); reverse(arr, k, n - 1); reverse(arr, 0, n - 1); } void printArray(const int arr[], int n) { for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); } int main(void) { int original[] = {1, 3, 5, 7, 9}; int n = sizeof(original) / sizeof(original[0]); int queries[] = {2, 3, 4, 6, 14}; int q = sizeof(queries) / sizeof(queries[0]); for (int i = 0; i < q; i++) { int *arr = malloc(n * sizeof(int)); if (arr == NULL) return 1; memcpy(arr, original, n * sizeof(int)); leftRotate(arr, n, queries[i]); printArray(arr, n); free(arr); } return 0; }

Expected output:

text

5 7 9 1 3 7 9 1 3 5 9 1 3 5 7 3 5 7 9 1 9 1 3 5 7

Time: O(n) per query, Space: O(n)

Approach 2: Doubled Array Preprocessing

Create a temporary array of size 2n containing two copies of the original array. For each query, print n elements starting from index k % n.

c

#include <stdio.h> #include <stdlib.h> #include <string.h> void preprocess(const int arr[], int n, int temp[]) { for (int i = 0; i < n; i++) { temp[i] = arr[i]; temp[i + n] = arr[i]; } } void leftRotate(const int temp[], int n, int k) { int start = k % n; for (int i = start; i < start + n; i++) { printf("%d ", temp[i]); } printf("\n"); } int main(void) { int arr[] = {1, 3, 5, 7, 9}; int n = sizeof(arr) / sizeof(arr[0]); int *temp = malloc(2 * n * sizeof(int)); if (temp == NULL) return 1; preprocess(arr, n, temp); int queries[] = {2, 3, 4, 6, 14}; int q = sizeof(queries) / sizeof(queries[0]); for (int i = 0; i < q; i++) { leftRotate(temp, n, queries[i]); } free(temp); return 0; }

Expected output:

text

5 7 9 1 3 7 9 1 3 5 9 1 3 5 7 3 5 7 9 1 9 1 3 5 7

Preprocessing: O(n), Per query: O(n), Space: O(n)

Approach 3: Space-Optimized Modular Indexing

Without extra space, use modular arithmetic to access elements directly.

c

#include <stdio.h> void leftRotate(const int arr[], int n, int k) { k %= n; for (int i = k; i < k + n; i++) { printf("%d ", arr[i % n]); } printf("\n"); } int main(void) { int arr[] = {1, 3, 5, 7, 9}; int n = sizeof(arr) / sizeof(arr[0]); int queries[] = {2, 3, 4, 6, 14}; int q = sizeof(queries) / sizeof(queries[0]); for (int i = 0; i < q; i++) { leftRotate(arr, n, queries[i]); } return 0; }

Expected output:

text

5 7 9 1 3 7 9 1 3 5 9 1 3 5 7 3 5 7 9 1 9 1 3 5 7

Preprocessing: None, Per query: O(n), Space: O(1)


Full Practical Code Example with Explanation

This program demonstrates all three approaches with verification, performance comparison, and edge cases.

Program: Multiple Left Rotations Demo

c

/** * multiple_left_rotations.c * Demonstrates approaches to quickly find multiple left rotations. * Compares naive, doubled array, and modular indexing approaches. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <time.h> #define MAX_LINE_LEN 100 #define MAX_SIZE 100000 /* Function prototypes */ void reverse(int arr[], int start, int end); void leftRotateInPlace(int arr[], int n, int k); void printRotatedNaive(const int arr[], int n, int k); void preprocessDoubled(const int arr[], int n, int temp[]); void printRotatedDoubled(const int temp[], int n, int k); void printRotatedModular(const int arr[], int n, int k); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); void demonstrateApproaches(void); void performanceComparison(void); void demonstrateEdgeCases(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Multiple Left Rotations Demo ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Demonstrate All Approaches\n"); printf("2. Custom Array with Multiple Queries\n"); printf("3. Performance Comparison\n"); printf("4. Edge Cases\n"); printf("5. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstrateApproaches(); break; case 2: { int arr[MAX_SIZE]; int n, q; if (readInteger("Enter array size (1-100000): ", &n) != 0) { fprintf(stderr, "Invalid size.\n"); break; } if (n < 1 || n > MAX_SIZE) { printf("Size must be 1-%d.\n", MAX_SIZE); break; } printf("Enter %d elements:\n", n); for (int i = 0; i < n; i++) { char prompt[50]; snprintf(prompt, sizeof(prompt), " Element %d: ", i); if (readInteger(prompt, &arr[i]) != 0) { fprintf(stderr, "Invalid input.\n"); n = i; break; } } if (readInteger("Enter number of queries: ", &q) != 0) { fprintf(stderr, "Invalid q.\n"); break; } printf("\nOriginal: "); printArray(arr, n); /* Preprocess for doubled approach */ int *temp = malloc(2 * n * sizeof(int)); if (temp == NULL) { fprintf(stderr, "Memory allocation failed.\n"); break; } preprocessDoubled(arr, n, temp); printf("\nResults:\n"); for (int i = 0; i < q; i++) { int k; char prompt[50]; snprintf(prompt, sizeof(prompt), "Query %d k: ", i + 1); if (readInteger(prompt, &k) != 0) { fprintf(stderr, "Invalid k.\n"); i--; continue; } printf(" k=%-4d | Naive: ", k); printRotatedNaive(arr, n, k); printf(" | Doubled: "); printRotatedDoubled(temp, n, k); printf(" | Modular: "); printRotatedModular(arr, n, k); } free(temp); break; } case 3: performanceComparison(); break; case 4: demonstrateEdgeCases(); break; case 5: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Reverses array portion */ void reverse(int arr[], int start, int end) { while (start < end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } /* In-place left rotation using reversal algorithm */ void leftRotateInPlace(int arr[], int n, int k) { if (n <= 1) return; k %= n; if (k == 0) return; reverse(arr, 0, k - 1); reverse(arr, k, n - 1); reverse(arr, 0, n - 1); } /* Naive: rotate a copy and print */ void printRotatedNaive(const int arr[], int n, int k) { int *copy = malloc(n * sizeof(int)); if (copy == NULL) return; memcpy(copy, arr, n * sizeof(int)); leftRotateInPlace(copy, n, k); printArray(copy, n); free(copy); } /* Preprocess doubled array */ void preprocessDoubled(const int arr[], int n, int temp[]) { for (int i = 0; i < n; i++) { temp[i] = arr[i]; temp[i + n] = arr[i]; } } /* Print using doubled array */ void printRotatedDoubled(const int temp[], int n, int k) { int start = k % n; printf("["); for (int i = start; i < start + n; i++) { printf("%d", temp[i]); if (i < start + n - 1) printf(", "); } printf("]\n"); } /* Print using modular indexing */ void printRotatedModular(const int arr[], int n, int k) { k %= n; printf("["); for (int i = k; i < k + n; i++) { printf("%d", arr[i % n]); if (i < k + n - 1) printf(", "); } printf("]\n"); } /* Prints array */ void printArray(const int arr[], int n) { printf("["); for (int i = 0; i < n; i++) { printf("%d", arr[i]); if (i < n - 1) printf(", "); } printf("]\n"); } /* Demonstrates all three approaches */ void demonstrateApproaches(void) { printf("\n=== All Approaches ===\n"); int arr[] = {1, 3, 5, 7, 9}; int n = sizeof(arr) / sizeof(arr[0]); int queries[] = {2, 3, 4, 6, 14}; int q = sizeof(queries) / sizeof(queries[0]); printf("Original: "); printArray(arr, n); printf("\n"); int *temp = malloc(2 * n * sizeof(int)); if (temp == NULL) return; preprocessDoubled(arr, n, temp); for (int i = 0; i < q; i++) { int k = queries[i]; printf("k = %d\n", k); printf(" Naive: "); printRotatedNaive(arr, n, k); printf(" Doubled: "); printRotatedDoubled(temp, n, k); printf(" Modular: "); printRotatedModular(arr, n, k); printf("\n"); } free(temp); } /* Performance comparison */ void performanceComparison(void) { printf("\n=== Performance Comparison (n = 100000, q = 10) ===\n"); int n = 100000; int q = 10; int *arr = malloc(n * sizeof(int)); int *temp = malloc(2 * n * sizeof(int)); if (arr == NULL || temp == NULL) { fprintf(stderr, "Memory allocation failed.\n"); free(arr); free(temp); return; } for (int i = 0; i < n; i++) arr[i] = i; preprocessDoubled(arr, n, temp); clock_t start, end; double timeTaken; /* Naive */ start = clock(); for (int i = 0; i < q; i++) { int *copy = malloc(n * sizeof(int)); memcpy(copy, arr, n * sizeof(int)); leftRotateInPlace(copy, n, i * 100); free(copy); } end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Naive (rotate copy): %.3f ms\n", timeTaken); /* Doubled */ start = clock(); for (int i = 0; i < q; i++) { int k = i * 100; int startIdx = k % n; volatile long sum = 0; for (int j = startIdx; j < startIdx + n; j++) { sum += temp[j]; } (void)sum; } end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Doubled array: %.3f ms\n", timeTaken); /* Modular */ start = clock(); for (int i = 0; i < q; i++) { int k = i * 100; volatile long sum = 0; for (int j = k; j < k + n; j++) { sum += arr[j % n]; } (void)sum; } end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Modular indexing: %.3f ms\n", timeTaken); free(arr); free(temp); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); int arr[] = {1, 3, 5, 7, 9}; int n = sizeof(arr) / sizeof(arr[0]); /* Case 1: k = 0 */ printf("Case 1: k = 0 (no rotation)\n"); printRotatedModular(arr, n, 0); /* Case 2: k = n */ printf("\nCase 2: k = n (full rotation, no change)\n"); printRotatedModular(arr, n, n); /* Case 3: k > n */ printf("\nCase 3: k = 14 > n = 5 (k %% n = 4)\n"); printRotatedModular(arr, n, 14); /* Case 4: Single element */ int arr2[] = {42}; printf("\nCase 4: Single element\n"); printRotatedModular(arr2, 1, 3); /* Case 5: k = n - 1 */ printf("\nCase 5: k = n - 1 = 4\n"); printRotatedModular(arr, n, 4); } /* Reads an integer safely */ int readInteger(const char *prompt, int *outValue) { char buffer[MAX_LINE_LEN]; char *endPtr; long val; printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { return -1; } errno = 0; val = strtol(buffer, &endPtr, 10); if (endPtr == buffer || (*endPtr != '\n' && *endPtr != '\0')) { return -1; } if (errno == ERANGE || val < -2147483647 - 1 || val > 2147483647) { return -1; } *outValue = (int)val; return 0; }

Explanation of Approaches

  1. Naive: Copies and rotates for each query; O(n) per query.
  2. Doubled: Preprocesses 2n array; O(n) per query for printing.
  3. Modular: No preprocessing; O(n) per query for printing with O(1) space.

Expected Output (sample run)

text

=== Multiple Left Rotations Demo === --- Menu --- 1. Demonstrate All Approaches 2. Custom Array with Multiple Queries 3. Performance Comparison 4. Edge Cases 5. Exit Enter choice: 1 === All Approaches === Original: [1, 3, 5, 7, 9] k = 2 Naive: [5, 7, 9, 1, 3] Doubled: [5, 7, 9, 1, 3] Modular: [5, 7, 9, 1, 3] k = 3 Naive: [7, 9, 1, 3, 5] Doubled: [7, 9, 1, 3, 5] Modular: [7, 9, 1, 3, 5] k = 4 Naive: [9, 1, 3, 5, 7] Doubled: [9, 1, 3, 5, 7] Modular: [9, 1, 3, 5, 7] k = 6 Naive: [3, 5, 7, 9, 1] Doubled: [3, 5, 7, 9, 1] Modular: [3, 5, 7, 9, 1] k = 14 Naive: [9, 1, 3, 5, 7] Doubled: [9, 1, 3, 5, 7] Modular: [9, 1, 3, 5, 7] --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: k = 0 (no rotation) [1, 3, 5, 7, 9] Case 2: k = n (full rotation, no change) [1, 3, 5, 7, 9] Case 3: k = 14 > n = 5 (k % n = 4) [9, 1, 3, 5, 7] Case 4: Single element [42] Case 5: k = n - 1 = 4 [9, 1, 3, 5, 7]

Common Pitfalls, Edge Cases & Memory Considerations

1. Not Handling k > n

Forgetting k %= n causes out-of-bounds access.

c

int start = k; /* BUG if k > n */ int start = k % n; /* Correct */

Solution: Always apply k %= n.

2. Negative k

Negative k causes implementation-defined modulo results.

c

k = -1; k %= n; /* Implementation-defined */

Solution: Normalize: k = ((k % n) + n) % n;

3. Empty Array

Accessing arr[0] on empty array causes issues.

c

if (n <= 0) return; /* Required */

4. Single Element

A single-element array remains unchanged for any k.

c

int arr[] = {42}; /* Rotated by any k: [42] */

5. Memory Leaks with Naive Approach

Forgetting to free the copy array.

c

int *copy = malloc(n * sizeof(int)); /* ... */ /* Missing free(copy); */

Solution: Always free dynamically allocated memory.

6. Overflow in i + n

For doubled array, ensure index stays within 2n.

c

for (int i = start; i < start + n; i++) /* start + n <= 2n */

7. Modular Indexing Correctness

arr[i % n] works correctly for non-negative i.

Solution: Ensure loop variable starts at k (non-negative after modulo).

8. Printing Format

Consistent formatting for readability.

c

printf("["); /* ... */ printf("]\n");

9. Multiple Queries with Same k

Cache results if same k repeats.

Solution: Use a hash map or sort queries for optimization.

10. Memory Considerations

  • Naive: O(n) extra space per query.
  • Doubled: O(n) extra space once.
  • Modular: O(1) extra space.
  • Modular approach is preferred for memory-constrained environments.

Summary & Best Practices

Quickly finding multiple left rotations of an array involves preprocessing or using modular arithmetic to answer each query efficiently. The modular indexing approach is the most space-efficient.

Key Takeaways

  • After k left rotations, element i becomes arr[(i + k) % n].
  • Naive: O(n) per query, O(n) space.
  • Doubled array: O(n) preprocessing, O(n) per query, O(n) space.
  • Modular: No preprocessing, O(n) per query, O(1) space.
  • Always apply k %= n to handle large k.

Best Practices

  1. Use modular indexing for space efficiency.
  2. Use doubled array when multiple queries benefit from preprocessing.
  3. Apply k %= n to normalize rotation count.
  4. Handle edge cases: empty array, single element, k = 0, k = n.
  5. Free dynamically allocated memory.
  6. Use size_t for array sizes where appropriate.
  7. Validate input (array size, k range).
  8. Consistent output formatting for readability.
  9. Test with k > n and negative values.
  10. Profile for very large arrays and many queries.

By mastering this problem, you’ll efficiently handle multiple rotation queries and understand the trade-offs between preprocessing and on-the-fly computation.

Learn to left rotate an array in C: one-by-one, temporary array, juggling, and reversal algorithms. Master O(n) in-place rotation techniques.

Rotate an Array by d – Counterclockwise or Left in C

Rotate an Array by d – Counterclockwise or Left in C: A Comprehensive Tutorial

Left rotation (counterclockwise rotation) of an array by d positions shifts each element d positions to the left, with elements wrapping around from the beginning to the end. Understanding left rotation is essential for solving problems involving circular data, rotated sorted arrays, and algorithmic challenges.

This tutorial provides an in-depth exploration of left array rotation in C: multiple approaches, time and space complexities, practical applications, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is Left Rotation?

Left rotation (counterclockwise rotation) by d positions moves each element d positions to the left. Elements that go past index 0 wrap around to the end.

Example:

text

Original: [1, 2, 3, 4, 5, 6], d = 2 Rotated: [3, 4, 5, 6, 1, 2]

Step-by-step:

text

Start: [1, 2, 3, 4, 5, 6] After 1: [2, 3, 4, 5, 6, 1] After 2: [3, 4, 5, 6, 1, 2]

Why Left Rotation Matters

  • Circular buffers: Implement queues and ring buffers.
  • Rotated sorted arrays: Search in O(log n) with modifications.
  • String rotation: Check if one string is a rotation of another.
  • Data processing: Shift data windows forward.
  • Cryptography: Caesar cipher (left shift variant).

Approaches Overview

ApproachTime ComplexitySpace Complexity
Rotate one by oneO(n × d)O(1)
Temporary arrayO(n)O(n)
Juggling algorithmO(n)O(1)
Reversal algorithmO(n)O(1)

Handling d > n

When d > n, reduce using d % n:

text

d = 4, n = 3 → d % n = 1 (equivalent left rotation)

Syntax & Core Mechanics

Approach 1: Rotate One by One (Naive)

Rotate the array by one position d times.

c

#include <stdio.h> void rotateLeftOneByOne(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; for (int i = 0; i < d; i++) { int first = arr[0]; /* Save first element */ /* Shift all elements left by one */ for (int j = 0; j < n - 1; j++) { arr[j] = arr[j + 1]; } arr[n - 1] = first; /* Place first element at end */ } } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateLeftOneByOne(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

3 4 5 6 1 2

Time: O(n × d), Space: O(1)

Approach 2: Temporary Array

Copy the last n - d elements to the front and the first d elements to the back.

c

#include <stdio.h> #include <stdlib.h> void rotateLeftTemp(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; int *temp = malloc(n * sizeof(int)); if (temp == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } /* Copy last n-d elements to front of temp */ for (int i = 0; i < n - d; i++) { temp[i] = arr[d + i]; } /* Copy first d elements to back of temp */ for (int i = 0; i < d; i++) { temp[n - d + i] = arr[i]; } /* Copy back to original */ for (int i = 0; i < n; i++) { arr[i] = temp[i]; } free(temp); } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateLeftTemp(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

3 4 5 6 1 2

Time: O(n), Space: O(n)

Approach 3: Juggling Algorithm

Uses GCD to determine cycles; rotates elements within each cycle.

Key insight: For left rotation, element at index i moves to index (i - d + n) % n. Equivalently, the element that ends up at index i comes from (i + d) % n.

c

#include <stdio.h> int gcd(int a, int b) { while (b) { int temp = b; b = a % b; a = temp; } return a; } void rotateLeftJuggling(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; int cycles = gcd(n, d); for (int i = 0; i < cycles; i++) { int currIdx = i; int currEle = arr[currIdx]; do { /* For left rotation: next index where current element goes */ int nextIdx = (currIdx - d + n) % n; int nextEle = arr[nextIdx]; arr[nextIdx] = currEle; currEle = nextEle; currIdx = nextIdx; } while (currIdx != i); } } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateLeftJuggling(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

3 4 5 6 1 2

Time: O(n), Space: O(1)

Approach 4: Reversal Algorithm

Reverse the first d elements, then reverse the remaining n - d elements, then reverse the entire array.

c

#include <stdio.h> void reverse(int arr[], int start, int end) { while (start < end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } void rotateLeftReversal(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; reverse(arr, 0, d - 1); /* Reverse first d elements */ reverse(arr, d, n - 1); /* Reverse remaining n-d elements */ reverse(arr, 0, n - 1); /* Reverse entire array */ } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateLeftReversal(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

3 4 5 6 1 2

Time: O(n), Space: O(1)


Full Practical Code Example with Explanation

This program demonstrates all four left rotation approaches with verification, performance comparison, and edge cases.

Program: Left Array Rotation Comparison

c

/** * array_left_rotation.c * Demonstrates multiple approaches to left rotate an array. * Includes verification, timing, and edge case handling. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <time.h> #define MAX_LINE_LEN 100 #define MAX_SIZE 100000 /* Function prototypes */ void rotateLeftOneByOne(int arr[], int n, int d); void rotateLeftTemp(int arr[], int n, int d); void rotateLeftJuggling(int arr[], int n, int d); void rotateLeftReversal(int arr[], int n, int d); void reverse(int arr[], int start, int end); int gcd(int a, int b); void printArray(const int arr[], int n); int readInteger(const char *prompt, int *outValue); int arraysEqual(const int a[], const int b[], int n); void copyArray(const int src[], int dst[], int n); void demonstrateApproaches(void); void performanceComparison(void); void demonstrateEdgeCases(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Array Left Rotation (Counterclockwise) Demo ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Demonstrate All Approaches\n"); printf("2. Custom Rotation\n"); printf("3. Performance Comparison\n"); printf("4. Edge Cases\n"); printf("5. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstrateApproaches(); break; case 2: { int arr[MAX_SIZE]; int n, d, method; if (readInteger("Enter array size (1-100000): ", &n) != 0) { fprintf(stderr, "Invalid size.\n"); break; } if (n < 1 || n > MAX_SIZE) { printf("Size must be 1-%d.\n", MAX_SIZE); break; } printf("Enter %d elements:\n", n); for (int i = 0; i < n; i++) { char prompt[50]; snprintf(prompt, sizeof(prompt), " Element %d: ", i); if (readInteger(prompt, &arr[i]) != 0) { fprintf(stderr, "Invalid input.\n"); n = i; break; } } if (readInteger("Enter rotation count d: ", &d) != 0) { fprintf(stderr, "Invalid d.\n"); break; } printf("\nOriginal: "); printArray(arr, n); printf("\nMethod:\n"); printf("1. One by One\n"); printf("2. Temporary Array\n"); printf("3. Juggling\n"); printf("4. Reversal\n"); printf("Enter method: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) break; method = atoi(buffer); switch (method) { case 1: rotateLeftOneByOne(arr, n, d); break; case 2: rotateLeftTemp(arr, n, d); break; case 3: rotateLeftJuggling(arr, n, d); break; case 4: rotateLeftReversal(arr, n, d); break; default: printf("Invalid method.\n"); continue; } printf("Rotated left by %d: ", d); printArray(arr, n); break; } case 3: performanceComparison(); break; case 4: demonstrateEdgeCases(); break; case 5: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Approach 1: Rotate one by one */ void rotateLeftOneByOne(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; for (int i = 0; i < d; i++) { int first = arr[0]; for (int j = 0; j < n - 1; j++) { arr[j] = arr[j + 1]; } arr[n - 1] = first; } } /* Approach 2: Temporary array */ void rotateLeftTemp(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; int *temp = malloc(n * sizeof(int)); if (temp == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } for (int i = 0; i < n - d; i++) { temp[i] = arr[d + i]; } for (int i = 0; i < d; i++) { temp[n - d + i] = arr[i]; } for (int i = 0; i < n; i++) { arr[i] = temp[i]; } free(temp); } /* Approach 3: Juggling algorithm */ int gcd(int a, int b) { while (b) { int temp = b; b = a % b; a = temp; } return a; } void rotateLeftJuggling(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; int cycles = gcd(n, d); for (int i = 0; i < cycles; i++) { int currIdx = i; int currEle = arr[currIdx]; do { int nextIdx = (currIdx - d + n) % n; int nextEle = arr[nextIdx]; arr[nextIdx] = currEle; currEle = nextEle; currIdx = nextIdx; } while (currIdx != i); } } /* Approach 4: Reversal algorithm */ void reverse(int arr[], int start, int end) { while (start < end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } void rotateLeftReversal(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; reverse(arr, 0, d - 1); reverse(arr, d, n - 1); reverse(arr, 0, n - 1); } /* Prints array */ void printArray(const int arr[], int n) { printf("["); for (int i = 0; i < n; i++) { printf("%d", arr[i]); if (i < n - 1) printf(", "); } printf("]\n"); } /* Compares arrays */ int arraysEqual(const int a[], const int b[], int n) { for (int i = 0; i < n; i++) { if (a[i] != b[i]) return 0; } return 1; } /* Copies array */ void copyArray(const int src[], int dst[], int n) { memcpy(dst, src, n * sizeof(int)); } /* Demonstrates all approaches */ void demonstrateApproaches(void) { printf("\n=== All Left Rotation Approaches ===\n"); int original[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(original) / sizeof(original[0]); int d = 2; int expected[] = {3, 4, 5, 6, 1, 2}; int arr[MAX_SIZE]; printf("Original: [1, 2, 3, 4, 5, 6], d = 2\n"); printf("Expected: [3, 4, 5, 6, 1, 2]\n\n"); copyArray(original, arr, n); rotateLeftOneByOne(arr, n, d); printf("One by One: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); copyArray(original, arr, n); rotateLeftTemp(arr, n, d); printf("Temp Array: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); copyArray(original, arr, n); rotateLeftJuggling(arr, n, d); printf("Juggling: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); copyArray(original, arr, n); rotateLeftReversal(arr, n, d); printf("Reversal: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); } /* Performance comparison */ void performanceComparison(void) { printf("\n=== Performance Comparison (n = 100000, d = 12345) ===\n"); int size = 100000; int d = 12345; int *original = malloc(size * sizeof(int)); int *arr = malloc(size * sizeof(int)); if (original == NULL || arr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); free(original); free(arr); return; } for (int i = 0; i < size; i++) { original[i] = i; } clock_t start, end; double timeTaken; copyArray(original, arr, size); start = clock(); rotateLeftTemp(arr, size, d); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Temp Array: %.3f ms\n", timeTaken); copyArray(original, arr, size); start = clock(); rotateLeftJuggling(arr, size, d); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Juggling: %.3f ms\n", timeTaken); copyArray(original, arr, size); start = clock(); rotateLeftReversal(arr, size, d); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Reversal: %.3f ms\n", timeTaken); free(original); free(arr); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); /* Case 1: d = 0 */ int arr1[] = {1, 2, 3, 4, 5}; printf("Case 1: d = 0 (no rotation)\n"); printf(" Before: "); printArray(arr1, 5); rotateLeftReversal(arr1, 5, 0); printf(" After: "); printArray(arr1, 5); /* Case 2: d > n */ int arr2[] = {1, 2, 3}; printf("\nCase 2: d = 4 > n = 3 (d %% n = 1)\n"); printf(" Before: "); printArray(arr2, 3); rotateLeftReversal(arr2, 3, 4); printf(" After: "); printArray(arr2, 3); /* Case 3: Single element */ int arr3[] = {42}; printf("\nCase 3: Single element\n"); printf(" Before: "); printArray(arr3, 1); rotateLeftReversal(arr3, 1, 3); printf(" After: "); printArray(arr3, 1); /* Case 4: d = n */ int arr4[] = {1, 2, 3, 4}; printf("\nCase 4: d = n (no change)\n"); printf(" Before: "); printArray(arr4, 4); rotateLeftReversal(arr4, 4, 4); printf(" After: "); printArray(arr4, 4); } /* Reads an integer safely */ int readInteger(const char *prompt, int *outValue) { char buffer[MAX_LINE_LEN]; char *endPtr; long val; printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { return -1; } errno = 0; val = strtol(buffer, &endPtr, 10); if (endPtr == buffer || (*endPtr != '\n' && *endPtr != '\0')) { return -1; } if (errno == ERANGE || val < -2147483647 - 1 || val > 2147483647) { return -1; } *outValue = (int)val; return 0; }

Explanation of Left Rotation Approaches

  1. One by One: Simple, O(n × d) time — inefficient for large d.
  2. Temporary Array: O(n) time, O(n) space — simple and fast.
  3. Juggling: O(n) time, O(1) space — uses GCD cycles for left rotation.
  4. Reversal: O(n) time, O(1) space — reverse first d, then rest, then all.

Expected Output (sample run)

text

=== Array Left Rotation (Counterclockwise) Demo === --- Menu --- 1. Demonstrate All Approaches 2. Custom Rotation 3. Performance Comparison 4. Edge Cases 5. Exit Enter choice: 1 === All Left Rotation Approaches === Original: [1, 2, 3, 4, 5, 6], d = 2 Expected: [3, 4, 5, 6, 1, 2] One by One: [3, 4, 5, 6, 1, 2] Verify: PASS Temp Array: [3, 4, 5, 6, 1, 2] Verify: PASS Juggling: [3, 4, 5, 6, 1, 2] Verify: PASS Reversal: [3, 4, 5, 6, 1, 2] Verify: PASS --- Menu --- ... Enter choice: 3 === Performance Comparison (n = 100000, d = 12345) === Temp Array: 0.812 ms Juggling: 0.598 ms Reversal: 0.561 ms --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: d = 0 (no rotation) Before: [1, 2, 3, 4, 5] After: [1, 2, 3, 4, 5] Case 2: d = 4 > n = 3 (d % n = 1) Before: [1, 2, 3] After: [2, 3, 1] Case 3: Single element Before: [42] After: [42] Case 4: d = n (no change) Before: [1, 2, 3, 4] After: [1, 2, 3, 4]

Common Pitfalls, Edge Cases & Memory Considerations

1. Not Handling d > n

Forgetting d %= n causes incorrect or excessive rotations.

c

void rotate(int arr[], int n, int d) { /* BUG: d might be > n */ }

Solution: Always apply d %= n at the start.

2. Negative d

Negative d causes implementation-defined modulo results.

c

d = -1; d %= n; /* Implementation-defined for negatives */

Solution: Normalize: d = ((d % n) + n) % n;

3. Empty Array

Rotating an empty array with n = 0 causes division by zero.

c

d %= n; /* BUG: n == 0 */

Solution: Check if (n <= 1) return; at the start.

4. Single Element

A single-element array is unchanged by rotation.

Solution: Early return for n <= 1.

5. Memory Leaks with Temporary Array

Forgetting to free the temporary array.

c

int *temp = malloc(n * sizeof(int)); /* ... */ /* Missing free(temp); */

Solution: Always free dynamically allocated memory.

6. Juggling Direction Confusion

For left rotation, the index formula differs from right rotation.

c

/* Right rotation: nextIdx = (currIdx + d) % n */ /* Left rotation: nextIdx = (currIdx - d + n) % n */

Solution: Use the correct formula for the direction.

7. Reversal Order for Left Rotation

The order of reversals differs between left and right rotation.

c

/* Left rotation: reverse first d, reverse rest, reverse all */ /* Right rotation: reverse all, reverse first d, reverse rest */

Solution: Follow the correct order for left rotation.

8. Off-by-One in Reversal Bounds

Incorrect bounds cause elements not to be reversed.

c

reverse(arr, 0, d); /* BUG: should be d - 1 */

Solution: Use reverse(arr, 0, d - 1) for first d elements.

9. Integer Overflow in Index Computation

(currIdx - d + n) % n may overflow for very large n.

Solution: Use (currIdx - (d % n) + n) % n to keep values small.

10. Memory Considerations

  • One-by-One: O(1) space, but slow for large d.
  • Temporary Array: O(n) space, fast.
  • Juggling: O(1) space, optimal for in-place.
  • Reversal: O(1) space, simplest in-place.

Summary & Best Practices

Left array rotation is a fundamental operation with multiple efficient implementations. The reversal algorithm is the most elegant in-place solution; the temporary array is simplest for readability.

Key Takeaways

  • Left rotation by d moves elements left; wrap-around occurs at index 0.
  • Apply d %= n to handle d > n.
  • One-by-One: O(n × d) time — simple but slow.
  • Temporary Array: O(n) time, O(n) space — simple and fast.
  • Juggling: O(n) time, O(1) space — uses GCD cycles.
  • Reversal: O(n) time, O(1) space — reverse first d, rest, then all.

Best Practices

  1. Always apply d %= n to handle d > n.
  2. Handle edge cases: empty array, single element, d = 0, d = n.
  3. Use the reversal algorithm for in-place O(1) rotation.
  4. Use the temporary array for clarity when space permits.
  5. Free dynamically allocated memory in temporary array approach.
  6. Use the correct index formula for left rotation in juggling.
  7. Validate input (array size, d range).
  8. Document whether rotation is left or right.
  9. Test with odd and even array lengths.
  10. Measure performance for very large arrays.

By mastering left array rotation, you’ll have a powerful tool for circular data processing, rotated sorted array problems, and algorithm design.

Learn to right rotate an array in C: one-by-one, temporary array, juggling, and reversal algorithms. Master O(n) in-place rotation techniques.

Rotate an Array – Clockwise or Right in C

Rotate an Array – Clockwise or Right in C: A Comprehensive Tutorial

Array rotation is the process of rearranging the elements in an array by shifting each element to a new position. Right rotation (clockwise) shifts elements towards the right, with elements wrapping around from the end to the beginning. Understanding array rotation is essential for solving problems involving circular buffers, data shifting, and algorithmic puzzles.

This tutorial provides an in-depth exploration of right array rotation in C: multiple approaches, their time and space complexities, practical applications, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is Right Rotation?

Right rotation (clockwise rotation) by d positions moves each element d positions to the right. Elements that go past the last index wrap around to the beginning.

Example:

text

Original: [1, 2, 3, 4, 5, 6], d = 2 Rotated: [5, 6, 1, 2, 3, 4]

Why Array Rotation Matters

  • Circular buffers: Implement queues and ring buffers.
  • Data shifting: Move data windows in signal processing.
  • Algorithm foundation: Rotated sorted arrays (binary search variations).
  • Cryptography: Caesar cipher and similar ciphers.
  • Image processing: Rotate pixel arrays.

Approaches Overview

ApproachTime ComplexitySpace Complexity
Rotate one by oneO(n × d)O(1)
Temporary arrayO(n)O(n)
Juggling algorithmO(n)O(1)
Reversal algorithmO(n)O(1)

Handling d > n

When d > n, we can reduce it using d % n because rotating by n positions returns the array to its original state.

text

d = 4, n = 3 → d % n = 1 (equivalent rotation)

Syntax & Core Mechanics

Approach 1: Rotate One by One

Rotate the array by one position d times.

c

#include <stdio.h> void rotateArrOneByOne(int arr[], int n, int d) { d %= n; /* Handle d > n */ for (int i = 0; i < d; i++) { int last = arr[n - 1]; /* Save last element */ /* Shift all elements right by one */ for (int j = n - 1; j > 0; j--) { arr[j] = arr[j - 1]; } arr[0] = last; /* Place last element at front */ } } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateArrOneByOne(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

5 6 1 2 3 4

Time: O(n × d), Space: O(1)

Approach 2: Temporary Array

Copy the last d elements to the front and the first n - d elements to the back.

c

#include <stdio.h> #include <stdlib.h> void rotateArrTemp(int arr[], int n, int d) { d %= n; if (d == 0 || n <= 1) return; int *temp = malloc(n * sizeof(int)); if (temp == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } /* Copy last d elements to front */ for (int i = 0; i < d; i++) { temp[i] = arr[n - d + i]; } /* Copy first n-d elements to back */ for (int i = 0; i < n - d; i++) { temp[d + i] = arr[i]; } /* Copy back to original */ for (int i = 0; i < n; i++) { arr[i] = temp[i]; } free(temp); } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateArrTemp(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

5 6 1 2 3 4

Time: O(n), Space: O(n)

Approach 3: Juggling Algorithm

Uses GCD to determine cycles; rotates elements within each cycle.

c

#include <stdio.h> int gcd(int a, int b) { while (b) { int temp = b; b = a % b; a = temp; } return a; } void rotateArrJuggling(int arr[], int n, int d) { d %= n; if (d == 0 || n <= 1) return; int cycles = gcd(n, d); for (int i = 0; i < cycles; i++) { int currIdx = i; int currEle = arr[currIdx]; do { int nextIdx = (currIdx + d) % n; int nextEle = arr[nextIdx]; arr[nextIdx] = currEle; currEle = nextEle; currIdx = nextIdx; } while (currIdx != i); } } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateArrJuggling(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

5 6 1 2 3 4

Time: O(n), Space: O(1)

Approach 4: Reversal Algorithm

Reverse the entire array, then reverse the first d elements, then reverse the remaining n - d elements.

c

#include <stdio.h> void reverse(int arr[], int start, int end) { while (start < end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } void rotateArrReversal(int arr[], int n, int d) { d %= n; if (d == 0 || n <= 1) return; reverse(arr, 0, n - 1); /* Reverse entire array */ reverse(arr, 0, d - 1); /* Reverse first d elements */ reverse(arr, d, n - 1); /* Reverse remaining n-d elements */ } int main(void) { int arr[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int d = 2; rotateArrReversal(arr, n, d); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

Expected output:

text

5 6 1 2 3 4

Time: O(n), Space: O(1)


Full Practical Code Example with Explanation

This program demonstrates all four rotation approaches with verification and performance comparison.

Program: Array Rotation Comparison

c

/** * array_rotation.c * Demonstrates multiple approaches to right rotate an array. * Includes verification, timing, and edge case handling. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <time.h> #define MAX_LINE_LEN 100 #define MAX_SIZE 100000 /* Function prototypes */ void rotateOneByOne(int arr[], int n, int d); void rotateTempArray(int arr[], int n, int d); void rotateJuggling(int arr[], int n, int d); void rotateReversal(int arr[], int n, int d); void reverse(int arr[], int start, int end); int gcd(int a, int b); void printArray(const int arr[], int n); int readInteger(const char *prompt, int *outValue); int arraysEqual(const int a[], const int b[], int n); void copyArray(const int src[], int dst[], int n); void demonstrateApproaches(void); void performanceComparison(void); void demonstrateEdgeCases(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Array Rotation (Right/Clockwise) Demo ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Demonstrate All Approaches\n"); printf("2. Custom Rotation\n"); printf("3. Performance Comparison\n"); printf("4. Edge Cases\n"); printf("5. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstrateApproaches(); break; case 2: { int arr[MAX_SIZE]; int n, d, method; if (readInteger("Enter array size (1-100000): ", &n) != 0) { fprintf(stderr, "Invalid size.\n"); break; } if (n < 1 || n > MAX_SIZE) { printf("Size must be 1-%d.\n", MAX_SIZE); break; } printf("Enter %d elements:\n", n); for (int i = 0; i < n; i++) { char prompt[50]; snprintf(prompt, sizeof(prompt), " Element %d: ", i); if (readInteger(prompt, &arr[i]) != 0) { fprintf(stderr, "Invalid input.\n"); n = i; break; } } if (readInteger("Enter rotation count d: ", &d) != 0) { fprintf(stderr, "Invalid d.\n"); break; } printf("\nOriginal: "); printArray(arr, n); printf("\nMethod:\n"); printf("1. One by One\n"); printf("2. Temporary Array\n"); printf("3. Juggling\n"); printf("4. Reversal\n"); printf("Enter method: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) break; method = atoi(buffer); switch (method) { case 1: rotateOneByOne(arr, n, d); break; case 2: rotateTempArray(arr, n, d); break; case 3: rotateJuggling(arr, n, d); break; case 4: rotateReversal(arr, n, d); break; default: printf("Invalid method.\n"); continue; } printf("Rotated by %d: ", d); printArray(arr, n); break; } case 3: performanceComparison(); break; case 4: demonstrateEdgeCases(); break; case 5: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Approach 1: Rotate one by one */ void rotateOneByOne(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; for (int i = 0; i < d; i++) { int last = arr[n - 1]; for (int j = n - 1; j > 0; j--) { arr[j] = arr[j - 1]; } arr[0] = last; } } /* Approach 2: Temporary array */ void rotateTempArray(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; int *temp = malloc(n * sizeof(int)); if (temp == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } for (int i = 0; i < d; i++) { temp[i] = arr[n - d + i]; } for (int i = 0; i < n - d; i++) { temp[d + i] = arr[i]; } for (int i = 0; i < n; i++) { arr[i] = temp[i]; } free(temp); } /* Approach 3: Juggling algorithm */ int gcd(int a, int b) { while (b) { int temp = b; b = a % b; a = temp; } return a; } void rotateJuggling(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; int cycles = gcd(n, d); for (int i = 0; i < cycles; i++) { int currIdx = i; int currEle = arr[currIdx]; do { int nextIdx = (currIdx + d) % n; int nextEle = arr[nextIdx]; arr[nextIdx] = currEle; currEle = nextEle; currIdx = nextIdx; } while (currIdx != i); } } /* Approach 4: Reversal algorithm */ void reverse(int arr[], int start, int end) { while (start < end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } void rotateReversal(int arr[], int n, int d) { if (n <= 1) return; d %= n; if (d == 0) return; reverse(arr, 0, n - 1); reverse(arr, 0, d - 1); reverse(arr, d, n - 1); } /* Prints array */ void printArray(const int arr[], int n) { printf("["); for (int i = 0; i < n; i++) { printf("%d", arr[i]); if (i < n - 1) printf(", "); } printf("]\n"); } /* Compares arrays */ int arraysEqual(const int a[], const int b[], int n) { for (int i = 0; i < n; i++) { if (a[i] != b[i]) return 0; } return 1; } /* Copies array */ void copyArray(const int src[], int dst[], int n) { memcpy(dst, src, n * sizeof(int)); } /* Demonstrates all approaches */ void demonstrateApproaches(void) { printf("\n=== All Rotation Approaches ===\n"); int original[] = {1, 2, 3, 4, 5, 6}; int n = sizeof(original) / sizeof(original[0]); int d = 2; int expected[] = {5, 6, 1, 2, 3, 4}; int arr[MAX_SIZE]; printf("Original: [1, 2, 3, 4, 5, 6], d = 2\n"); printf("Expected: [5, 6, 1, 2, 3, 4]\n\n"); copyArray(original, arr, n); rotateOneByOne(arr, n, d); printf("One by One: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); copyArray(original, arr, n); rotateTempArray(arr, n, d); printf("Temp Array: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); copyArray(original, arr, n); rotateJuggling(arr, n, d); printf("Juggling: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); copyArray(original, arr, n); rotateReversal(arr, n, d); printf("Reversal: "); printArray(arr, n); printf(" Verify: %s\n", arraysEqual(arr, expected, n) ? "PASS" : "FAIL"); } /* Performance comparison */ void performanceComparison(void) { printf("\n=== Performance Comparison (n = 100000, d = 12345) ===\n"); int size = 100000; int d = 12345; int *original = malloc(size * sizeof(int)); int *arr = malloc(size * sizeof(int)); if (original == NULL || arr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); free(original); free(arr); return; } for (int i = 0; i < size; i++) { original[i] = i; } clock_t start, end; double timeTaken; copyArray(original, arr, size); start = clock(); rotateTempArray(arr, size, d); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Temp Array: %.3f ms\n", timeTaken); copyArray(original, arr, size); start = clock(); rotateJuggling(arr, size, d); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Juggling: %.3f ms\n", timeTaken); copyArray(original, arr, size); start = clock(); rotateReversal(arr, size, d); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Reversal: %.3f ms\n", timeTaken); free(original); free(arr); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); /* Case 1: d = 0 */ int arr1[] = {1, 2, 3, 4, 5}; int n1 = 5; printf("Case 1: d = 0\n"); printf(" Before: "); printArray(arr1, n1); rotateReversal(arr1, n1, 0); printf(" After: "); printArray(arr1, n1); /* Case 2: d > n */ int arr2[] = {1, 2, 3}; int n2 = 3; int d2 = 4; printf("\nCase 2: d = 4 > n = 3 (d %% n = 1)\n"); printf(" Before: "); printArray(arr2, n2); rotateReversal(arr2, n2, d2); printf(" After: "); printArray(arr2, n2); /* Case 3: Single element */ int arr3[] = {42}; printf("\nCase 3: Single element\n"); printf(" Before: "); printArray(arr3, 1); rotateReversal(arr3, 1, 3); printf(" After: "); printArray(arr3, 1); /* Case 4: d = n */ int arr4[] = {1, 2, 3, 4}; printf("\nCase 4: d = n (no change)\n"); printf(" Before: "); printArray(arr4, 4); rotateReversal(arr4, 4, 4); printf(" After: "); printArray(arr4, 4); } /* Reads an integer safely */ int readInteger(const char *prompt, int *outValue) { char buffer[MAX_LINE_LEN]; char *endPtr; long val; printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { return -1; } errno = 0; val = strtol(buffer, &endPtr, 10); if (endPtr == buffer || (*endPtr != '\n' && *endPtr != '\0')) { return -1; } if (errno == ERANGE || val < -2147483647 - 1 || val > 2147483647) { return -1; } *outValue = (int)val; return 0; }

Explanation of Rotation Approaches

  1. One by One: Simple, O(n × d) time — inefficient for large d.
  2. Temporary Array: O(n) time, O(n) space — simple and fast.
  3. Juggling: O(n) time, O(1) space — uses GCD cycles.
  4. Reversal: O(n) time, O(1) space — elegant and efficient.

Expected Output (sample run)

text

=== Array Rotation (Right/Clockwise) Demo === --- Menu --- 1. Demonstrate All Approaches 2. Custom Rotation 3. Performance Comparison 4. Edge Cases 5. Exit Enter choice: 1 === All Rotation Approaches === Original: [1, 2, 3, 4, 5, 6], d = 2 Expected: [5, 6, 1, 2, 3, 4] One by One: [5, 6, 1, 2, 3, 4] Verify: PASS Temp Array: [5, 6, 1, 2, 3, 4] Verify: PASS Juggling: [5, 6, 1, 2, 3, 4] Verify: PASS Reversal: [5, 6, 1, 2, 3, 4] Verify: PASS --- Menu --- ... Enter choice: 3 === Performance Comparison (n = 100000, d = 12345) === Temp Array: 0.834 ms Juggling: 0.612 ms Reversal: 0.578 ms --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: d = 0 Before: [1, 2, 3, 4, 5] After: [1, 2, 3, 4, 5] Case 2: d = 4 > n = 3 (d % n = 1) Before: [1, 2, 3] After: [3, 1, 2] Case 3: Single element Before: [42] After: [42] Case 4: d = n (no change) Before: [1, 2, 3, 4] After: [1, 2, 3, 4]

Common Pitfalls, Edge Cases & Memory Considerations

1. Not Handling d > n

Forgetting d %= n causes incorrect or excessive rotations.

c

void rotate(int arr[], int n, int d) { /* BUG: d might be > n */ }

Solution: Always apply d %= n at the start.

2. Negative d

Negative d causes undefined behavior with modulo.

c

d = -1; d %= n; /* Result is implementation-defined for negatives */

Solution: Convert negative to positive: d = ((d % n) + n) % n;

3. Empty Array

Rotating an empty array with n = 0 causes division by zero.

c

d %= n; /* BUG: n == 0 */

Solution: Check if (n <= 1) return; at the start.

4. Single Element

A single-element array is unchanged by rotation.

Solution: Early return for n <= 1.

5. Memory Leaks with Temporary Array

Forgetting to free the temporary array.

c

int *temp = malloc(n * sizeof(int)); /* ... */ /* Missing free(temp); */

Solution: Always free dynamically allocated memory.

6. Integer Overflow in Index Computation

(currIdx + d) % n may overflow for large values.

c

int nextIdx = (currIdx + d) % n; /* May overflow for very large d */

Solution: Use (currIdx + (d % n)) % n to keep values small.

7. Juggling Cycle Count

Incorrect cycle count (GCD) leads to incomplete rotation.

c

int cycles = d; /* BUG: should be gcd(n, d) */

Solution: Use gcd(n, d).

8. Reversal with d = 0

Reversing when d = 0 corrupts the array.

c

reverse(arr, 0, n - 1); /* BUG if d == 0 */

Solution: Early return when d % n == 0.

9. Off-by-One in Reversal Bounds

Incorrect bounds cause elements not to be reversed.

c

reverse(arr, 0, d); /* BUG: should be d - 1 */

Solution: Use reverse(arr, 0, d - 1) for first d elements.

10. Memory Considerations

  • One-by-One: O(1) space, but slow for large d.
  • Temporary Array: O(n) space, fast.
  • Juggling: O(1) space, optimal for in-place.
  • Reversal: O(1) space, simplest in-place.

Summary & Best Practices

Right array rotation is a fundamental operation with multiple efficient implementations. The reversal algorithm is the most elegant in-place solution; the temporary array is simplest for readability.

Key Takeaways

  • Right rotation by d moves elements right; wrap-around occurs.
  • Apply d %= n to handle d > n.
  • One-by-One: O(n × d) time — simple but slow.
  • Temporary Array: O(n) time, O(n) space — simple and fast.
  • Juggling: O(n) time, O(1) space — uses GCD cycles.
  • Reversal: O(n) time, O(1) space — elegant in-place.

Best Practices

  1. Always apply d %= n to handle d > n.
  2. Handle edge cases: empty array, single element, d = 0, d = n.
  3. Use the reversal algorithm for in-place O(1) rotation.
  4. Use the temporary array for clarity when space permits.
  5. Free dynamically allocated memory in temporary array approach.
  6. Prefer iterative approaches over recursion for stack safety.
  7. Validate input (array size, d range).
  8. Document whether rotation is left or right.
  9. Test with odd and even array lengths.
  10. Measure performance for very large arrays.

By mastering array rotation, you’ll have a powerful tool for circular data processing and algorithm design.