CoreTechED

Loading

Learn Kadane's Algorithm in C to find maximum subarray sum in O(n) time. Master dynamic programming with naive O(n²) and optimal O(n) approaches.

Maximum Subarray Sum – Kadane’s Algorithm in C

Maximum Subarray Sum – Kadane’s Algorithm in C: A Comprehensive Tutorial

Finding the maximum subarray sum is one of the most fundamental problems in algorithm design. Given an integer array, the task is to find the contiguous subarray (containing at least one element) with the maximum sum. This problem is elegantly solved by Kadane’s Algorithm in O(n) time, making it a favorite in coding interviews and a cornerstone of dynamic programming.

This tutorial provides an in-depth exploration of Kadane’s Algorithm in C: problem understanding, naive O(n²) approach, optimized O(n) Kadane’s algorithm, tracking the actual subarray, practical implementation, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is the Maximum Subarray Sum?

Given an integer array arr[], find the contiguous subarray with the largest sum.

Examples:

text

Input: [2, 3, -8, 7, -1, 2, 3] Output: 11 Explanation: Subarray [7, -1, 2, 3] has sum 11. Input: [-2, -4] Output: -2 Explanation: Subarray [-2] has the maximum sum. Input: [5, 4, 1, 7, 8] Output: 25 Explanation: The entire array has the maximum sum.

Why This Problem Matters

  • Dynamic programming foundation: Classic DP problem.
  • Algorithm optimization: O(n²) → O(n) transformation.
  • Interview preparation: Very common coding interview question.
  • Real-world applications: Stock analysis, signal processing, bioinformatics.
  • Foundation for variants: Maximum product subarray, circular subarray.

Key Insight

For each position, we decide:

  • Extend the previous subarray if its sum is beneficial (positive contribution).
  • Start fresh from the current element if the previous sum is negative.

Recurrence:

text

maxEnding[i] = max(maxEnding[i-1] + arr[i], arr[i]) result = max(result, maxEnding[i])

Approaches Overview

ApproachTime ComplexitySpace Complexity
Naive (all subarrays)O(n²)O(1)
Kadane’s AlgorithmO(n)O(1)

Syntax & Core Mechanics

Approach 1: Naive — Iterate Over All Subarrays

Use two nested loops to compute sums of all subarrays.

c

#include <stdio.h> #include <limits.h> int maxSubarraySumNaive(const int arr[], int n) { if (n <= 0) return 0; int maxSum = arr[0]; for (int i = 0; i < n; i++) { int currSum = 0; for (int j = i; j < n; j++) { currSum += arr[j]; if (currSum > maxSum) { maxSum = currSum; } } } return maxSum; } int main(void) { int arr[] = {2, 3, -8, 7, -1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); printf("Maximum subarray sum: %d\n", maxSubarraySumNaive(arr, n)); return 0; }

Expected output:

text

Maximum subarray sum: 11

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

Approach 2: Kadane’s Algorithm

Track the maximum sum ending at each position.

c

#include <stdio.h> #include <limits.h> int maxSubarraySumKadane(const int arr[], int n) { if (n <= 0) return 0; int maxEnding = arr[0]; int result = arr[0]; for (int i = 1; i < n; i++) { /* Either extend previous subarray or start fresh */ if (maxEnding + arr[i] > arr[i]) { maxEnding = maxEnding + arr[i]; } else { maxEnding = arr[i]; } /* Update result */ if (maxEnding > result) { result = maxEnding; } } return result; } int main(void) { int arr[] = {2, 3, -8, 7, -1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); printf("Maximum subarray sum: %d\n", maxSubarraySumKadane(arr, n)); return 0; }

Expected output:

text

Maximum subarray sum: 11

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

Compact Kadane’s Formulation

c

int maxSubarraySum(const int arr[], int n) { int maxEnding = arr[0]; int result = arr[0]; for (int i = 1; i < n; i++) { maxEnding = (maxEnding + arr[i] > arr[i]) ? maxEnding + arr[i] : arr[i]; result = (result > maxEnding) ? result : maxEnding; } return result; }

Step-by-Step Trace for [2, 3, -8, 7, -1, 2, 3]

text

Init: maxEnding = 2, result = 2 i=1, x=3: maxEnding = max(2+3, 3) = 5; result = 5 i=2, x=-8: maxEnding = max(5-8, -8) = -3; result = 5 i=3, x=7: maxEnding = max(-3+7, 7) = 7; result = 7 i=4, x=-1: maxEnding = max(7-1, -1) = 6; result = 7 i=5, x=2: maxEnding = max(6+2, 2) = 8; result = 8 i=6, x=3: maxEnding = max(8+3, 3) = 11; result = 11 Final result = 11 (subarray [7, -1, 2, 3])

Tracking the Actual Subarray

To return the indices of the maximum subarray:

c

void maxSubarrayWithIndices(const int arr[], int n, int *maxSum, int *start, int *end) { int maxEnding = arr[0]; int currentStart = 0; int bestStart = 0; int bestEnd = 0; int result = arr[0]; for (int i = 1; i < n; i++) { if (maxEnding + arr[i] > arr[i]) { maxEnding = maxEnding + arr[i]; } else { maxEnding = arr[i]; currentStart = i; /* Start fresh from current */ } if (maxEnding > result) { result = maxEnding; bestStart = currentStart; bestEnd = i; } } *maxSum = result; *start = bestStart; *end = bestEnd; }

Full Practical Code Example with Explanation

This program demonstrates both approaches with verification, subarray tracking, performance comparison, and edge cases.

Program: Maximum Subarray Sum Finder

c

/** * max_subarray_sum.c * Finds maximum subarray sum using naive and Kadane's approaches. * Includes subarray tracking, verification, and edge cases. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <limits.h> #include <time.h> #define MAX_LINE_LEN 100 #define MAX_SIZE 100000 /* Function prototypes */ int maxSubarrayNaive(const int arr[], int n); int maxSubarrayKadane(const int arr[], int n); void maxSubarrayWithIndices(const int arr[], int n, int *maxSum, int *start, int *end); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); void printSubarray(const int arr[], int start, int end); void demonstrateApproaches(void); void performanceComparison(void); void demonstrateEdgeCases(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Maximum Subarray Sum (Kadane's Algorithm) ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Demonstrate with Sample Arrays\n"); printf("2. Enter 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; int maxSum, start, end; 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 = maxSubarrayNaive(arr, n); int kadane = maxSubarrayKadane(arr, n); printf("Naive result: %d\n", naive); printf("Kadane result: %d\n", kadane); printf("Verification: %s\n", (naive == kadane) ? "PASS" : "FAIL"); maxSubarrayWithIndices(arr, n, &maxSum, &start, &end); printf("Maximum subarray: "); printSubarray(arr, start, end); 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 maxSubarrayNaive(const int arr[], int n) { if (n <= 0) return 0; int maxSum = arr[0]; for (int i = 0; i < n; i++) { int currSum = 0; for (int j = i; j < n; j++) { currSum += arr[j]; if (currSum > maxSum) { maxSum = currSum; } } } return maxSum; } /* Kadane's: O(n) */ int maxSubarrayKadane(const int arr[], int n) { if (n <= 0) return 0; int maxEnding = arr[0]; int result = arr[0]; for (int i = 1; i < n; i++) { if (maxEnding + arr[i] > arr[i]) { maxEnding = maxEnding + arr[i]; } else { maxEnding = arr[i]; } if (maxEnding > result) { result = maxEnding; } } return result; } /* Kadane's with subarray indices */ void maxSubarrayWithIndices(const int arr[], int n, int *maxSum, int *start, int *end) { int maxEnding = arr[0]; int currentStart = 0; int bestStart = 0; int bestEnd = 0; int result = arr[0]; for (int i = 1; i < n; i++) { if (maxEnding + arr[i] > arr[i]) { maxEnding = maxEnding + arr[i]; } else { maxEnding = arr[i]; currentStart = i; } if (maxEnding > result) { result = maxEnding; bestStart = currentStart; bestEnd = i; } } *maxSum = result; *start = bestStart; *end = bestEnd; } /* 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"); } /* Prints subarray */ void printSubarray(const int arr[], int start, int end) { printf("["); for (int i = start; i <= end; i++) { printf("%d", arr[i]); if (i < end) printf(", "); } printf("]\n"); } /* Demonstrates both approaches */ void demonstrateApproaches(void) { printf("\n=== Sample Arrays ===\n"); int arr1[] = {2, 3, -8, 7, -1, 2, 3}; int n1 = sizeof(arr1) / sizeof(arr1[0]); printf("\nArray 1: "); printArray(arr1, n1); printf("Naive: %d\n", maxSubarrayNaive(arr1, n1)); printf("Kadane: %d\n", maxSubarrayKadane(arr1, n1)); int arr2[] = {-2, -4}; int n2 = sizeof(arr2) / sizeof(arr2[0]); printf("\nArray 2: "); printArray(arr2, n2); printf("Naive: %d\n", maxSubarrayNaive(arr2, n2)); printf("Kadane: %d\n", maxSubarrayKadane(arr2, n2)); int arr3[] = {5, 4, 1, 7, 8}; int n3 = sizeof(arr3) / sizeof(arr3[0]); printf("\nArray 3: "); printArray(arr3, n3); printf("Naive: %d\n", maxSubarrayNaive(arr3, n3)); printf("Kadane: %d\n", maxSubarrayKadane(arr3, n3)); int arr4[] = {-1, -2, -3, -4}; int n4 = sizeof(arr4) / sizeof(arr4[0]); printf("\nArray 4 (all negative): "); printArray(arr4, n4); printf("Kadane: %d\n", maxSubarrayKadane(arr4, n4)); /* Subarray tracking demonstration */ int maxSum, start, end; maxSubarrayWithIndices(arr1, n1, &maxSum, &start, &end); printf("\nArray 1 maximum subarray (sum = %d): ", maxSum); printSubarray(arr1, start, end); } /* 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; /* Range -100..99 */ } clock_t start, end; double timeTaken; start = clock(); int naiveResult = maxSubarrayNaive(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 kadaneResult = maxSubarrayKadane(arr, n); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Kadane (O(n)): Result = %d, Time = %.3f ms\n", kadaneResult, timeTaken); printf("\nVerification: %s\n", (naiveResult == kadaneResult) ? "PASS" : "FAIL"); free(arr); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); /* Case 1: Single positive element */ int arr1[] = {42}; printf("Case 1: Single positive element\n"); printf(" Array: "); printArray(arr1, 1); printf(" Kadane: %d\n", maxSubarrayKadane(arr1, 1)); /* Case 2: Single negative element */ int arr2[] = {-5}; printf("\nCase 2: Single negative element\n"); printf(" Array: "); printArray(arr2, 1); printf(" Kadane: %d\n", maxSubarrayKadane(arr2, 1)); /* Case 3: All positive */ int arr3[] = {1, 2, 3, 4, 5}; printf("\nCase 3: All positive\n"); printf(" Array: "); printArray(arr3, 5); printf(" Kadane: %d (entire array)\n", maxSubarrayKadane(arr3, 5)); /* Case 4: All negative */ int arr4[] = {-3, -1, -4, -1, -5}; printf("\nCase 4: All negative\n"); printf(" Array: "); printArray(arr4, 5); printf(" Kadane: %d (single max element)\n", maxSubarrayKadane(arr4, 5)); /* Case 5: Alternating */ int arr5[] = {1, -2, 3, -4, 5, -6, 7}; printf("\nCase 5: Alternating\n"); printf(" Array: "); printArray(arr5, 7); printf(" Kadane: %d\n", maxSubarrayKadane(arr5, 7)); /* Case 6: Zeros included */ int arr6[] = {0, -1, 0, 2, 0}; printf("\nCase 6: Zeros included\n"); printf(" Array: "); printArray(arr6, 5); printf(" Kadane: %d\n", maxSubarrayKadane(arr6, 5)); /* Case 7: Large values (overflow check) */ int arr7[] = {1000000000, 1000000000, -1, 1000000000}; printf("\nCase 7: Large values\n"); printf(" Array: "); printArray(arr7, 4); printf(" Kadane: %d (watch for overflow)\n", maxSubarrayKadane(arr7, 4)); /* Case 8: Subarray tracking */ int arr8[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int maxSum, start, end; maxSubarrayWithIndices(arr8, 9, &maxSum, &start, &end); printf("\nCase 8: Subarray tracking\n"); printf(" Array: "); printArray(arr8, 9); printf(" Max subarray: "); printSubarray(arr8, start, end); printf(" Sum: %d\n", maxSum); } /* 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: Enumerates all subarrays; O(n²).
  2. Kadane: Tracks maximum sum ending at each position; O(n).
  3. Subarray Tracking: Extends Kadane’s to record start/end indices.
  4. Overflow Protection: Uses int for typical cases; long long for large sums.

Expected Output (sample run)

text

=== Maximum Subarray Sum (Kadane's Algorithm) === --- Menu --- 1. Demonstrate with Sample Arrays 2. Enter Custom Array 3. Performance Comparison 4. Edge Cases 5. Exit Enter choice: 1 === Sample Arrays === Array 1: [2, 3, -8, 7, -1, 2, 3] Naive: 11 Kadane: 11 Array 2: [-2, -4] Naive: -2 Kadane: -2 Array 3: [5, 4, 1, 7, 8] Naive: 25 Kadane: 25 Array 4 (all negative): [-1, -2, -3, -4] Kadane: -1 Array 1 maximum subarray (sum = 11): [7, -1, 2, 3] --- Menu --- ... Enter choice: 3 === Performance Comparison (n = 20000) === Naive (O(n^2)): Result = 12345, Time = 234.567 ms Kadane (O(n)): Result = 12345, Time = 0.089 ms Verification: PASS --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: Single positive element Array: [42] Kadane: 42 Case 2: Single negative element Array: [-5] Kadane: -5 Case 3: All positive Array: [1, 2, 3, 4, 5] Kadane: 15 (entire array) Case 4: All negative Array: [-3, -1, -4, -1, -5] Kadane: -1 (single max element) Case 5: Alternating Array: [1, -2, 3, -4, 5, -6, 7] Kadane: 7 Case 6: Zeros included Array: [0, -1, 0, 2, 0] Kadane: 2 Case 7: Large values Array: [1000000000, 1000000000, -1, 1000000000] Kadane: 2000000000 (watch for overflow) Case 8: Subarray tracking Array: [-2, 1, -3, 4, -1, 2, 1, -5, 4] Max subarray: [4, -1, 2, 1] Sum: 6

Common Pitfalls, Edge Cases & Memory Considerations

1. Empty Array

Return 0 or handle gracefully.

c

if (n <= 0) return 0;

2. All Negative Elements

Kadane’s algorithm correctly returns the maximum single element.

c

int arr[] = {-2, -4}; /* Result: -2 */

Important: Initialize maxEnding and result to arr[0], not 0.

3. Integer Overflow

For large values, the sum may exceed int.

c

long long maxEnding = arr[0]; /* Use long long for large sums */

Solution: Use long long for the result and intermediate sums.

4. Initialization Error

Initializing maxEnding = 0 breaks all-negative arrays.

c

int maxEnding = 0; /* BUG: returns 0 for all-negative arrays */ int maxEnding = arr[0]; /* Correct */

5. Off-by-One in Subarray Tracking

Record currentStart only when starting fresh.

c

if (maxEnding + arr[i] > arr[i]) { maxEnding += arr[i]; } else { maxEnding = arr[i]; currentStart = i; }

6. Confusing maxEnding and result

  • maxEnding: maximum sum ending at current index.
  • result: maximum sum found so far.

7. Using >= vs >

Using >= may change which subarray is returned when ties exist.

c

if (maxEnding > result) { /* Strict: keeps first occurrence */ result = maxEnding; }

8. Modifying the Input

Kadane’s algorithm does not modify the input.

9. Non-Contiguous Subarrays

Kadane’s only works for contiguous subarrays. For non-contiguous, sum all positive elements.

10. Memory Considerations

  • Both approaches use O(1) extra space.
  • No dynamic allocation required.
  • Use long long for very large arrays.
  • Subarray tracking adds constant space.

Summary & Best Practices

Kadane’s Algorithm is the optimal solution for the maximum subarray sum problem, achieving O(n) time and O(1) space. The key insight is deciding at each element whether to extend the previous subarray or start fresh.

Key Takeaways

  • Maximum subarray sum: contiguous subarray with largest sum.
  • Naive: O(n²) time, O(1) space.
  • Kadane: O(n) time, O(1) space.
  • Initialize with arr[0], not 0.
  • Recurrence: maxEnding = max(maxEnding + arr[i], arr[i]).
  • Works correctly with all-negative arrays.

Best Practices

  1. Use Kadane’s Algorithm for O(n) time and O(1) space.
  2. Initialize to arr[0] to handle all-negative arrays.
  3. Use long long for large arrays to prevent overflow.
  4. Track start/end indices when the actual subarray is needed.
  5. Handle edge cases: empty, single element, all-negative.
  6. Verify with naive approach during testing.
  7. Document the recurrence in comments.
  8. Test with zeros and alternating signs.
  9. Use > (not >=) to keep the first occurrence.
  10. Profile for large arrays.

By mastering Kadane’s Algorithm, you’ll have a powerful tool for solving dynamic programming and array problems.

Learn to generate all subarrays in C using iterative and recursive approaches. Master subarray algorithms with practical examples and memory management.

Generating All Subarrays in C: Complete Guide

Generating All Subarrays in C: A Comprehensive Tutorial

Generating all subarrays of an array is a fundamental algorithmic problem that appears frequently in coding interviews and competitive programming. A subarray is a contiguous sequence of elements within an array. Understanding how to generate all subarrays efficiently is essential for solving problems involving ranges, sliding windows, and dynamic programming.

This tutorial provides an in-depth exploration of generating all subarrays in C: problem understanding, iterative and recursive approaches, practical implementation, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is a Subarray?

A subarray is a contiguous portion of an array. For an array of size n, there are n × (n + 1) / 2 possible subarrays.

Example:

text

Array: [1, 2, 3] Subarrays: [1], [1, 2], [1, 2, 3] [2], [2, 3] [3] Total: 3 × 4 / 2 = 6 subarrays

Subarray vs. Subsequence vs. Subset

ConceptDefinitionOrder Preserved?Contiguous?
SubarrayContiguous portionYesYes
SubsequenceNot necessarily contiguousYesNo
SubsetAny selectionNoNo

Why Generate All Subarrays?

  • Maximum subarray sum: Kadane’s algorithm foundation.
  • Sliding window problems: Subarray-based optimizations.
  • Range queries: Prefix sums and segment trees.
  • Interview preparation: Common coding problem.
  • Data analysis: Contiguous segment processing.

Number of Subarrays

For an array of size n:

  • Number of subarrays = n × (n + 1) / 2
  • For n = 3: 6 subarrays
  • For n = 4: 10 subarrays
  • For n = 5: 15 subarrays

Syntax & Core Mechanics

Approach 1: Iterative (Triple Nested Loops)

The most straightforward approach: fix starting index i, ending index j, and print elements from i to j.

Time complexity: O(n³)
Space complexity: O(1) for printing, O(n) for storing

c

#include <stdio.h> void printAllSubarrays(const int arr[], int n) { /* Fix starting index */ for (int i = 0; i < n; i++) { /* Fix ending index */ for (int j = i; j < n; j++) { /* Print subarray from i to j */ printf("["); for (int k = i; k <= j; k++) { printf("%d", arr[k]); if (k < j) printf(", "); } printf("]"); if (!(i == n - 1 && j == n - 1)) printf(", "); } } printf("\n"); } int main(void) { int arr[] = {1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); printf("All subarrays:\n"); printAllSubarrays(arr, n); return 0; }

Expected output:

text

All subarrays: [1], [1, 2], [1, 2, 3], [2], [2, 3], [3]

Approach 2: Iterative (Optimized — Two Loops)

Instead of a third loop, extend the subarray incrementally.

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

c

#include <stdio.h> void printAllSubarraysOptimized(const int arr[], int n) { for (int i = 0; i < n; i++) { printf("["); for (int j = i; j < n; j++) { printf("%d", arr[j]); if (j < n - 1) printf(", "); /* Print current subarray */ printf("]"); if (j < n - 1 || i < n - 1) printf(", "); if (j < n - 1) printf("["); } } printf("\n"); } int main(void) { int arr[] = {1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); printf("All subarrays (optimized):\n"); printAllSubarraysOptimized(arr, n); return 0; }

Better implementation:

c

#include <stdio.h> void printAllSubarraysTwoLoops(const int arr[], int n) { int first = 1; for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { if (!first) printf(", "); first = 0; printf("["); for (int k = i; k <= j; k++) { printf("%d", arr[k]); if (k < j) printf(", "); } printf("]"); } } printf("\n"); } int main(void) { int arr[] = {1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); printAllSubarraysTwoLoops(arr, n); return 0; }

Expected output:

text

[1], [1, 2], [1, 2, 3], [2], [2, 3], [3]

Approach 3: Recursive

Use recursion to generate subarrays starting from each index.

c

#include <stdio.h> /* Recursively extend subarray from start to end */ void generateFromStart(const int arr[], int n, int start, int end, int current[], int currentSize, int *first) { if (end == n) return; /* Add current element to subarray */ current[currentSize] = arr[end]; /* Print current subarray */ if (!*first) printf(", "); *first = 0; printf("["); for (int i = 0; i <= currentSize; i++) { printf("%d", current[i]); if (i < currentSize) printf(", "); } printf("]"); /* Recursively extend */ generateFromStart(arr, n, start, end + 1, current, currentSize + 1, first); } /* Generate subarrays for each starting index */ void generateAll(const int arr[], int n, int start, int *first) { if (start == n) return; int current[100]; /* Buffer for current subarray */ generateFromStart(arr, n, start, start, current, 0, first); generateAll(arr, n, start + 1, first); } int main(void) { int arr[] = {1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); int first = 1; printf("All subarrays (recursive):\n"); generateAll(arr, n, 0, &first); printf("\n"); return 0; }

Expected output:

text

All subarrays (recursive): [1], [1, 2], [1, 2, 3], [2], [2, 3], [3]

Full Practical Code Example with Explanation

This program demonstrates all subarray generation approaches with a complete implementation using dynamic memory for storing subarrays.

Program: Subarray Generator and Analyzer

c

/** * subarray_generator.c * Generates all subarrays using iterative and recursive approaches. * Includes storage, display, and analysis of subarrays. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MAX_LINE_LEN 100 #define MAX_SUBARRAYS 1000 /* Structure to hold a subarray */ typedef struct { int *elements; int size; int startIndex; int endIndex; int sum; } Subarray; /* Structure to hold all subarrays */ typedef struct { Subarray *items; int count; int capacity; } SubarrayCollection; /* Function prototypes */ SubarrayCollection *createCollection(int capacity); void freeCollection(SubarrayCollection *collection); int addSubarray(SubarrayCollection *collection, const int arr[], int start, int end); void generateIterative(const int arr[], int n, SubarrayCollection *collection); void generateRecursiveHelper(const int arr[], int n, int start, int end, SubarrayCollection *collection); void generateRecursive(const int arr[], int n, SubarrayCollection *collection); void displaySubarrays(const SubarrayCollection *collection); void analyzeSubarrays(const SubarrayCollection *collection); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); void displaySubarray(const Subarray *sub); int main(void) { int choice; char buffer[MAX_LINE_LEN]; int arr[MAX_LINE_LEN]; int n = 0; printf("=== Subarray Generator and Analyzer ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Enter Array\n"); printf("2. Generate All Subarrays (Iterative)\n"); printf("3. Generate All Subarrays (Recursive)\n"); printf("4. Analyze Subarrays\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: { int size; if (readInteger("Enter array size (1-20): ", &size) != 0) { fprintf(stderr, "Invalid size.\n"); break; } if (size < 1 || size > 20) { printf("Size must be 1-20.\n"); break; } printf("Enter %d elements:\n", size); for (int i = 0; i < size; i++) { char prompt[50]; snprintf(prompt, sizeof(prompt), " Element %d: ", i); if (readInteger(prompt, &arr[i]) != 0) { fprintf(stderr, "Invalid input.\n"); size = i; break; } } n = size; printf("Array: "); printArray(arr, n); break; } case 2: { if (n == 0) { printf("Please enter an array first.\n"); break; } int maxSubarrays = n * (n + 1) / 2 + 1; SubarrayCollection *collection = createCollection(maxSubarrays); if (collection == NULL) { fprintf(stderr, "Memory allocation failed.\n"); break; } generateIterative(arr, n, collection); printf("\n=== All Subarrays (Iterative) ===\n"); displaySubarrays(collection); freeCollection(collection); break; } case 3: { if (n == 0) { printf("Please enter an array first.\n"); break; } int maxSubarrays = n * (n + 1) / 2 + 1; SubarrayCollection *collection = createCollection(maxSubarrays); if (collection == NULL) { fprintf(stderr, "Memory allocation failed.\n"); break; } generateRecursive(arr, n, collection); printf("\n=== All Subarrays (Recursive) ===\n"); displaySubarrays(collection); freeCollection(collection); break; } case 4: { if (n == 0) { printf("Please enter an array first.\n"); break; } int maxSubarrays = n * (n + 1) / 2 + 1; SubarrayCollection *collection = createCollection(maxSubarrays); if (collection == NULL) { fprintf(stderr, "Memory allocation failed.\n"); break; } generateIterative(arr, n, collection); analyzeSubarrays(collection); freeCollection(collection); break; } case 5: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Creates a collection with initial capacity */ SubarrayCollection *createCollection(int capacity) { SubarrayCollection *collection = malloc(sizeof(SubarrayCollection)); if (collection == NULL) return NULL; collection->items = malloc(capacity * sizeof(Subarray)); if (collection->items == NULL) { free(collection); return NULL; } collection->count = 0; collection->capacity = capacity; return collection; } /* Frees the collection */ void freeCollection(SubarrayCollection *collection) { if (collection == NULL) return; for (int i = 0; i < collection->count; i++) { free(collection->items[i].elements); } free(collection->items); free(collection); } /* Adds a subarray to the collection */ int addSubarray(SubarrayCollection *collection, const int arr[], int start, int end) { if (collection->count >= collection->capacity) { return -1; /* Collection full */ } int size = end - start + 1; Subarray *sub = &collection->items[collection->count]; sub->elements = malloc(size * sizeof(int)); if (sub->elements == NULL) { return -1; } sub->size = size; sub->startIndex = start; sub->endIndex = end; sub->sum = 0; for (int i = 0; i < size; i++) { sub->elements[i] = arr[start + i]; sub->sum += sub->elements[i]; } collection->count++; return 0; } /* Generates all subarrays iteratively */ void generateIterative(const int arr[], int n, SubarrayCollection *collection) { for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { addSubarray(collection, arr, i, j); } } } /* Recursive helper to generate subarrays from a fixed start */ void generateRecursiveHelper(const int arr[], int n, int start, int end, SubarrayCollection *collection) { if (end >= n) return; addSubarray(collection, arr, start, end); generateRecursiveHelper(arr, n, start, end + 1, collection); } /* Generates all subarrays recursively */ void generateRecursive(const int arr[], int n, SubarrayCollection *collection) { if (n == 0) return; for (int start = 0; start < n; start++) { generateRecursiveHelper(arr, n, start, start, collection); } } /* Displays all subarrays */ void displaySubarrays(const SubarrayCollection *collection) { for (int i = 0; i < collection->count; i++) { displaySubarray(&collection->items[i]); if (i < collection->count - 1) printf(", "); } printf("\n\nTotal: %d subarrays\n", collection->count); } /* Displays a single subarray */ void displaySubarray(const Subarray *sub) { printf("["); for (int i = 0; i < sub->size; i++) { printf("%d", sub->elements[i]); if (i < sub->size - 1) printf(", "); } printf("]"); } /* Analyzes subarrays (min/max sum, etc.) */ void analyzeSubarrays(const SubarrayCollection *collection) { if (collection->count == 0) { printf("No subarrays to analyze.\n"); return; } int maxSum = collection->items[0].sum; int minSum = collection->items[0].sum; int maxSumIndex = 0; int minSumIndex = 0; for (int i = 1; i < collection->count; i++) { if (collection->items[i].sum > maxSum) { maxSum = collection->items[i].sum; maxSumIndex = i; } if (collection->items[i].sum < minSum) { minSum = collection->items[i].sum; minSumIndex = i; } } printf("\n=== Subarray Analysis ===\n"); printf("Total subarrays: %d\n", collection->count); printf("\nMaximum sum subarray: "); displaySubarray(&collection->items[maxSumIndex]); printf(" (sum = %d)\n", maxSum); printf("Minimum sum subarray: "); displaySubarray(&collection->items[minSumIndex]); printf(" (sum = %d)\n", minSum); } /* Prints an 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"); } /* 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 Subarray Generation

  1. Iterative: Triple nested loops (or two loops with incremental printing).
  2. Recursive: Recursion extends subarray from each start index.
  3. Storage: Subarray and SubarrayCollection structures store subarrays.
  4. Analysis: Finds max/min sum subarrays.
  5. Memory management: Frees all allocated memory.

Expected Output (sample run)

text

=== Subarray Generator and Analyzer === --- Menu --- 1. Enter Array 2. Generate All Subarrays (Iterative) 3. Generate All Subarrays (Recursive) 4. Analyze Subarrays 5. Exit Enter choice: 1 Enter array size (1-20): 3 Enter 3 elements: Element 0: 1 Element 1: 2 Element 2: 3 Array: [1, 2, 3] --- Menu --- ... Enter choice: 2 === All Subarrays (Iterative) === [1], [1, 2], [1, 2, 3], [2], [2, 3], [3] Total: 6 subarrays --- Menu --- ... Enter choice: 4 === Subarray Analysis === Total subarrays: 6 Maximum sum subarray: [1, 2, 3] (sum = 6) Minimum sum subarray: [1] (sum = 1)

Common Pitfalls, Edge Cases & Memory Considerations

1. Empty Array

An empty array has no subarrays.

c

int arr[] = {}; printAllSubarrays(arr, 0); // No output

Solution: Handle n == 0 separately.

2. Single Element

An array with one element has exactly one subarray.

c

int arr[] = {5}; // Subarrays: [5]

3. Duplicate Elements

Duplicate values produce distinct subarrays (by position).

c

int arr[] = {1, 1}; // Subarrays: [1], [1, 1], [1] (3 subarrays, all distinct by position)

4. Large Arrays

Number of subarrays grows as O(n²). For n = 1000, there are 500,500 subarrays.

c

int arr[1000]; /* 500,500 subarrays - may exhaust memory if stored */

Solution: Print subarrays incrementally instead of storing all.

5. Memory Leaks

Storing subarrays with malloc requires freeing each one.

c

for (int i = 0; i < count; i++) { free(collection->items[i].elements); } free(collection->items); free(collection);

Solution: Always free dynamically allocated memory.

6. Buffer Overflow

Using fixed-size buffers for subarrays may overflow for large arrays.

c

int current[100]; // BUG if n > 100

Solution: Allocate dynamically or use larger buffers.

7. Off-by-One Errors

Incorrect loop bounds produce missing or extra subarrays.

c

for (int j = i; j < n; j++) // Correct: j < n for (int j = i; j <= n; j++) // BUG: j <= n accesses out of bounds

Solution: Use j < n for inclusive ending index.

8. Integer Overflow in Sum

Summing large subarrays may overflow.

c

int sum = 0; for (int i = start; i <= end; i++) { sum += arr[i]; // May overflow for large values }

Solution: Use long long for sum calculations.

9. Recursion Depth

Recursive generation may cause stack overflow for large arrays.

Solution: Use iterative approach for large arrays.

10. Memory Considerations

  • Number of subarrays = n × (n + 1) / 2.
  • Each subarray of average length n/3 uses O(n) memory.
  • Total memory for storing all subarrays: O(n³).
  • For large n, print subarrays instead of storing.

Summary & Best Practices

Generating all subarrays is a fundamental operation with applications in algorithm design, data analysis, and interview preparation. Iterative approaches are simple; recursive approaches are elegant but less efficient.

Key Takeaways

  • Subarrays are contiguous portions of an array.
  • Number of subarrays = n × (n + 1) / 2.
  • Iterative approach: O(n³) or O(n²) time.
  • Recursive approach: O(n³) time, O(n) stack space.
  • Subarrays must be distinct by position, not value.
  • Store only when needed; print incrementally for large arrays.

Best Practices

  1. Use iterative approach for simplicity and efficiency.
  2. Use recursion only for educational purposes or specific requirements.
  3. Avoid storing all subarrays for large arrays (memory).
  4. Free dynamically allocated memory for subarrays.
  5. Handle empty arrays and single-element arrays.
  6. Use long long for sum calculations to prevent overflow.
  7. Use size_t for array sizes.
  8. Print incrementally for large arrays.
  9. Validate array size before generation.
  10. Test with edge cases: empty, single element, duplicates, negatives.

By mastering subarray generation, you’ll build a foundation for solving more complex array and range-based problems.