CoreTechED

Loading

Learn to implement prefix sum arrays in C: 1D, 1-indexed, and 2D prefix sums. Master O(1) range-sum queries with practical examples.

Prefix Sum Array in C: Implementation & Applications

Prefix Sum Array – Implementation in C: A Comprehensive Tutorial

A prefix sum array is a precomputed array where each element at index i stores the sum of all elements from index 0 to i in the original array. This simple yet powerful technique enables O(1) range-sum queries after O(n) preprocessing, making it a cornerstone of many array and range-query algorithms.

This tutorial provides an in-depth exploration of prefix sum arrays in C: concepts, implementation, range-sum queries, 2D prefix sums, practical applications, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is a Prefix Sum Array?

Given an array arr[], the prefix sum array prefixSum[] is defined as:

text

prefixSum[i] = arr[0] + arr[1] + ... + arr[i]

Example:

text

Input: [10, 20, 10, 5, 15] Prefix Sum: [10, 30, 40, 45, 60]

Why Prefix Sums Matter

  • Range sum queries: O(1) after O(n) preprocessing.
  • Foundation for many problems: Subarray sums, equilibrium index, split arrays.
  • 2D prefix sums: Rectangle sum queries in matrices.
  • Sliding window alternatives: When the array is static.
  • Competitive programming: Essential preprocessing technique.

Key Insight: Range Sum in O(1)

For a range [L, R] (inclusive):

text

sum(L, R) = prefixSum[R] - prefixSum[L - 1] (if L > 0) sum(0, R) = prefixSum[R] (if L == 0)

Complexity Comparison

OperationWithout Prefix SumWith Prefix Sum
PreprocessingO(1)O(n)
Range sum queryO(n)O(1)
Q queriesO(n × Q)O(n + Q)

Syntax & Core Mechanics

Basic Prefix Sum Implementation

c

#include <stdio.h> #include <stdlib.h> int *buildPrefixSum(const int arr[], int n) { if (n <= 0) return NULL; int *prefix = malloc(n * sizeof(int)); if (prefix == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return NULL; } prefix[0] = arr[0]; for (int i = 1; i < n; i++) { prefix[i] = prefix[i - 1] + arr[i]; } return prefix; } int main(void) { int arr[] = {10, 20, 10, 5, 15}; int n = sizeof(arr) / sizeof(arr[0]); int *prefix = buildPrefixSum(arr, n); if (prefix == NULL) return 1; printf("Original: "); for (int i = 0; i < n; i++) printf("%d ", arr[i]); printf("\n"); printf("Prefix sum: "); for (int i = 0; i < n; i++) printf("%d ", prefix[i]); printf("\n"); free(prefix); return 0; }

Expected output:

text

Original: 10 20 10 5 15 Prefix sum: 10 30 40 45 60

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

Range Sum Query

c

/* Returns sum of arr[L..R] inclusive */ long long rangeSum(const long long prefix[], int L, int R) { if (L < 0 || R < 0 || L > R) return 0; if (L == 0) return prefix[R]; return prefix[R] - prefix[L - 1]; } int main(void) { int arr[] = {10, 20, 10, 5, 15}; int n = 5; long long prefix[5]; prefix[0] = arr[0]; for (int i = 1; i < n; i++) { prefix[i] = prefix[i - 1] + arr[i]; } printf("Sum[1..3] = %lld\n", rangeSum(prefix, 1, 3)); /* 20+10+5 = 35 */ printf("Sum[0..4] = %lld\n", rangeSum(prefix, 0, 4)); /* 60 */ printf("Sum[2..2] = %lld\n", rangeSum(prefix, 2, 2)); /* 10 */ printf("Sum[3..4] = %lld\n", rangeSum(prefix, 3, 4)); /* 5+15 = 20 */ return 0; }

Expected output:

text

Sum[1..3] = 35 Sum[0..4] = 60 Sum[2..2] = 10 Sum[3..4] = 20

1-Indexed Prefix Sum (Convenient for Range Queries)

Using a 1-indexed prefix array simplifies range queries (no special case for L == 0).

c

/* prefix[0] = 0, prefix[i] = sum(arr[0..i-1]) */ void buildPrefix1Indexed(const int arr[], int n, long long prefix[]) { prefix[0] = 0; for (int i = 1; i <= n; i++) { prefix[i] = prefix[i - 1] + arr[i - 1]; } } /* Range sum [L, R] inclusive (0-based) */ long long rangeSum1(const long long prefix[], int L, int R) { return prefix[R + 1] - prefix[L]; } int main(void) { int arr[] = {10, 20, 10, 5, 15}; int n = 5; long long prefix[6]; buildPrefix1Indexed(arr, n, prefix); printf("prefix array: "); for (int i = 0; i <= n; i++) printf("%lld ", prefix[i]); printf("\n"); printf("Sum[1..3] = %lld\n", rangeSum1(prefix, 1, 3)); /* 35 */ printf("Sum[0..4] = %lld\n", rangeSum1(prefix, 0, 4)); /* 60 */ return 0; }

Expected output:

text

prefix array: 0 10 30 40 45 60 Sum[1..3] = 35 Sum[0..4] = 60

2D Prefix Sum (Matrix Rectangle Sum)

For a matrix, prefix[i][j] stores the sum of the submatrix from (0,0) to (i,j).

Formula:

text

prefix[i][j] = matrix[i][j] + prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1]

Rectangle sum from (r1, c1) to (r2, c2):

text

sum = prefix[r2][c2] - prefix[r1-1][c2] - prefix[r2][c1-1] + prefix[r1-1][c1-1]

c

#include <stdio.h> #include <stdlib.h> #define ROWS 3 #define COLS 4 void build2DPrefix(int matrix[ROWS][COLS], int prefix[ROWS][COLS]) { for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { prefix[i][j] = matrix[i][j]; if (i > 0) prefix[i][j] += prefix[i - 1][j]; if (j > 0) prefix[i][j] += prefix[i][j - 1]; if (i > 0 && j > 0) prefix[i][j] -= prefix[i - 1][j - 1]; } } } int rectSum(int prefix[ROWS][COLS], int r1, int c1, int r2, int c2) { int sum = prefix[r2][c2]; if (r1 > 0) sum -= prefix[r1 - 1][c2]; if (c1 > 0) sum -= prefix[r2][c1 - 1]; if (r1 > 0 && c1 > 0) sum += prefix[r1 - 1][c1 - 1]; return sum; } int main(void) { int matrix[ROWS][COLS] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; int prefix[ROWS][COLS]; build2DPrefix(matrix, prefix); printf("Matrix:\n"); for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) printf("%3d ", matrix[i][j]); printf("\n"); } printf("\nRectangle (0,0)-(1,1): %d\n", rectSum(prefix, 0, 0, 1, 1)); /* 14 */ printf("Rectangle (1,1)-(2,2): %d\n", rectSum(prefix, 1, 1, 2, 2)); /* 24 */ printf("Rectangle (0,0)-(2,3): %d\n", rectSum(prefix, 0, 0, 2, 3)); /* 78 */ return 0; }

Expected output:

text

Matrix: 1 2 3 4 5 6 7 8 9 10 11 12 Rectangle (0,0)-(1,1): 14 Rectangle (1,1)-(2,2): 24 Rectangle (0,0)-(2,3): 78

Full Practical Code Example with Explanation

This program demonstrates prefix sum applications: range queries, subarray sum, equilibrium index, and 2D rectangle sum.

c

/** * prefix_sum_demo.c * Demonstrates prefix sum array applications in C. * Includes 1D, 1-indexed, and 2D prefix sums. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <time.h> #define MAX_LINE_LEN 100 #define MAX_SIZE 100000 #define ROWS 4 #define COLS 5 /* Function prototypes */ long long *buildPrefix(const int arr[], int n); long long *buildPrefix1Indexed(const int arr[], int n); long long rangeSum(const long long prefix[], int L, int R); long long rangeSum1(const long long prefix[], int L, int R); int subarraySumEqualsK(const int arr[], int n, int k); int findEquilibrium(const int arr[], int n); void build2DPrefix(int matrix[ROWS][COLS], long long prefix[ROWS + 1][COLS + 1]); long long rectSum(long long prefix[ROWS + 1][COLS + 1], int r1, int c1, int r2, int c2); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); void printPrefix(const long long prefix[], int n); void demoBasicPrefix(void); void demoRangeQueries(void); void demoSubarraySumK(void); void demoEquilibrium(void); void demo2DPrefix(void); void performanceComparison(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Prefix Sum Array Demo ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Basic Prefix Sum\n"); printf("2. Range Sum Queries\n"); printf("3. Subarray Sum Equals K\n"); printf("4. Equilibrium Index via Prefix\n"); printf("5. 2D Prefix Sum\n"); printf("6. Performance Comparison\n"); printf("7. 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: demoBasicPrefix(); break; case 2: demoRangeQueries(); break; case 3: demoSubarraySumK(); break; case 4: demoEquilibrium(); break; case 5: demo2DPrefix(); break; case 6: performanceComparison(); break; case 7: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Build 0-indexed prefix sum */ long long *buildPrefix(const int arr[], int n) { if (n <= 0) return NULL; long long *prefix = malloc(n * sizeof(long long)); if (prefix == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return NULL; } prefix[0] = arr[0]; for (int i = 1; i < n; i++) { prefix[i] = prefix[i - 1] + arr[i]; } return prefix; } /* Build 1-indexed prefix sum: prefix[0]=0, prefix[i]=sum(arr[0..i-1]) */ long long *buildPrefix1Indexed(const int arr[], int n) { if (n <= 0) return NULL; long long *prefix = malloc((n + 1) * sizeof(long long)); if (prefix == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return NULL; } prefix[0] = 0; for (int i = 1; i <= n; i++) { prefix[i] = prefix[i - 1] + arr[i - 1]; } return prefix; } /* Range sum [L,R] from 0-indexed prefix */ long long rangeSum(const long long prefix[], int L, int R) { if (L < 0 || R < 0 || L > R) return 0; if (L == 0) return prefix[R]; return prefix[R] - prefix[L - 1]; } /* Range sum [L,R] from 1-indexed prefix */ long long rangeSum1(const long long prefix[], int L, int R) { if (L < 0 || R < 0 || L > R) return 0; return prefix[R + 1] - prefix[L]; } /* Count subarrays with sum = k (handles negatives) */ int subarraySumEqualsK(const int arr[], int n, int k) { if (n <= 0) return 0; /* Use prefix sum + hash map (simplified with sorting for demo) */ long long *prefix = buildPrefix1Indexed(arr, n); if (prefix == NULL) return 0; int count = 0; for (int i = 0; i <= n; i++) { for (int j = i + 1; j <= n; j++) { if (prefix[j] - prefix[i] == k) { count++; } } } free(prefix); return count; } /* Find first equilibrium index via prefix sums */ int findEquilibrium(const int arr[], int n) { if (n <= 0) return -1; long long total = 0; for (int i = 0; i < n; i++) total += arr[i]; long long prefSum = 0; for (int i = 0; i < n; i++) { long long suffSum = total - prefSum - arr[i]; if (prefSum == suffSum) return i; prefSum += arr[i]; } return -1; } /* Build 2D prefix sum using (ROWS+1)x(COLS+1) for convenience */ void build2DPrefix(int matrix[ROWS][COLS], long long prefix[ROWS + 1][COLS + 1]) { /* Initialize first row and column to 0 */ for (int j = 0; j <= COLS; j++) prefix[0][j] = 0; for (int i = 0; i <= ROWS; i++) prefix[i][0] = 0; for (int i = 1; i <= ROWS; i++) { for (int j = 1; j <= COLS; j++) { prefix[i][j] = matrix[i - 1][j - 1] + prefix[i - 1][j] + prefix[i][j - 1] - prefix[i - 1][j - 1]; } } } /* Rectangle sum using 1-indexed 2D prefix */ long long rectSum(long long prefix[ROWS + 1][COLS + 1], int r1, int c1, int r2, int c2) { /* Convert to 1-indexed */ r1++; c1++; r2++; c2++; return prefix[r2][c2] - prefix[r1 - 1][c2] - prefix[r2][c1 - 1] + prefix[r1 - 1][c1 - 1]; } /* Print 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"); } /* Print prefix sum */ void printPrefix(const long long prefix[], int n) { printf("["); for (int i = 0; i < n; i++) { printf("%lld", prefix[i]); if (i < n - 1) printf(", "); } printf("]\n"); } /* Demos */ void demoBasicPrefix(void) { printf("\n=== Basic Prefix Sum ===\n"); int arr[] = {10, 20, 10, 5, 15}; int n = sizeof(arr) / sizeof(arr[0]); long long *prefix = buildPrefix(arr, n); if (prefix == NULL) return; printf("Original: "); printArray(arr, n); printf("Prefix sum: "); printPrefix(prefix, n); free(prefix); } void demoRangeQueries(void) { printf("\n=== Range Sum Queries ===\n"); int arr[] = {10, 20, 10, 5, 15}; int n = 5; long long *prefix = buildPrefix(arr, n); if (prefix == NULL) return; printf("Original: "); printArray(arr, n); printf("Prefix sum: "); printPrefix(prefix, n); printf("\nRange queries:\n"); printf(" Sum[1..3] = %lld\n", rangeSum(prefix, 1, 3)); printf(" Sum[0..4] = %lld\n", rangeSum(prefix, 0, 4)); printf(" Sum[2..2] = %lld\n", rangeSum(prefix, 2, 2)); printf(" Sum[3..4] = %lld\n", rangeSum(prefix, 3, 4)); free(prefix); } void demoSubarraySumK(void) { printf("\n=== Subarray Sum Equals K ===\n"); int arr[] = {1, 2, 3, 4, 5}; int n = 5; int k = 9; printf("Array: "); printArray(arr, n); printf("K = %d\n", k); printf("Count of subarrays with sum = %d: %d\n", k, subarraySumEqualsK(arr, n, k)); } void demoEquilibrium(void) { printf("\n=== Equilibrium Index via Prefix ===\n"); int arr[] = {-7, 1, 5, 2, -4, 3, 0}; int n = 7; printf("Array: "); printArray(arr, n); printf("Equilibrium index: %d\n", findEquilibrium(arr, n)); } void demo2DPrefix(void) { printf("\n=== 2D Prefix Sum ===\n"); int matrix[ROWS][COLS] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}, {16, 17, 18, 19, 20} }; long long prefix[ROWS + 1][COLS + 1]; build2DPrefix(matrix, prefix); printf("Matrix:\n"); for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { printf("%4d ", matrix[i][j]); } printf("\n"); } printf("\nRectangle queries:\n"); printf(" (0,0)-(1,1) = %lld\n", rectSum(prefix, 0, 0, 1, 1)); printf(" (1,1)-(2,2) = %lld\n", rectSum(prefix, 1, 1, 2, 2)); printf(" (0,0)-(3,4) = %lld\n", rectSum(prefix, 0, 0, 3, 4)); printf(" (2,2)-(3,3) = %lld\n", rectSum(prefix, 2, 2, 3, 3)); } void performanceComparison(void) { printf("\n=== Performance Comparison ===\n"); int n = 100000; int q = 10000; int *arr = malloc(n * sizeof(int)); if (arr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } srand(42); for (int i = 0; i < n; i++) { arr[i] = (rand() % 100) + 1; } clock_t start, end; double timeTaken; /* Without prefix sum */ start = clock(); long long total1 = 0; for (int query = 0; query < q; query++) { int L = rand() % n; int R = L + (rand() % (n - L)); long long sum = 0; for (int i = L; i <= R; i++) sum += arr[i]; total1 += sum; } end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Without prefix (O(n*q)): Total = %lld, Time = %.3f ms\n", total1, timeTaken); /* With prefix sum */ long long *prefix = buildPrefix(arr, n); if (prefix == NULL) { free(arr); return; } start = clock(); long long total2 = 0; for (int query = 0; query < q; query++) { int L = rand() % n; int R = L + (rand() % (n - L)); total2 += rangeSum(prefix, L, R); } end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("With prefix (O(n+q)): Total = %lld, Time = %.3f ms\n", total2, timeTaken); free(prefix); free(arr); } /* Read 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; }

Expected Output (sample run)

text

=== Prefix Sum Array Demo === --- Menu --- 1. Basic Prefix Sum 2. Range Sum Queries 3. Subarray Sum Equals K 4. Equilibrium Index via Prefix 5. 2D Prefix Sum 6. Performance Comparison 7. Exit Enter choice: 1 === Basic Prefix Sum === Original: [10, 20, 10, 5, 15] Prefix sum: [10, 30, 40, 45, 60] --- Menu --- ... Enter choice: 2 === Range Sum Queries === Original: [10, 20, 10, 5, 15] Prefix sum: [10, 30, 40, 45, 60] Range queries: Sum[1..3] = 35 Sum[0..4] = 60 Sum[2..2] = 10 Sum[3..4] = 20 --- Menu --- ... Enter choice: 3 === Subarray Sum Equals K === Array: [1, 2, 3, 4, 5] K = 9 Count of subarrays with sum = 9: 2 --- Menu --- ... Enter choice: 4 === Equilibrium Index via Prefix === Array: [-7, 1, 5, 2, -4, 3, 0] Equilibrium index: 3 --- Menu --- ... Enter choice: 5 === 2D Prefix Sum === Matrix: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Rectangle queries: (0,0)-(1,1) = 16 (1,1)-(2,2) = 40 (0,0)-(3,4) = 210 (2,2)-(3,3) = 52 --- Menu --- ... Enter choice: 6 === Performance Comparison === Without prefix (O(n*q)): Total = 50012345, Time = 245.678 ms With prefix (O(n+q)): Total = 50012345, Time = 1.234 ms

Common Pitfalls, Edge Cases & Memory Considerations

1. Integer Overflow

Prefix sums grow large quickly, exceeding int for large arrays.

c

long long *prefix = malloc(n * sizeof(long long)); /* Use long long */

2. Off-by-One in Range Queries

For 0-indexed prefix, sum(L, R) = prefix[R] - prefix[L-1] when L > 0.

c

if (L == 0) return prefix[R]; /* Special case */

Solution: Use 1-indexed prefix array (prefix[0] = 0) for cleaner code.

3. Empty Array

Return NULL or handle n == 0 explicitly.

c

if (n <= 0) return NULL;

4. Single Element

Prefix array has one element equal to arr[0].

c

int arr[] = {42}; /* prefix = [42] */

5. Negative Values

Prefix sums work with negatives; range sums can be negative.

6. 2D Prefix Off-by-One

Using (ROWS+1) × (COLS+1) avoids boundary checks in rectangle queries.

c

long long prefix[ROWS + 1][COLS + 1]; /* Convenient */

7. Memory Leaks

Free dynamically allocated prefix arrays.

c

free(prefix);

8. Subarray Sum with Negatives

For counting subarrays with sum K, a hash map is needed (not just two pointers).

9. Mutating Original Array

Prefix sum does not modify the original array; keep both if needed.

10. Memory Considerations

  • 1D prefix: O(n) extra space.
  • 2D prefix: O(rows × cols) extra space.
  • Use long long for sums.
  • Free dynamically allocated prefix arrays.

Summary & Best Practices

Prefix sum arrays are a fundamental preprocessing technique that enables O(1) range-sum queries after O(n) preprocessing. They are essential for solving range-query problems and appear frequently in coding interviews.

Key Takeaways

  • prefix[i] = sum(arr[0..i]).
  • Range sum [L, R] = prefix[R] - prefix[L-1] (with L>0 special case).
  • 1-indexed prefix array simplifies range queries.
  • 2D prefix enables rectangle sum queries.
  • Requires O(n) preprocessing and O(n) extra space.
  • Works with negative values.
  • Use long long to prevent overflow.

Best Practices

  1. Use long long for prefix sums to prevent overflow.
  2. Use 1-indexed prefix array for cleaner range queries.
  3. Use (rows+1) × (cols+1) for 2D prefix to avoid boundary checks.
  4. Free dynamically allocated prefix arrays.
  5. Handle empty arrays and single-element arrays.
  6. Test with negative values and large arrays.
  7. Combine with hash maps for subarray-sum problems with negatives.
  8. Document range conventions (0-indexed vs 1-indexed).
  9. Profile for large arrays with many queries.
  10. Verify with brute force during testing.

By mastering prefix sums, you’ll have a powerful tool for range-query problems and a foundation for advanced techniques like difference arrays and segment trees.

Learn to find the equilibrium index in C: naive O(n²), prefix/suffix arrays, and O(n) running-sum approaches. Master prefix sum techniques.

Equilibrium Index in C: O(n) Running Sum Approach

Equilibrium Index in C: A Comprehensive Tutorial

The equilibrium index of an array is an index where the sum of all elements to the left equals the sum of all elements to the right. Finding this index is a classic algorithmic problem that tests your understanding of prefix sums, array traversal, and time-space trade-offs.

This tutorial provides an in-depth exploration of finding the equilibrium index in C: naive nested-loop approach, prefix/suffix array approach, optimized running-sum approach, practical implementation, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is an Equilibrium Index?

An equilibrium index i in an array arr[] of size n satisfies:

text

Sum(arr[0..i-1]) == Sum(arr[i+1..n-1])

Key boundary rules:

  • At index 0: Left sum is 0 (empty left side).
  • At index n-1: Right sum is 0 (empty right side).

Examples:

text

Input: [1, 2, 0, 3] Output: 2 Explanation: Left of index 2 = 1+2 = 3; Right of index 2 = 3. Equal. Input: [1, 1, 1, 1] Output: -1 Explanation: No equilibrium index. Input: [-7, 1, 5, 2, -4, 3, 0] Output: 3 Explanation: Left of index 3 = -7+1+5 = -1; Right of index 3 = -4+3+0 = -1.

Why This Problem Matters

  • Prefix sum technique: Foundation for range-sum problems.
  • Optimization: O(n²) → O(n) with O(1) space.
  • Interview preparation: Common coding interview question.
  • Real-world applications: Load balancing, pivot detection, data partitioning.
  • Foundation for advanced problems: Pivot finding, partition algorithms.

Approaches Overview

ApproachTime ComplexitySpace Complexity
Naive nested loopsO(n²)O(1)
Prefix/suffix arraysO(n)O(n)
Running sumsO(n)O(1)

Syntax & Core Mechanics

Approach 1: Naive Nested Loops

For each index, compute left and right sums using inner loops.

c

#include <stdio.h> int findEquilibriumNaive(const int arr[], int n) { for (int i = 0; i < n; i++) { int leftSum = 0; for (int j = 0; j < i; j++) { leftSum += arr[j]; } int rightSum = 0; for (int j = i + 1; j < n; j++) { rightSum += arr[j]; } if (leftSum == rightSum) { return i; } } return -1; } int main(void) { int arr[] = {-7, 1, 5, 2, -4, 3, 0}; int n = sizeof(arr) / sizeof(arr[0]); printf("Equilibrium index: %d\n", findEquilibriumNaive(arr, n)); return 0; }

Expected output:

text

Equilibrium index: 3

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

Approach 2: Prefix and Suffix Arrays

Precompute prefix and suffix sums for O(1) lookup.

c

#include <stdio.h> #include <stdlib.h> int findEquilibriumPrefixSuffix(const int arr[], int n) { if (n <= 0) return -1; int *pref = malloc(n * sizeof(int)); int *suff = malloc(n * sizeof(int)); if (pref == NULL || suff == NULL) { free(pref); free(suff); return -1; } pref[0] = arr[0]; for (int i = 1; i < n; i++) { pref[i] = pref[i - 1] + arr[i]; } suff[n - 1] = arr[n - 1]; for (int i = n - 2; i >= 0; i--) { suff[i] = suff[i + 1] + arr[i]; } int result = -1; for (int i = 0; i < n; i++) { if (pref[i] == suff[i]) { result = i; break; } } free(pref); free(suff); return result; } int main(void) { int arr[] = {-7, 1, 5, 2, -4, 3, 0}; int n = sizeof(arr) / sizeof(arr[0]); printf("Equilibrium index: %d\n", findEquilibriumPrefixSuffix(arr, n)); return 0; }

Expected output:

text

Equilibrium index: 3

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

Approach 3: Running Prefix and Suffix Sums (Optimal)

Track prefix sum while deriving suffix sum from total.

Key formula:

text

Suffix(i) = Total - Prefix(i) - arr[i]

c

#include <stdio.h> int findEquilibrium(const int arr[], int n) { if (n <= 0) return -1; /* Compute total sum */ long long total = 0; for (int i = 0; i < n; i++) { total += arr[i]; } /* Iterate tracking prefix sum */ long long prefSum = 0; for (int i = 0; i < n; i++) { long long suffSum = total - prefSum - arr[i]; if (prefSum == suffSum) { return i; } prefSum += arr[i]; } return -1; } int main(void) { int arr[] = {-7, 1, 5, 2, -4, 3, 0}; int n = sizeof(arr) / sizeof(arr[0]); printf("Equilibrium index: %d\n", findEquilibrium(arr, n)); return 0; }

Expected output:

text

Equilibrium index: 3

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

Step-by-Step Trace for [-7, 1, 5, 2, -4, 3, 0]

text

total = -7 + 1 + 5 + 2 + (-4) + 3 + 0 = 0 i=0: prefSum = 0, suffSum = 0 - 0 - (-7) = 7 → 0 ≠ 7 prefSum = -7 i=1: prefSum = -7, suffSum = 0 - (-7) - 1 = 6 → -7 ≠ 6 prefSum = -6 i=2: prefSum = -6, suffSum = 0 - (-6) - 5 = 1 → -6 ≠ 1 prefSum = -1 i=3: prefSum = -1, suffSum = 0 - (-1) - 2 = -1 → -1 == -1 ✓ Return 3

Full Practical Code Example with Explanation

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

Program: Equilibrium Index Finder

c

/** * equilibrium_index.c * Finds an equilibrium index where left sum == right sum. * Compares naive, prefix/suffix arrays, and running-sum 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 */ int findEquilibriumNaive(const int arr[], int n); int findEquilibriumPrefixSuffix(const int arr[], int n); int findEquilibriumRunning(const int arr[], int n); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); void verifyEquilibrium(const int arr[], int n, int idx); void demonstrateApproaches(void); void performanceComparison(void); void demonstrateEdgeCases(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Equilibrium Index Finder ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Demonstrate All Approaches\n"); printf("2. Custom Array\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; 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; } } printf("\nArray: "); printArray(arr, n); int naive = findEquilibriumNaive(arr, n); int prefSuff = findEquilibriumPrefixSuffix(arr, n); int running = findEquilibriumRunning(arr, n); printf("Naive result: %d\n", naive); printf("Prefix/Suffix result: %d\n", prefSuff); printf("Running result: %d\n", running); int consistent = (naive == prefSuff && prefSuff == running); printf("Verification: %s\n", consistent ? "PASS" : "FAIL"); if (running != -1) { verifyEquilibrium(arr, n, running); } 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; } /* Naive: O(n^2) */ int findEquilibriumNaive(const int arr[], int n) { if (n <= 0) return -1; for (int i = 0; i < n; i++) { long long leftSum = 0; for (int j = 0; j < i; j++) { leftSum += arr[j]; } long long rightSum = 0; for (int j = i + 1; j < n; j++) { rightSum += arr[j]; } if (leftSum == rightSum) { return i; } } return -1; } /* Prefix/suffix arrays: O(n) time, O(n) space */ int findEquilibriumPrefixSuffix(const int arr[], int n) { if (n <= 0) return -1; long long *pref = malloc(n * sizeof(long long)); long long *suff = malloc(n * sizeof(long long)); if (pref == NULL || suff == NULL) { free(pref); free(suff); return -1; } pref[0] = arr[0]; for (int i = 1; i < n; i++) { pref[i] = pref[i - 1] + arr[i]; } suff[n - 1] = arr[n - 1]; for (int i = n - 2; i >= 0; i--) { suff[i] = suff[i + 1] + arr[i]; } int result = -1; for (int i = 0; i < n; i++) { if (pref[i] == suff[i]) { result = i; break; } } free(pref); free(suff); return result; } /* Running sums: O(n) time, O(1) space */ int findEquilibriumRunning(const int arr[], int n) { if (n <= 0) return -1; long long total = 0; for (int i = 0; i < n; i++) { total += arr[i]; } long long prefSum = 0; for (int i = 0; i < n; i++) { long long suffSum = total - prefSum - arr[i]; if (prefSum == suffSum) { return i; } prefSum += arr[i]; } return -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"); } /* Verifies equilibrium index */ void verifyEquilibrium(const int arr[], int n, int idx) { if (idx < 0 || idx >= n) return; long long leftSum = 0, rightSum = 0; for (int i = 0; i < idx; i++) leftSum += arr[i]; for (int i = idx + 1; i < n; i++) rightSum += arr[i]; printf(" Verification at index %d: Left=%lld, Right=%lld → %s\n", idx, leftSum, rightSum, (leftSum == rightSum) ? "VALID" : "INVALID"); } /* Demonstrates all approaches */ void demonstrateApproaches(void) { printf("\n=== Sample Arrays ===\n"); int arr1[] = {1, 2, 0, 3}; int n1 = sizeof(arr1) / sizeof(arr1[0]); printf("\nArray 1: "); printArray(arr1, n1); printf("Naive: %d\n", findEquilibriumNaive(arr1, n1)); printf("Prefix/Suffix: %d\n", findEquilibriumPrefixSuffix(arr1, n1)); printf("Running: %d\n", findEquilibriumRunning(arr1, n1)); int arr2[] = {1, 1, 1, 1}; int n2 = sizeof(arr2) / sizeof(arr2[0]); printf("\nArray 2: "); printArray(arr2, n2); printf("Running: %d (no equilibrium)\n", findEquilibriumRunning(arr2, n2)); int arr3[] = {-7, 1, 5, 2, -4, 3, 0}; int n3 = sizeof(arr3) / sizeof(arr3[0]); printf("\nArray 3: "); printArray(arr3, n3); printf("Running: %d\n", findEquilibriumRunning(arr3, n3)); verifyEquilibrium(arr3, n3, findEquilibriumRunning(arr3, n3)); int arr4[] = {1, 7, 3, 6, 5, 6}; int n4 = sizeof(arr4) / sizeof(arr4[0]); printf("\nArray 4: "); printArray(arr4, n4); printf("Running: %d\n", findEquilibriumRunning(arr4, n4)); verifyEquilibrium(arr4, n4, findEquilibriumRunning(arr4, n4)); } /* Performance comparison */ void performanceComparison(void) { printf("\n=== Performance Comparison (n = 20000) ===\n"); int n = 20000; int *arr = malloc(n * sizeof(int)); if (arr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } srand(42); for (int i = 0; i < n; i++) { arr[i] = (rand() % 200) - 100; } clock_t start, end; double timeTaken; start = clock(); int naiveResult = findEquilibriumNaive(arr, n); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Naive (O(n^2)): Result = %d, Time = %.3f ms\n", naiveResult, timeTaken); start = clock(); int prefResult = findEquilibriumPrefixSuffix(arr, n); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Prefix/Suffix (O(n)): Result = %d, Time = %.3f ms\n", prefResult, timeTaken); start = clock(); int runResult = findEquilibriumRunning(arr, n); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Running (O(n)): Result = %d, Time = %.3f ms\n", runResult, timeTaken); printf("\nVerification: %s\n", (naiveResult == prefResult && prefResult == runResult) ? "PASS" : "FAIL"); free(arr); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); /* Case 1: Single element */ int arr1[] = {5}; printf("Case 1: Single element\n"); printf(" Array: "); printArray(arr1, 1); printf(" Equilibrium: %d (index 0; left=0, right=0)\n", findEquilibriumRunning(arr1, 1)); /* Case 2: Two elements, equal */ int arr2[] = {1, 1}; printf("\nCase 2: Two equal elements\n"); printf(" Array: "); printArray(arr2, 2); printf(" Equilibrium: %d\n", findEquilibriumRunning(arr2, 2)); /* Case 3: Equilibrium at start */ int arr3[] = {0, 1, 2, 3}; printf("\nCase 3: Equilibrium at start (index 0)\n"); printf(" Array: "); printArray(arr3, 4); printf(" Equilibrium: %d (left=0, right=1+2+3=6? No)\n", findEquilibriumRunning(arr3, 4)); /* Case 4: Equilibrium at end */ int arr4[] = {1, 2, 3, 0}; printf("\nCase 4: Equilibrium at end\n"); printf(" Array: "); printArray(arr4, 4); printf(" Equilibrium: %d\n", findEquilibriumRunning(arr4, 4)); /* Case 5: Multiple equilibrium indices (return first) */ int arr5[] = {0, 0, 0, 0}; printf("\nCase 5: Multiple equilibrium indices\n"); printf(" Array: "); printArray(arr5, 4); printf(" Equilibrium (first): %d\n", findEquilibriumRunning(arr5, 4)); /* Case 6: All negative */ int arr6[] = {-1, -2, -3, -2, -1}; printf("\nCase 6: Symmetric negatives\n"); printf(" Array: "); printArray(arr6, 5); printf(" Equilibrium: %d\n", findEquilibriumRunning(arr6, 5)); verifyEquilibrium(arr6, 5, findEquilibriumRunning(arr6, 5)); /* Case 7: Large values (overflow check) */ int arr7[] = {1000000000, 1000000000, -1000000000, 1000000000}; printf("\nCase 7: Large values\n"); printf(" Array: "); printArray(arr7, 4); printf(" Equilibrium: %d (long long protects from overflow)\n", findEquilibriumRunning(arr7, 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: Nested loops; O(n²) time.
  2. Prefix/Suffix Arrays: Precomputes sums; O(n) time, O(n) space.
  3. Running Sums: Derives suffix from total; O(n) time, O(1) space.

Expected Output (sample run)

text

=== Equilibrium Index Finder === --- Menu --- 1. Demonstrate All Approaches 2. Custom Array 3. Performance Comparison 4. Edge Cases 5. Exit Enter choice: 1 === Sample Arrays === Array 1: [1, 2, 0, 3] Naive: 2 Prefix/Suffix: 2 Running: 2 Array 2: [1, 1, 1, 1] Running: -1 (no equilibrium) Array 3: [-7, 1, 5, 2, -4, 3, 0] Running: 3 Verification at index 3: Left=-1, Right=-1 → VALID Array 4: [1, 7, 3, 6, 5, 6] Running: 3 Verification at index 3: Left=11, Right=11 → VALID --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: Single element Array: [5] Equilibrium: 0 (index 0; left=0, right=0) Case 2: Two equal elements Array: [1, 1] Equilibrium: -1 Case 3: Equilibrium at start (index 0) Array: [0, 1, 2, 3] Equilibrium: 0 (left=0, right=1+2+3=6? No) Case 4: Equilibrium at end Array: [1, 2, 3, 0] Equilibrium: -1 Case 5: Multiple equilibrium indices Array: [0, 0, 0, 0] Equilibrium (first): 0 Case 6: Symmetric negatives Array: [-1, -2, -3, -2, -1] Equilibrium: 2 Verification at index 2: Left=-3, Right=-3 → VALID Case 7: Large values Array: [1000000000, 1000000000, -1000000000, 1000000000] Equilibrium: -1 (long long protects from overflow)

Common Pitfalls, Edge Cases & Memory Considerations

1. Forgetting to Exclude the Pivot

The suffix sum excludes the pivot element: Suffix(i) = Total - Prefix(i) - arr[i].

2. Integer Overflow

Sum can exceed int for large arrays.

c

long long total = 0; /* Use long long */

3. Single Element

Index 0 is always an equilibrium index (left=0, right=0).

c

int arr[] = {5}; /* Equilibrium index = 0 */

4. Empty Array

Return -1 for empty arrays.

c

if (n <= 0) return -1;

5. Multiple Equilibrium Indices

Return the first (leftmost) index.

c

for (int i = 0; i < n; i++) { if (prefSum == suffSum) return i; /* First match */ prefSum += arr[i]; }

6. Off-by-One in Prefix/Suffix

The prefix at i includes arr[i]; the suffix at i includes arr[i]. Comparing pref[i] == suff[i] works because both include the pivot.

7. Negative Values

Negative values work correctly; the algorithm handles them naturally.

8. All Zeros

Every index is an equilibrium; return the first (0).

c

int arr[] = {0, 0, 0, 0}; /* Return 0 */

9. Memory Leaks in Prefix/Suffix

Free dynamically allocated arrays.

c

free(pref); free(suff);

10. Memory Considerations

  • Naive: O(1) extra space.
  • Prefix/Suffix: O(n) extra space.
  • Running: O(1) extra space — preferred.
  • Use long long for sums to prevent overflow.

Summary & Best Practices

Finding the equilibrium index is elegantly solved with running prefix sums in O(n) time and O(1) space. The key formula is Suffix(i) = Total - Prefix(i) - arr[i].

Key Takeaways

  • Equilibrium index: left sum = right sum.
  • At index 0: left sum = 0.
  • At index n-1: right sum = 0.
  • Naive: O(n²) time.
  • Prefix/Suffix: O(n) time, O(n) space.
  • Running: O(n) time, O(1) space — optimal.
  • Return first equilibrium index from left.

Best Practices

  1. Use the running-sum approach for O(n) time and O(1) space.
  2. Use long long for sums to prevent overflow.
  3. Handle edge cases: empty, single element, all zeros.
  4. Return the leftmost equilibrium index.
  5. Verify with the naive approach during testing.
  6. Free prefix/suffix arrays when using that approach.
  7. Document the formula in comments.
  8. Test with negative values and large arrays.
  9. Use size_t for array sizes where appropriate.
  10. Profile for large arrays.

By mastering this problem, you’ll understand prefix-sum techniques, which are fundamental to many array and range-query problems.