CoreTechED

Loading

Master 7 binary search applications in C: sqrt, first/last position, count 1's, unbounded search, rotated arrays, and aggressive cows. Complete code examples.

7 Binary Search Applications in C: Complete Guide

Binary Search Applications in C: A Comprehensive Tutorial

Binary search is one of the most powerful algorithmic techniques in computer science. Beyond simple searching, it powers a wide range of problems—from mathematical computations to optimization and rotated array searching. This tutorial covers seven important binary search applications in C, each with complete, portable implementations.

The seven topics are:

  1. Square Root of an Integer
  2. First and Last Positions in a Sorted Array
  3. Count 1’s in a Sorted Binary Array
  4. Unbounded Binary Search
  5. Minimum in a Sorted Rotated Array
  6. Search in a Sorted Rotated Array
  7. Aggressive Cows (Optimization Problem)

Conceptual Introduction & Fundamentals

The Binary Search Template

All binary search variants share a common structure:

c

int low = 0, high = n - 1; while (low <= high) { int mid = low + (high - low) / 2; /* Overflow-safe */ if (condition(mid)) { /* Narrow search to one half */ } else { /* Narrow search to other half */ } } return answer;

Key Prerequisites

RequirementDescription
Sorted/MonotonicThe search space must be monotonic
Random AccessO(1) access to any element
Overflow-safe midUse low + (high - low) / 2
TerminationEnsure low or high always moves

Applications Overview

ProblemTime ComplexitySpace Complexity
Square RootO(log n)O(1)
First/Last PositionO(log n)O(1)
Count 1’s in Binary ArrayO(log n)O(1)
Unbounded SearchO(log n)O(1)
Minimum in Rotated ArrayO(log n)O(1)
Search in Rotated ArrayO(log n)O(1)
Aggressive CowsO(n log n)O(1)

Syntax & Core Mechanics

1. Square Root of an Integer

Problem: Find the integer square root of n (floor of √n) without using built-in sqrt().

Idea: Binary search on the answer [0, n]. Find the largest x such that x² ≤ n.

c

#include <stdio.h> long long integerSqrt(long long n) { if (n < 0) return -1; /* Undefined for negatives */ if (n == 0 || n == 1) return n; long long low = 1, high = n, result = 1; while (low <= high) { long long mid = low + (high - low) / 2; /* Avoid overflow: use division instead of mid*mid */ if (mid <= n / mid) { result = mid; low = mid + 1; /* Try larger */ } else { high = mid - 1; } } return result; } int main(void) { long long tests[] = {0, 1, 4, 8, 16, 25, 100, 999999999}; int count = sizeof(tests) / sizeof(tests[0]); for (int i = 0; i < count; i++) { printf("sqrt(%lld) = %lld\n", tests[i], integerSqrt(tests[i])); } return 0; }

Expected output:

text

sqrt(0) = 0 sqrt(1) = 1 sqrt(4) = 2 sqrt(8) = 2 sqrt(16) = 4 sqrt(25) = 5 sqrt(100) = 10 sqrt(999999999) = 31622

Key insight: Use mid <= n / mid instead of mid * mid <= n to avoid overflow.

2. First and Last Positions in a Sorted Array

Problem: Find the first and last occurrence of a target in a sorted array (with duplicates).

Idea: Two modified binary searches — one biased left (first), one biased right (last).

c

#include <stdio.h> int findFirst(const int arr[], int n, int target) { int low = 0, high = n - 1, result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; high = mid - 1; /* Bias left */ } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; } int findLast(const int arr[], int n, int target) { int low = 0, high = n - 1, result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; low = mid + 1; /* Bias right */ } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; } int main(void) { int arr[] = {1, 2, 2, 2, 3, 4, 5}; int n = sizeof(arr) / sizeof(arr[0]); int target = 2; int first = findFirst(arr, n, target); int last = findLast(arr, n, target); if (first == -1) { printf("%d not found\n", target); } else { printf("First occurrence of %d: %d\n", target, first); printf("Last occurrence of %d: %d\n", target, last); printf("Count: %d\n", last - first + 1); } return 0; }

Expected output:

text

First occurrence of 2: 1 Last occurrence of 2: 3 Count: 3

3. Count 1’s in a Sorted Binary Array

Problem: Count the number of 1’s in a sorted array containing only 0’s and 1’s.

Idea: Find the first occurrence of 1. Count = n - firstOneIndex.

c

#include <stdio.h> int countOnes(const int arr[], int n) { int low = 0, high = n - 1, firstOne = n; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == 1) { firstOne = mid; high = mid - 1; /* Search left for earlier 1 */ } else { low = mid + 1; /* 0: search right */ } } return n - firstOne; } int main(void) { int arr1[] = {0, 0, 0, 1, 1, 1, 1}; int arr2[] = {0, 0, 0, 0}; int arr3[] = {1, 1, 1, 1}; int arr4[] = {0}; printf("Test 1: %d\n", countOnes(arr1, 7)); printf("Test 2: %d\n", countOnes(arr2, 4)); printf("Test 3: %d\n", countOnes(arr3, 4)); printf("Test 4: %d\n", countOnes(arr4, 1)); return 0; }

Expected output:

text

Test 1: 4 Test 2: 0 Test 3: 4 Test 4: 0

4. Unbounded Binary Search

Problem: Search for a target in an array of infinite size (or unknown size), where all elements are sorted.

Idea: Double the index high until arr[high] >= target, then perform standard binary search.

c

#include <stdio.h> /* Simulated infinite array via a function */ int getValue(int idx) { static int data[] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29}; int size = sizeof(data) / sizeof(data[0]); if (idx >= size) return 1000000; /* Sentinel */ return data[idx]; } int unboundedSearch(int target) { if (getValue(0) == target) return 0; int high = 1; while (getValue(high) < target) { if (high > 1000000) return -1; /* Safety bound */ high *= 2; } int low = high / 2; while (low <= high) { int mid = low + (high - low) / 2; int val = getValue(mid); if (val == target) return mid; if (val < target) low = mid + 1; else high = mid - 1; } return -1; } int main(void) { int targets[] = {1, 9, 29, 30}; int count = sizeof(targets) / sizeof(targets[0]); for (int i = 0; i < count; i++) { int result = unboundedSearch(targets[i]); if (result == -1) printf("Target %d: Not found\n", targets[i]); else printf("Target %d: Index %d\n", targets[i], result); } return 0; }

Expected output:

text

Target 1: Index 0 Target 9: Index 4 Target 29: Index 14 Target 30: Not found

5. Minimum in a Sorted Rotated Array

Problem: Find the minimum element in a sorted array rotated at an unknown pivot.

Idea: Compare arr[mid] with arr[high]; narrow the search.

c

#include <stdio.h> int findMinRotated(const int arr[], int n) { if (n <= 0) return -1; int low = 0, high = n - 1; while (low < high) { /* If the range is already sorted, minimum is at low */ if (arr[low] < arr[high]) return arr[low]; int mid = low + (high - low) / 2; if (arr[mid] > arr[high]) { low = mid + 1; /* Minimum in right half */ } else { high = mid; /* Minimum at mid or left half */ } } return arr[low]; } int main(void) { int arr1[] = {5, 6, 1, 2, 3, 4}; int arr2[] = {3, 1, 2}; int arr3[] = {4, 2, 3}; int arr4[] = {1, 2, 3, 4}; printf("Min 1: %d\n", findMinRotated(arr1, 6)); printf("Min 2: %d\n", findMinRotated(arr2, 3)); printf("Min 3: %d\n", findMinRotated(arr3, 3)); printf("Min 4: %d\n", findMinRotated(arr4, 4)); return 0; }

Expected output:

text

Min 1: 1 Min 2: 1 Min 3: 2 Min 4: 1

6. Search in a Sorted Rotated Array

Problem: Search for a target in a sorted rotated array.

Idea: One half is always sorted. Determine which half and search accordingly.

c

#include <stdio.h> int searchRotated(const int arr[], int n, int target) { int low = 0, high = n - 1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) return mid; /* Left half is sorted */ if (arr[low] <= arr[mid]) { if (target >= arr[low] && target < arr[mid]) { high = mid - 1; } else { low = mid + 1; } } /* Right half is sorted */ else { if (target > arr[mid] && target <= arr[high]) { low = mid + 1; } else { high = mid - 1; } } } return -1; } int main(void) { int arr[] = {5, 6, 7, 8, 9, 1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); int targets[] = {5, 1, 3, 8, 10, 0}; int count = sizeof(targets) / sizeof(targets[0]); for (int i = 0; i < count; i++) { int result = searchRotated(arr, n, targets[i]); if (result == -1) printf("Target %d: Not found\n", targets[i]); else printf("Target %d: Index %d\n", targets[i], result); } return 0; }

Expected output:

text

Target 5: Index 0 Target 1: Index 5 Target 3: Index 7 Target 8: Index 3 Target 10: Not found Target 0: Not found

7. Aggressive Cows (Optimization)

Problem: Place k cows in n stalls (given positions) so that the minimum distance between any two cows is maximized.

Idea: Binary search on the answer (distance). For each candidate distance, check if k cows can be placed with at least that gap.

c

#include <stdio.h> #include <stdlib.h> int compareInts(const void *a, const void *b) { int int_a = *(const int *)a; int int_b = *(const int *)b; return (int_a > int_b) - (int_a < int_b); } /* Check if k cows can be placed with minimum distance >= dist */ int canPlace(const int stalls[], int n, int k, int dist) { int count = 1; /* Place first cow at stalls[0] */ int lastPos = stalls[0]; for (int i = 1; i < n; i++) { if (stalls[i] - lastPos >= dist) { count++; lastPos = stalls[i]; if (count >= k) return 1; } } return 0; } int aggressiveCows(int stalls[], int n, int k) { if (k > n) return -1; qsort(stalls, n, sizeof(int), compareInts); int low = 1; int high = stalls[n - 1] - stalls[0]; int result = 0; while (low <= high) { int mid = low + (high - low) / 2; if (canPlace(stalls, n, k, mid)) { result = mid; /* Try larger distance */ low = mid + 1; } else { high = mid - 1; /* Try smaller distance */ } } return result; } int main(void) { int stalls1[] = {1, 2, 4, 8, 9}; int stalls2[] = {1, 2, 3, 4, 5}; printf("Aggressive Cows (k=3, stalls=[1,2,4,8,9]): %d\n", aggressiveCows(stalls1, 5, 3)); printf("Aggressive Cows (k=3, stalls=[1,2,3,4,5]): %d\n", aggressiveCows(stalls2, 5, 3)); printf("Aggressive Cows (k=2, stalls=[1,2,4,8,9]): %d\n", aggressiveCows(stalls1, 5, 2)); return 0; }

Expected output:

text

Aggressive Cows (k=3, stalls=[1,2,4,8,9]): 3 Aggressive Cows (k=3, stalls=[1,2,3,4,5]): 2 Aggressive Cows (k=2, stalls=[1,2,4,8,9]): 8

Full Practical Code Example with Explanation

This program combines all seven binary search applications into a single interactive tool.

c

/** * binary_search_applications.c * Demonstrates seven binary search applications in C. * Covers sqrt, first/last occurrence, count 1's, unbounded search, * rotated array min/search, and aggressive cows. */ #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 */ long long integerSqrt(long long n); int findFirst(const int arr[], int n, int target); int findLast(const int arr[], int n, int target); int countOnes(const int arr[], int n); int unboundedSearch(int target); int findMinRotated(const int arr[], int n); int searchRotated(const int arr[], int n, int target); int aggressiveCows(int stalls[], int n, int k); int compareInts(const void *a, const void *b); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); void demoSquareRoot(void); void demoFirstLast(void); void demoCountOnes(void); void demoUnbounded(void); void demoRotatedMin(void); void demoRotatedSearch(void); void demoAggressiveCows(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Binary Search Applications Demo ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Square Root of Integer\n"); printf("2. First and Last Position\n"); printf("3. Count 1's in Binary Array\n"); printf("4. Unbounded Binary Search\n"); printf("5. Minimum in Rotated Array\n"); printf("6. Search in Rotated Array\n"); printf("7. Aggressive Cows\n"); printf("8. 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: demoSquareRoot(); break; case 2: demoFirstLast(); break; case 3: demoCountOnes(); break; case 4: demoUnbounded(); break; case 5: demoRotatedMin(); break; case 6: demoRotatedSearch(); break; case 7: demoAggressiveCows(); break; case 8: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* 1. Integer square root */ long long integerSqrt(long long n) { if (n < 0) return -1; if (n == 0 || n == 1) return n; long long low = 1, high = n, result = 1; while (low <= high) { long long mid = low + (high - low) / 2; if (mid <= n / mid) { result = mid; low = mid + 1; } else { high = mid - 1; } } return result; } /* 2. First occurrence */ int findFirst(const int arr[], int n, int target) { int low = 0, high = n - 1, result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; high = mid - 1; } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; } /* 2. Last occurrence */ int findLast(const int arr[], int n, int target) { int low = 0, high = n - 1, result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; low = mid + 1; } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; } /* 3. Count 1's */ int countOnes(const int arr[], int n) { int low = 0, high = n - 1, firstOne = n; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == 1) { firstOne = mid; high = mid - 1; } else { low = mid + 1; } } return n - firstOne; } /* 4. Unbounded search */ int getValue(int idx) { static int data[] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29}; int size = sizeof(data) / sizeof(data[0]); if (idx < 0 || idx >= size) return 1000000; return data[idx]; } int unboundedSearch(int target) { if (getValue(0) == target) return 0; int high = 1; while (getValue(high) < target) { if (high > 1000000) return -1; high *= 2; } int low = high / 2; while (low <= high) { int mid = low + (high - low) / 2; int val = getValue(mid); if (val == target) return mid; if (val < target) low = mid + 1; else high = mid - 1; } return -1; } /* 5. Minimum in rotated array */ int findMinRotated(const int arr[], int n) { if (n <= 0) return -1; int low = 0, high = n - 1; while (low < high) { if (arr[low] < arr[high]) return arr[low]; int mid = low + (high - low) / 2; if (arr[mid] > arr[high]) low = mid + 1; else high = mid; } return arr[low]; } /* 6. Search in rotated array */ int searchRotated(const int arr[], int n, int target) { int low = 0, high = n - 1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) return mid; if (arr[low] <= arr[mid]) { if (target >= arr[low] && target < arr[mid]) high = mid - 1; else low = mid + 1; } else { if (target > arr[mid] && target <= arr[high]) low = mid + 1; else high = mid - 1; } } return -1; } /* 7. Aggressive Cows */ int compareInts(const void *a, const void *b) { int int_a = *(const int *)a; int int_b = *(const int *)b; return (int_a > int_b) - (int_a < int_b); } int canPlace(const int stalls[], int n, int k, int dist) { int count = 1; int lastPos = stalls[0]; for (int i = 1; i < n; i++) { if (stalls[i] - lastPos >= dist) { count++; lastPos = stalls[i]; if (count >= k) return 1; } } return 0; } int aggressiveCows(int stalls[], int n, int k) { if (k > n) return -1; qsort(stalls, n, sizeof(int), compareInts); int low = 1; int high = stalls[n - 1] - stalls[0]; int result = 0; while (low <= high) { int mid = low + (high - low) / 2; if (canPlace(stalls, n, k, mid)) { result = mid; low = mid + 1; } else { high = mid - 1; } } return result; } /* 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"); } /* Demos */ void demoSquareRoot(void) { printf("\n=== Integer Square Root ===\n"); long long tests[] = {0, 1, 4, 8, 16, 25, 100, 999999999}; int count = sizeof(tests) / sizeof(tests[0]); for (int i = 0; i < count; i++) { printf("sqrt(%lld) = %lld\n", tests[i], integerSqrt(tests[i])); } } void demoFirstLast(void) { printf("\n=== First and Last Position ===\n"); int arr[] = {1, 2, 2, 2, 3, 4, 5}; int n = sizeof(arr) / sizeof(arr[0]); printf("Array: "); printArray(arr, n); int target = 2; int first = findFirst(arr, n, target); int last = findLast(arr, n, target); if (first == -1) printf("%d not found\n", target); else printf("First: %d, Last: %d, Count: %d\n", first, last, last - first + 1); } void demoCountOnes(void) { printf("\n=== Count 1's in Sorted Binary Array ===\n"); int arr1[] = {0, 0, 0, 1, 1, 1, 1}; int arr2[] = {0, 0, 0, 0}; int arr3[] = {1, 1, 1, 1}; printf("arr1: "); printArray(arr1, 7); printf(" Count 1's: %d\n", countOnes(arr1, 7)); printf("arr2: "); printArray(arr2, 4); printf(" Count 1's: %d\n", countOnes(arr2, 4)); printf("arr3: "); printArray(arr3, 4); printf(" Count 1's: %d\n", countOnes(arr3, 4)); } void demoUnbounded(void) { printf("\n=== Unbounded Binary Search ===\n"); int targets[] = {1, 9, 29, 30}; int count = sizeof(targets) / sizeof(targets[0]); for (int i = 0; i < count; i++) { int r = unboundedSearch(targets[i]); if (r == -1) printf("Target %d: Not found\n", targets[i]); else printf("Target %d: Index %d\n", targets[i], r); } } void demoRotatedMin(void) { printf("\n=== Minimum in Sorted Rotated Array ===\n"); int arr1[] = {5, 6, 1, 2, 3, 4}; int arr2[] = {3, 1, 2}; int arr3[] = {4, 2, 3}; int arr4[] = {1, 2, 3, 4}; printf("arr1: "); printArray(arr1, 6); printf(" Min: %d\n", findMinRotated(arr1, 6)); printf("arr2: "); printArray(arr2, 3); printf(" Min: %d\n", findMinRotated(arr2, 3)); printf("arr3: "); printArray(arr3, 3); printf(" Min: %d\n", findMinRotated(arr3, 3)); printf("arr4: "); printArray(arr4, 4); printf(" Min: %d\n", findMinRotated(arr4, 4)); } void demoRotatedSearch(void) { printf("\n=== Search in Sorted Rotated Array ===\n"); int arr[] = {5, 6, 7, 8, 9, 1, 2, 3}; int n = sizeof(arr) / sizeof(arr[0]); printf("Array: "); printArray(arr, n); int targets[] = {5, 1, 3, 8, 10, 0}; int count = sizeof(targets) / sizeof(targets[0]); for (int i = 0; i < count; i++) { int r = searchRotated(arr, n, targets[i]); if (r == -1) printf("Target %d: Not found\n", targets[i]); else printf("Target %d: Index %d\n", targets[i], r); } } void demoAggressiveCows(void) { printf("\n=== Aggressive Cows ===\n"); int stalls1[] = {1, 2, 4, 8, 9}; int stalls2[] = {1, 2, 3, 4, 5}; printf("stalls=[1,2,4,8,9], k=3 → %d\n", aggressiveCows(stalls1, 5, 3)); printf("stalls=[1,2,3,4,5], k=3 → %d\n", aggressiveCows(stalls2, 5, 3)); printf("stalls=[1,2,4,8,9], k=2 → %d\n", aggressiveCows(stalls1, 5, 2)); } /* 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

=== Binary Search Applications Demo === --- Menu --- 1. Square Root of Integer 2. First and Last Position 3. Count 1's in Binary Array 4. Unbounded Binary Search 5. Minimum in Rotated Array 6. Search in Rotated Array 7. Aggressive Cows 8. Exit Enter choice: 1 === Integer Square Root === sqrt(0) = 0 sqrt(1) = 1 sqrt(4) = 2 sqrt(8) = 2 sqrt(16) = 4 sqrt(25) = 5 sqrt(100) = 10 sqrt(999999999) = 31622

Common Pitfalls, Edge Cases & Memory Considerations

1. Integer Overflow in mid * mid

Use mid <= n / mid instead of mid * mid <= n.

2. Infinite Loops from Non-Progress

Always update low = mid + 1 or high = mid - 1.

3. Off-by-One in Rotated Arrays

Use high = mid (not mid - 1) when arr[mid] < arr[high] to keep minimum in range.

4. Missing Check for Sorted Subarray

In rotated search, always check which half is sorted first.

5. Unbounded Search Safety

Include an upper bound to prevent infinite doubling.

6. Aggressive Cows: Sorting Required

Always sort stall positions first.

7. First/Last Occurrence Initialization

Initialize result = -1 and update only on match.

8. Empty Arrays

Return sentinel values (-1) for empty inputs.

9. Duplicates in Rotated Arrays

The minimum in rotated array with duplicates can be ambiguous; linear fallback may be needed.

10. Memory Considerations

  • All seven algorithms use O(1) extra space.
  • No dynamic allocation required.
  • Use long long for integer sqrt to avoid overflow.

Summary & Best Practices

Binary search is a versatile technique that extends far beyond simple searching. The seven applications in this tutorial demonstrate its power across mathematical, structural, and optimization problems.

Key Takeaways

ProblemKey Insight
Square RootBinary search on answer with overflow-safe comparison
First/Last PositionBias search direction based on requirement
Count 1’sFind first 1; count = n – index
Unbounded SearchExponential doubling then binary search
Min in RotatedCompare mid with high
Search in RotatedOne half always sorted
Aggressive CowsBinary search on distance with feasibility check

Best Practices

  1. Use low + (high - low) / 2 to avoid overflow.
  2. Ensure monotonicity before applying binary search.
  3. Check both halves’ sorted status in rotated arrays.
  4. Bias first/last occurrence searches appropriately.
  5. Include safety bounds in unbounded search.
  6. Sort input for aggressive cows.
  7. Use long long for large computations (square root).
  8. Handle edge cases: empty, single element, duplicates.
  9. Verify with brute force during testing.
  10. Profile for large inputs.

By mastering these seven applications, you’ll be well-equipped to solve a wide range of binary search problems in competitive programming and technical interviews.

Learn Binary Search in C: iterative, recursive, first/last occurrence, and descending variants. Master O(log n) searching with examples and edge cases.

Binary Search in C: Iterative & Recursive Guide

Binary Search in C: A Comprehensive Tutorial

Binary Search is a fundamental searching algorithm that operates on a sorted or monotonic search space, repeatedly dividing it into halves to find a target value or optimal answer in logarithmic time O(log N). It is one of the most important algorithms in computer science and forms the foundation for many advanced algorithms and data structures.

This tutorial provides an in-depth exploration of Binary Search in C: algorithm principles, iterative and recursive implementations, complexity analysis, practical applications, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is Binary Search?

Binary Search is a divide-and-conquer algorithm that finds the position of a target value within a sorted array. It compares the target with the middle element of the array and eliminates half of the search space based on the comparison.

Why Binary Search Matters

  • Efficiency: O(log n) time — exponentially faster than linear search.
  • Foundation: Basis for many algorithms and data structures (BST, B-trees).
  • Wide applications: Databases, debugging, network routing, machine learning.
  • Interview preparation: Very common coding interview question.
  • Optimization problems: Used in “binary search on answer” problems.

Prerequisites

  1. Sorted data: The array must be sorted (ascending or descending).
  2. Random access: Any element can be accessed in O(1) time (arrays qualify).
  3. Monotonic property: The search space must be monotonic.

How Binary Search Works

For a sorted array and target x:

  1. Find the middle index mid.
  2. If arr[mid] == x, return mid.
  3. If arr[mid] < x, search the right half.
  4. If arr[mid] > x, search the left half.
  5. Repeat until found or search space is empty.

Example:

text

Array: [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] Target: 23 Step 1: mid = 4, arr[4] = 16 < 23 → search right [23, 38, 56, 72, 91] Step 2: mid = 7, arr[7] = 56 > 23 → search left [23, 38] Step 3: mid = 5, arr[5] = 23 == 23 → Found at index 5

Complexity Analysis

CaseTime Complexity
BestO(1)
AverageO(log n)
WorstO(log n)

Space:

  • Iterative: O(1)
  • Recursive: O(log n) (call stack)

Syntax & Core Mechanics

Iterative Binary Search

c

#include <stdio.h> int binarySearchIterative(const int arr[], int n, int target) { int low = 0; int high = n - 1; while (low <= high) { /* Avoid overflow: low + (high - low) / 2 */ int mid = low + (high - low) / 2; if (arr[mid] == target) { return mid; } if (arr[mid] < target) { low = mid + 1; /* Search right half */ } else { high = mid - 1; /* Search left half */ } } return -1; /* Not found */ } int main(void) { int arr[] = {2, 3, 4, 10, 40}; int n = sizeof(arr) / sizeof(arr[0]); int target = 10; int result = binarySearchIterative(arr, n, target); if (result == -1) { printf("Element is not present in array\n"); } else { printf("Element is present at index %d\n", result); } return 0; }

Expected output:

text

Element is present at index 3

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

Recursive Binary Search

c

#include <stdio.h> int binarySearchRecursive(const int arr[], int low, int high, int target) { if (low > high) { return -1; /* Not found */ } int mid = low + (high - low) / 2; if (arr[mid] == target) { return mid; } if (arr[mid] > target) { return binarySearchRecursive(arr, low, mid - 1, target); } return binarySearchRecursive(arr, mid + 1, high, target); } int main(void) { int arr[] = {2, 3, 4, 10, 40}; int n = sizeof(arr) / sizeof(arr[0]); int target = 10; int result = binarySearchRecursive(arr, 0, n - 1, target); if (result == -1) { printf("Element is not present in array\n"); } else { printf("Element is present at index %d\n", result); } return 0; }

Expected output:

text

Element is present at index 3

Time: O(log n), Space: O(log n) due to recursion stack

Finding First and Last Occurrence

For arrays with duplicates, binary search can be modified to find first/last occurrence.

c

/* Find first occurrence of target */ int findFirst(const int arr[], int n, int target) { int low = 0, high = n - 1; int result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; high = mid - 1; /* Search left for earlier occurrence */ } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; } /* Find last occurrence of target */ int findLast(const int arr[], int n, int target) { int low = 0, high = n - 1; int result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; low = mid + 1; /* Search right for later occurrence */ } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; }

Full Practical Code Example with Explanation

This program demonstrates all binary search variants with verification, performance comparison, and edge cases.

Program: Binary Search Complete Demo

c

/** * binary_search_demo.c * Demonstrates iterative, recursive, and variant binary search approaches. * Includes first/last occurrence, verification, and edge cases. */ #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 binarySearchIterative(const int arr[], int n, int target); int binarySearchRecursive(const int arr[], int low, int high, int target); int findFirstOccurrence(const int arr[], int n, int target); int findLastOccurrence(const int arr[], int n, int target); int findClosest(const int arr[], int n, int target); int binarySearchDescending(const int arr[], int n, int target); int compareInts(const void *a, const void *b); 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("=== Binary Search Complete Demo ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Demonstrate All Variants\n"); printf("2. Search in 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, target; 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 (will be sorted):\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; } } qsort(arr, n, sizeof(int), compareInts); printf("\nSorted array: "); printArray(arr, n); if (readInteger("Enter target: ", &target) != 0) { fprintf(stderr, "Invalid target.\n"); break; } int iter = binarySearchIterative(arr, n, target); int rec = binarySearchRecursive(arr, 0, n - 1, target); int first = findFirstOccurrence(arr, n, target); int last = findLastOccurrence(arr, n, target); printf("Iterative: %d\n", iter); printf("Recursive: %d\n", rec); printf("First occurrence: %d\n", first); printf("Last occurrence: %d\n", last); if (iter != -1) { printf("Verification: %s\n", (iter == rec && iter == first) ? "PASS" : "FAIL"); } 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; } /* Iterative binary search */ int binarySearchIterative(const int arr[], int n, int target) { int low = 0; int high = n - 1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) return mid; if (arr[mid] < target) low = mid + 1; else high = mid - 1; } return -1; } /* Recursive binary search */ int binarySearchRecursive(const int arr[], int low, int high, int target) { if (low > high) return -1; int mid = low + (high - low) / 2; if (arr[mid] == target) return mid; if (arr[mid] > target) return binarySearchRecursive(arr, low, mid - 1, target); return binarySearchRecursive(arr, mid + 1, high, target); } /* Find first occurrence */ int findFirstOccurrence(const int arr[], int n, int target) { int low = 0, high = n - 1; int result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; high = mid - 1; } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; } /* Find last occurrence */ int findLastOccurrence(const int arr[], int n, int target) { int low = 0, high = n - 1; int result = -1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { result = mid; low = mid + 1; } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } return result; } /* Find closest element to target */ int findClosest(const int arr[], int n, int target) { if (n <= 0) return -1; int low = 0, high = n - 1; if (target <= arr[0]) return 0; if (target >= arr[n - 1]) return n - 1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) return mid; if (arr[mid] < target) { if (mid + 1 < n && arr[mid + 1] > target) { return (target - arr[mid] < arr[mid + 1] - target) ? mid : mid + 1; } low = mid + 1; } else { if (mid - 1 >= 0 && arr[mid - 1] < target) { return (target - arr[mid - 1] < arr[mid] - target) ? mid - 1 : mid; } high = mid - 1; } } return -1; } /* Binary search for descending sorted array */ int binarySearchDescending(const int arr[], int n, int target) { int low = 0, high = n - 1; while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) return mid; if (arr[mid] > target) low = mid + 1; else high = mid - 1; } return -1; } /* Comparison function */ int compareInts(const void *a, const void *b) { int int_a = *(const int *)a; int int_b = *(const int *)b; return (int_a > int_b) - (int_a < int_b); } /* 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 variants */ void demonstrateApproaches(void) { printf("\n=== Binary Search Variants ===\n"); int arr[] = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91}; int n = sizeof(arr) / sizeof(arr[0]); printf("\nArray: "); printArray(arr, n); int targets[] = {2, 23, 91, 100}; int numTargets = sizeof(targets) / sizeof(targets[0]); for (int i = 0; i < numTargets; i++) { int t = targets[i]; printf("\nTarget %d:\n", t); printf(" Iterative: %d\n", binarySearchIterative(arr, n, t)); printf(" Recursive: %d\n", binarySearchRecursive(arr, 0, n - 1, t)); printf(" Closest: %d\n", findClosest(arr, n, t)); } /* First/last occurrence with duplicates */ int dupArr[] = {1, 2, 2, 2, 3, 4, 5}; int dupN = sizeof(dupArr) / sizeof(dupArr[0]); printf("\nArray with duplicates: "); printArray(dupArr, dupN); printf("Target 2:\n"); printf(" First: %d\n", findFirstOccurrence(dupArr, dupN, 2)); printf(" Last: %d\n", findLastOccurrence(dupArr, dupN, 2)); /* Descending array */ int descArr[] = {100, 90, 80, 70, 60, 50}; int descN = sizeof(descArr) / sizeof(descArr[0]); printf("\nDescending array: "); printArray(descArr, descN); printf("Target 70:\n"); printf(" Index: %d\n", binarySearchDescending(descArr, descN, 70)); } /* Performance comparison */ void performanceComparison(void) { printf("\n=== Performance Comparison (n = 10000000) ===\n"); int n = 10000000; int *arr = malloc(n * sizeof(int)); if (arr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } for (int i = 0; i < n; i++) { arr[i] = i * 2; } int target = arr[n - 1]; /* Worst case: last element */ clock_t start, end; double timeTaken; /* Linear search for comparison */ start = clock(); int linearResult = -1; for (int i = 0; i < n; i++) { if (arr[i] == target) { linearResult = i; break; } } end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Linear search (O(n)): Result = %d, Time = %.3f ms\n", linearResult, timeTaken); /* Binary search */ start = clock(); int binResult = binarySearchIterative(arr, n, target); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Binary search (O(log n)): Result = %d, Time = %.3f ms\n", binResult, timeTaken); printf("\nVerification: %s\n", (linearResult == binResult) ? "PASS" : "FAIL"); free(arr); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); /* Case 1: Empty array */ int arr1[] = {}; printf("Case 1: Empty array\n"); printf(" Result: %d\n", binarySearchIterative(arr1, 0, 5)); /* Case 2: Single element, found */ int arr2[] = {42}; printf("\nCase 2: Single element, found\n"); printf(" Array: "); printArray(arr2, 1); printf(" Result: %d\n", binarySearchIterative(arr2, 1, 42)); /* Case 3: Single element, not found */ int arr3[] = {42}; printf("\nCase 3: Single element, not found\n"); printf(" Array: "); printArray(arr3, 1); printf(" Result: %d\n", binarySearchIterative(arr3, 1, 10)); /* Case 4: Target at start */ int arr4[] = {1, 2, 3, 4, 5}; printf("\nCase 4: Target at start\n"); printf(" Array: "); printArray(arr4, 5); printf(" Result: %d\n", binarySearchIterative(arr4, 5, 1)); /* Case 5: Target at end */ int arr5[] = {1, 2, 3, 4, 5}; printf("\nCase 5: Target at end\n"); printf(" Array: "); printArray(arr5, 5); printf(" Result: %d\n", binarySearchIterative(arr5, 5, 5)); /* Case 6: All same elements */ int arr6[] = {5, 5, 5, 5, 5}; printf("\nCase 6: All same elements\n"); printf(" Array: "); printArray(arr6, 5); printf(" First: %d, Last: %d\n", findFirstOccurrence(arr6, 5, 5), findLastOccurrence(arr6, 5, 5)); /* Case 7: Negative numbers */ int arr7[] = {-10, -5, 0, 5, 10}; printf("\nCase 7: Negative numbers\n"); printf(" Array: "); printArray(arr7, 5); printf(" Target -5: %d\n", binarySearchIterative(arr7, 5, -5)); /* Case 8: Large array overflow check */ int arr8[] = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91}; printf("\nCase 8: Overflow-safe mid calculation\n"); printf(" Array: "); printArray(arr8, 10); printf(" Target 56: %d\n", binarySearchIterative(arr8, 10, 56)); } /* 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 Binary Search Variants

  1. Iterative: Standard while loop; O(log n) time, O(1) space.
  2. Recursive: Self-calling function; O(log n) time, O(log n) space.
  3. First Occurrence: Modified to find leftmost match.
  4. Last Occurrence: Modified to find rightmost match.
  5. Closest: Finds nearest element to target.
  6. Descending: Adapted for descending arrays.

Expected Output (sample run)

text

=== Binary Search Complete Demo === --- Menu --- 1. Demonstrate All Variants 2. Search in Custom Array 3. Performance Comparison 4. Edge Cases 5. Exit Enter choice: 1 === Binary Search Variants === Array: [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] Target 2: Iterative: 0 Recursive: 0 Closest: 0 Target 23: Iterative: 5 Recursive: 5 Closest: 5 Target 91: Iterative: 9 Recursive: 9 Closest: 9 Target 100: Iterative: -1 Recursive: -1 Closest: 9 Array with duplicates: [1, 2, 2, 2, 3, 4, 5] Target 2: First: 1 Last: 3 Descending array: [100, 90, 80, 70, 60, 50] Target 70: Index: 3 --- Menu --- ... Enter choice: 3 === Performance Comparison (n = 10000000) === Linear search (O(n)): Result = 9999999, Time = 45.234 ms Binary search (O(log n)): Result = 9999999, Time = 0.002 ms Verification: PASS --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: Empty array Result: -1 Case 2: Single element, found Array: [42] Result: 0 Case 3: Single element, not found Array: [42] Result: -1 Case 4: Target at start Array: [1, 2, 3, 4, 5] Result: 0 Case 5: Target at end Array: [1, 2, 3, 4, 5] Result: 4 Case 6: All same elements Array: [5, 5, 5, 5, 5] First: 0, Last: 4 Case 7: Negative numbers Array: [-10, -5, 0, 5, 10] Target -5: 1 Case 8: Overflow-safe mid calculation Array: [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] Target 56: 7

Common Pitfalls, Edge Cases & Memory Considerations

1. Integer Overflow in Mid Calculation

(low + high) / 2 can overflow for large arrays.

c

int mid = (low + high) / 2; /* BUG: potential overflow */ int mid = low + (high - low) / 2; /* Correct */

2. Infinite Loop from Wrong Update

Forgetting to update low or high causes infinite loops.

c

if (arr[mid] < target) low = mid; /* BUG: no progress */ if (arr[mid] < target) low = mid + 1; /* Correct */

3. Off-by-One in Loop Condition

Using low < high vs low <= high affects termination.

c

while (low <= high) { ... } /* Correct for standard binary search */

4. Unsorted Array

Binary search requires a sorted array; results are undefined otherwise.

Solution: Sort the array first with qsort.

5. Empty Array

Return -1 for empty arrays.

c

if (n <= 0) return -1;

6. Duplicates

Standard binary search returns any occurrence; use variants for first/last.

7. Recursion Depth

Recursive binary search uses O(log n) stack space — safe for typical arrays.

8. Descending Arrays

Adapt comparison logic for descending order.

c

if (arr[mid] > target) low = mid + 1; else high = mid - 1;

9. Search on Monotonic Functions

Binary search works on any monotonic search space, not just arrays.

10. Memory Considerations

  • Iterative: O(1) extra space.
  • Recursive: O(log n) stack space.
  • No dynamic allocation required.
  • Use int for indices; size_t for very large arrays.

Summary & Best Practices

Binary Search is a fundamental O(log n) algorithm for searching in sorted arrays. It is widely applicable and forms the foundation for many advanced algorithms.

Key Takeaways

  • Requires sorted data and random access.
  • Iterative: O(log n) time, O(1) space.
  • Recursive: O(log n) time, O(log n) space.
  • Use low + (high - low) / 2 to avoid overflow.
  • Variants: first/last occurrence, closest match, descending.
  • Applications: databases, debugging, routing, ML tuning.

Best Practices

  1. Use the iterative version for O(1) space.
  2. Use low + (high - low) / 2 to prevent overflow.
  3. Ensure the array is sorted before searching.
  4. Update low = mid + 1 or high = mid - 1 for progress.
  5. Handle edge cases: empty, single element, target not found.
  6. Use variants for first/last occurrence when duplicates exist.
  7. Use qsort for sorting before binary search.
  8. Test with negative numbers and boundary targets.
  9. Consider binary search on answer for optimization problems.
  10. Profile for large arrays.

By mastering Binary Search, you’ll have a powerful tool for efficient searching and be prepared for advanced algorithms and data structures.

Learn to find the minimum in a sorted and rotated array in C: linear O(n) and binary search O(log n) approaches. Master rotated array algorithms.

Minimum in Sorted and Rotated Array in C

Minimum in a Sorted and Rotated Array in C: A Comprehensive Tutorial

Given a sorted array of distinct elements that has been rotated at some unknown point, the task is to find the minimum element. This is a classic binary search problem that tests your understanding of rotated arrays and efficient search techniques. The minimum element marks the rotation point and is the only element smaller than its predecessor.

This tutorial provides an in-depth exploration of finding the minimum in a sorted and rotated array in C: naive linear search, optimized binary search, practical implementation, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is a Sorted and Rotated Array?

A sorted array that has been rotated at some pivot point. The array consists of two sorted subarrays.

Example:

text

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

The minimum element 1 is at the rotation point.

Key Insight

In a sorted and rotated array:

  • The array is divided into two sorted parts.
  • The minimum element is the only element smaller than its predecessor.
  • The minimum element marks the rotation pivot.

Why This Problem Matters

  • Binary search variants: Classic rotated array problem.
  • Search optimization: O(n) to O(log n) transformation.
  • Algorithm design: Teaches divide-and-conquer.
  • Interview preparation: Common coding interview question.
  • Rotated array operations: Foundation for searching in rotated arrays.

Approaches Overview

ApproachTime ComplexitySpace Complexity
Linear searchO(n)O(1)
Binary searchO(log n)O(1)

Syntax & Core Mechanics

Approach 1: Linear Search (Naive)

Traverse the entire array to find the minimum.

c

#include <stdio.h> int findMinLinear(const int arr[], int n) { if (n <= 0) return -1; int min = arr[0]; for (int i = 1; i < n; i++) { if (arr[i] < min) { min = arr[i]; } } return min; } int main(void) { int arr[] = {5, 6, 1, 2, 3, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("Minimum: %d\n", findMinLinear(arr, n)); return 0; }

Expected output:

text

Minimum: 1

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

Approach 2: Binary Search (Optimized)

Use binary search to find the minimum in O(log n).

Steps:

  1. If arr[low] < arr[high], the range is sorted; return arr[low].
  2. Compute mid.
  3. If arr[mid] > arr[high], minimum is in the right half; low = mid + 1.
  4. Otherwise, minimum is at mid or left; high = mid.
  5. When low == high, that index holds the minimum.

c

#include <stdio.h> int findMinBinary(const int arr[], int n) { if (n <= 0) return -1; int low = 0; int high = n - 1; while (low < high) { /* If current range is sorted, first element is minimum */ if (arr[low] < arr[high]) { return arr[low]; } int mid = low + (high - low) / 2; /* Minimum lies in right half */ if (arr[mid] > arr[high]) { low = mid + 1; } /* Minimum lies at mid or left half */ else { high = mid; } } /* low == high points to minimum element */ return arr[low]; } int main(void) { int arr[] = {5, 6, 1, 2, 3, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("Minimum: %d\n", findMinBinary(arr, n)); return 0; }

Expected output:

text

Minimum: 1

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

Alternative: Finding Minimum Index

c

#include <stdio.h> int findMinIndex(const int arr[], int n) { if (n <= 0) return -1; int low = 0; int high = n - 1; while (low < high) { if (arr[low] < arr[high]) { return low; } int mid = low + (high - low) / 2; if (arr[mid] > arr[high]) { low = mid + 1; } else { high = mid; } } return low; } int main(void) { int arr[] = {5, 6, 1, 2, 3, 4}; int n = sizeof(arr) / sizeof(arr[0]); int index = findMinIndex(arr, n); printf("Minimum: %d at index %d\n", arr[index], index); return 0; }

Expected output:

text

Minimum: 1 at index 2

Full Practical Code Example with Explanation

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

Program: Minimum in Sorted and Rotated Array

c

/** * min_sorted_rotated.c * Finds the minimum element in a sorted and rotated array. * Compares linear and binary search approaches. */ #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 findMinLinear(const int arr[], int n); int findMinBinary(const int arr[], int n); int findMinIndex(const int arr[], int n); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); void rotateArray(int arr[], int n, int d); void demonstrateApproaches(void); void performanceComparison(void); void demonstrateEdgeCases(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Minimum in Sorted and Rotated Array ===\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; 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 (sorted then rotated):\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 linear = findMinLinear(arr, n); int binary = findMinBinary(arr, n); printf("Minimum (linear): %d\n", linear); printf("Minimum (binary): %d\n", binary); printf("Verification: %s\n", (linear == binary) ? "PASS" : "FAIL"); 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; } /* Linear search: O(n) */ int findMinLinear(const int arr[], int n) { if (n <= 0) return INT_MAX; int min = arr[0]; for (int i = 1; i < n; i++) { if (arr[i] < min) { min = arr[i]; } } return min; } /* Binary search: O(log n) */ int findMinBinary(const int arr[], int n) { if (n <= 0) return INT_MAX; int low = 0; int high = n - 1; while (low < high) { /* If current range is sorted, first element is minimum */ if (arr[low] < arr[high]) { return arr[low]; } int mid = low + (high - low) / 2; /* Minimum lies in right half */ if (arr[mid] > arr[high]) { low = mid + 1; } /* Minimum lies at mid or left half */ else { high = mid; } } return arr[low]; } /* Finds index of minimum element */ int findMinIndex(const int arr[], int n) { if (n <= 0) return -1; int low = 0; int high = n - 1; while (low < high) { if (arr[low] < arr[high]) { return low; } int mid = low + (high - low) / 2; if (arr[mid] > arr[high]) { low = mid + 1; } else { high = mid; } } return low; } /* Rotates array right by d positions */ void rotateArray(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) return; for (int i = 0; i < n; i++) { temp[(i + d) % n] = arr[i]; } for (int i = 0; i < n; i++) { arr[i] = temp[i]; } free(temp); } /* 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 both approaches */ void demonstrateApproaches(void) { printf("\n=== Sample Arrays ===\n"); int arr1[] = {5, 6, 1, 2, 3, 4}; int n1 = sizeof(arr1) / sizeof(arr1[0]); printf("\nArray 1: "); printArray(arr1, n1); printf("Linear: %d\n", findMinLinear(arr1, n1)); printf("Binary: %d\n", findMinBinary(arr1, n1)); printf("Index: %d\n", findMinIndex(arr1, n1)); int arr2[] = {3, 1, 2}; int n2 = sizeof(arr2) / sizeof(arr2[0]); printf("\nArray 2: "); printArray(arr2, n2); printf("Linear: %d\n", findMinLinear(arr2, n2)); printf("Binary: %d\n", findMinBinary(arr2, n2)); int arr3[] = {4, 2, 3}; int n3 = sizeof(arr3) / sizeof(arr3[0]); printf("\nArray 3: "); printArray(arr3, n3); printf("Linear: %d\n", findMinLinear(arr3, n3)); printf("Binary: %d\n", findMinBinary(arr3, n3)); int arr4[] = {1, 2, 3, 4, 5}; int n4 = sizeof(arr4) / sizeof(arr4[0]); printf("\nArray 4 (not rotated): "); printArray(arr4, n4); printf("Linear: %d\n", findMinLinear(arr4, n4)); printf("Binary: %d\n", findMinBinary(arr4, n4)); int arr5[] = {2, 3, 4, 5, 1}; int n5 = sizeof(arr5) / sizeof(arr5[0]); printf("\nArray 5: "); printArray(arr5, n5); printf("Linear: %d\n", findMinLinear(arr5, n5)); printf("Binary: %d\n", findMinBinary(arr5, n5)); } /* Performance comparison */ void performanceComparison(void) { printf("\n=== Performance Comparison (n = 100000) ===\n"); int size = 100000; int *arr = malloc(size * sizeof(int)); if (arr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } /* Create sorted array then rotate */ for (int i = 0; i < size; i++) { arr[i] = i + 1; } rotateArray(arr, size, 12345); printf("Array size: %d, Rotated by: 12345\n\n", size); clock_t start, end; double timeTaken; /* Linear search */ start = clock(); int linearResult = findMinLinear(arr, size); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Linear (O(n)): Min = %d, Time = %.3f ms\n", linearResult, timeTaken); /* Binary search */ start = clock(); int binaryResult = findMinBinary(arr, size); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Binary (O(log n)): Min = %d, Time = %.3f ms\n", binaryResult, timeTaken); printf("\nVerification: %s\n", (linearResult == binaryResult) ? "PASS" : "FAIL"); free(arr); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); /* Case 1: Single element */ int arr1[] = {42}; printf("Case 1: Single element\n"); printf(" Array: "); printArray(arr1, 1); printf(" Min: %d\n", findMinBinary(arr1, 1)); /* Case 2: Two elements, rotated */ int arr2[] = {2, 1}; printf("\nCase 2: Two elements, rotated\n"); printf(" Array: "); printArray(arr2, 2); printf(" Min: %d\n", findMinBinary(arr2, 2)); /* Case 3: Two elements, sorted */ int arr3[] = {1, 2}; printf("\nCase 3: Two elements, sorted\n"); printf(" Array: "); printArray(arr3, 2); printf(" Min: %d\n", findMinBinary(arr3, 2)); /* Case 4: Already sorted */ int arr4[] = {1, 2, 3, 4, 5}; printf("\nCase 4: Already sorted (not rotated)\n"); printf(" Array: "); printArray(arr4, 5); printf(" Min: %d\n", findMinBinary(arr4, 5)); /* Case 5: Rotated by n-1 (minimum at end) */ int arr5[] = {2, 3, 4, 5, 1}; printf("\nCase 5: Rotated by n-1 (minimum at end)\n"); printf(" Array: "); printArray(arr5, 5); printf(" Min: %d\n", findMinBinary(arr5, 5)); /* Case 6: Rotated by 1 (minimum at index 1) */ int arr6[] = {5, 1, 2, 3, 4}; printf("\nCase 6: Rotated by 1 (minimum at index 1)\n"); printf(" Array: "); printArray(arr6, 5); printf(" Min: %d\n", findMinBinary(arr6, 5)); /* Case 7: Negative numbers */ int arr7[] = {-3, -2, -1, -5, -4}; printf("\nCase 7: Negative numbers\n"); printf(" Array: "); printArray(arr7, 5); printf(" Min: %d\n", findMinBinary(arr7, 5)); } /* 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. Linear Search: Traverse entire array; O(n).
  2. Binary Search: Uses sorted range detection; O(log n).
  3. Index variant: Returns the index of the minimum element.

Expected Output (sample run)

text

=== Minimum in Sorted and Rotated Array === --- 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: [5, 6, 1, 2, 3, 4] Linear: 1 Binary: 1 Index: 2 Array 2: [3, 1, 2] Linear: 1 Binary: 1 Array 3: [4, 2, 3] Linear: 2 Binary: 2 Array 4 (not rotated): [1, 2, 3, 4, 5] Linear: 1 Binary: 1 Array 5: [2, 3, 4, 5, 1] Linear: 1 Binary: 1 --- Menu --- ... Enter choice: 3 === Performance Comparison (n = 100000) === Array size: 100000, Rotated by: 12345 Linear (O(n)): Min = 1, Time = 0.398 ms Binary (O(log n)): Min = 1, Time = 0.001 ms Verification: PASS --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: Single element Array: [42] Min: 42 Case 2: Two elements, rotated Array: [2, 1] Min: 1 Case 3: Two elements, sorted Array: [1, 2] Min: 1 Case 4: Already sorted (not rotated) Array: [1, 2, 3, 4, 5] Min: 1 Case 5: Rotated by n-1 (minimum at end) Array: [2, 3, 4, 5, 1] Min: 1 Case 6: Rotated by 1 (minimum at index 1) Array: [5, 1, 2, 3, 4] Min: 1 Case 7: Negative numbers Array: [-3, -2, -1, -5, -4] Min: -5

Common Pitfalls, Edge Cases & Memory Considerations

1. Empty Array

Accessing arr[0] on an empty array is undefined behavior.

c

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

Solution: Check n <= 0 before processing.

2. Single Element

A single-element array has that element as minimum.

c

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

Solution: Handle n == 1 correctly.

3. Already Sorted Array

The arr[low] < arr[high] check handles this case.

c

/* If sorted, returns arr[low] immediately */

4. Duplicate Elements

Duplicates can break binary search assumptions.

c

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

Solution: For duplicates, fall back to linear search.

5. Using <= Instead of <

Using arr[low] <= arr[high] may cause incorrect results with duplicates.

c

if (arr[low] < arr[high]) /* Correct for distinct elements */

6. Off-by-One in Mid Calculation

(low + high) / 2 can overflow.

c

int mid = low + (high - low) / 2; /* Correct */

7. Infinite Loop

Incorrect high = mid - 1 can skip the minimum.

c

high = mid; /* Correct: keep mid in search range */

8. Wrong Return Value

Returning arr[low] after loop vs. inside loop.

c

while (low < high) { ... } return arr[low]; /* Correct */

9. Negative Numbers

Negative numbers work correctly with the algorithm.

c

int arr[] = {-3, -2, -1, -5, -4}; /* Minimum = -5 */

10. Memory Considerations

  • Both approaches use O(1) extra space.
  • No dynamic allocation required.
  • Array size should fit in int or size_t.
  • Binary search preferred for large arrays.

Summary & Best Practices

Finding the minimum in a sorted and rotated array is a classic binary search problem. The minimum marks the rotation pivot and is the only element smaller than its predecessor.

Key Takeaways

  • Sorted and rotated array has two sorted parts.
  • Minimum element is at the rotation point.
  • Linear search: O(n) time.
  • Binary search: O(log n) time.
  • Key check: arr[low] < arr[high] means sorted range.
  • Use high = mid (not mid - 1) to keep minimum in range.

Best Practices

  1. Use binary search for O(log n) performance.
  2. Check n <= 0 for empty arrays.
  3. Use arr[low] < arr[high] to detect sorted ranges.
  4. Use low + (high - low) / 2 to avoid overflow.
  5. Use high = mid (not mid - 1) to keep minimum in range.
  6. Test with edge cases: empty, single, sorted, rotated by n-1.
  7. Handle duplicates with linear fallback if needed.
  8. Use INT_MAX for error return on empty arrays.
  9. Verify with naive approach during testing.
  10. Document rotation direction (this problem assumes right rotation).

By mastering this problem, you’ll understand binary search variations and be prepared for related problems like searching in rotated sorted arrays and finding rotation counts.

Learn to find rotation count in a right-rotated sorted array in C: naive O(n) and binary search O(log n) approaches. Master rotated array algorithms.

Rotation Count in Rotated Sorted Array in C

Rotation Count in a Rotated Sorted Array in C: A Comprehensive Tutorial

Given a sorted array that has been right-rotated k times, the task is to find the value of k. Right rotation means the last element moves to the first position, and all other elements shift one position right. The rotation count equals the index of the minimum element in the rotated array. Understanding this problem is essential for mastering binary search variations and rotated array manipulation.

This tutorial provides an in-depth exploration of finding the rotation count in a rotated sorted array in C: naive linear approach, optimized binary search, practical implementation, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is a Right-Rotated Sorted Array?

A sorted array that has been right-rotated k times. Each right rotation moves the last element to the front.

Example:

text

Original sorted: [2, 3, 6, 12, 15, 18] After 1 rotation: [18, 2, 3, 6, 12, 15] After 2 rotations: [15, 18, 2, 3, 6, 12]

The rotation count k = 2.

Key Insight

In a right-rotated sorted array:

  • The minimum element is the pivot point.
  • The index of the minimum element equals the number of right rotations k.

Example:

text

Array: [15, 18, 2, 3, 6, 12] Minimum: 2 at index 2 Rotation count: k = 2

Why This Problem Matters

  • Binary search variants: Classic rotated array problem.
  • Search in rotated arrays: Foundation for searching in rotated sorted arrays.
  • Algorithm design: Teaches divide-and-conquer optimization.
  • Interview preparation: Common coding interview question.
  • Data processing: Understanding circular shifts.

Approaches Overview

ApproachTime ComplexitySpace Complexity
Naive (linear scan)O(n)O(1)
Binary searchO(log n)O(1)

Syntax & Core Mechanics

Approach 1: Naive Linear Scan

Scan the array to find the minimum element; its index is the rotation count.

c

#include <stdio.h> int findRotationCount(const int arr[], int n) { if (n <= 0) return -1; int minIndex = 0; int minValue = arr[0]; for (int i = 1; i < n; i++) { if (arr[i] < minValue) { minValue = arr[i]; minIndex = i; } } return minIndex; } int main(void) { int arr[] = {15, 18, 2, 3, 6, 12}; int n = sizeof(arr) / sizeof(arr[0]); int k = findRotationCount(arr, n); printf("Rotation count: %d\n", k); return 0; }

Expected output:

text

Rotation count: 2

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

Approach 2: Binary Search (Optimized)

Use binary search to find the minimum element in O(log n) time.

Key logic:

  • If arr[low] <= arr[high], the subarray is sorted; minimum is at low.
  • If arr[mid] > arr[high], minimum is in the right half.
  • Otherwise, minimum is in the left half (could be mid).

c

#include <stdio.h> int findRotationCount(const int arr[], int n) { if (n <= 0) return -1; int low = 0; int high = n - 1; while (low <= high) { /* If subarray is sorted, minimum is at low */ if (arr[low] <= arr[high]) { return low; } int mid = low + (high - low) / 2; /* Check if mid is the minimum */ int next = (mid + 1) % n; int prev = (mid - 1 + n) % n; if (arr[mid] <= arr[next] && arr[mid] <= arr[prev]) { return mid; } /* Minimum is in right half */ if (arr[mid] > arr[high]) { low = mid + 1; } /* Minimum is in left half */ else { high = mid - 1; } } return low; } int main(void) { int arr[] = {15, 18, 2, 3, 6, 12}; int n = sizeof(arr) / sizeof(arr[0]); int k = findRotationCount(arr, n); printf("Rotation count: %d\n", k); return 0; }

Expected output:

text

Rotation count: 2

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

Simplified Binary Search

A cleaner version without explicit neighbor checks:

c

#include <stdio.h> int findRotationCount(const int arr[], int n) { if (n <= 0) return -1; int low = 0; int high = n - 1; while (low < high) { /* If subarray is sorted, minimum is at low */ if (arr[low] <= arr[high]) { return low; } int mid = low + (high - low) / 2; /* Minimum is in right half */ if (arr[mid] > arr[high]) { low = mid + 1; } /* Minimum is in left half (could be mid) */ else { high = mid; } } return low; } int main(void) { int arr[] = {15, 18, 2, 3, 6, 12}; int n = sizeof(arr) / sizeof(arr[0]); printf("Rotation count: %d\n", findRotationCount(arr, n)); return 0; }

Expected output:

text

Rotation count: 2

Full Practical Code Example with Explanation

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

Program: Rotation Count Finder

c

/** * rotation_count.c * Finds the rotation count in a right-rotated sorted array. * Compares naive and binary search 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 findRotationNaive(const int arr[], int n); int findRotationBinary(const int arr[], int n); int readInteger(const char *prompt, int *outValue); void printArray(const int arr[], int n); int isSorted(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("=== Rotation Count Finder ===\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; 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 (sorted then right-rotated):\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 = findRotationNaive(arr, n); int binary = findRotationBinary(arr, n); printf("Rotation count (naive): %d\n", naive); printf("Rotation count (binary): %d\n", binary); printf("Verification: %s\n", (naive == binary) ? "PASS" : "FAIL"); 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 approach: O(n) */ int findRotationNaive(const int arr[], int n) { if (n <= 0) return -1; int minIndex = 0; for (int i = 1; i < n; i++) { if (arr[i] < arr[minIndex]) { minIndex = i; } } return minIndex; } /* Binary search approach: O(log n) */ int findRotationBinary(const int arr[], int n) { if (n <= 0) return -1; if (n == 1) return 0; int low = 0; int high = n - 1; while (low <= high) { /* If subarray is sorted, minimum is at low */ if (arr[low] <= arr[high]) { return low; } int mid = low + (high - low) / 2; int next = (mid + 1) % n; int prev = (mid - 1 + n) % n; /* Check if mid is the minimum */ if (arr[mid] <= arr[next] && arr[mid] <= arr[prev]) { return mid; } /* Minimum is in right half */ if (arr[mid] > arr[high]) { low = mid + 1; } /* Minimum is in left half */ else { high = mid - 1; } } return low; } /* Checks if array is sorted in ascending order */ int isSorted(const int arr[], int n) { for (int i = 1; i < n; i++) { if (arr[i] < arr[i - 1]) return 0; } 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"); } /* Demonstrates both approaches on sample arrays */ void demonstrateApproaches(void) { printf("\n=== Sample Arrays ===\n"); int arr1[] = {15, 18, 2, 3, 6, 12}; int n1 = sizeof(arr1) / sizeof(arr1[0]); printf("\nArray 1: "); printArray(arr1, n1); printf("Naive: %d\n", findRotationNaive(arr1, n1)); printf("Binary: %d\n", findRotationBinary(arr1, n1)); int arr2[] = {7, 9, 11, 12, 5}; int n2 = sizeof(arr2) / sizeof(arr2[0]); printf("\nArray 2: "); printArray(arr2, n2); printf("Naive: %d\n", findRotationNaive(arr2, n2)); printf("Binary: %d\n", findRotationBinary(arr2, n2)); int arr3[] = {7, 9, 11, 12, 15}; int n3 = sizeof(arr3) / sizeof(arr3[0]); printf("\nArray 3 (already sorted): "); printArray(arr3, n3); printf("Naive: %d\n", findRotationNaive(arr3, n3)); printf("Binary: %d\n", findRotationBinary(arr3, n3)); int arr4[] = {3, 4, 5, 1, 2}; int n4 = sizeof(arr4) / sizeof(arr4[0]); printf("\nArray 4: "); printArray(arr4, n4); printf("Naive: %d\n", findRotationNaive(arr4, n4)); printf("Binary: %d\n", findRotationBinary(arr4, n4)); } /* Performance comparison */ void performanceComparison(void) { printf("\n=== Performance Comparison (n = 100000) ===\n"); int size = 100000; int *arr = malloc(size * sizeof(int)); if (arr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); return; } /* Create a rotated sorted array with 12345 rotations */ int k = 12345; for (int i = 0; i < size; i++) { arr[i] = (i + k) % size; } printf("Array size: %d, Actual rotation: %d\n\n", size, k); clock_t start, end; double timeTaken; /* Naive approach */ start = clock(); int naiveResult = findRotationNaive(arr, size); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Naive (O(n)): Result = %d, Time = %.3f ms\n", naiveResult, timeTaken); /* Binary approach */ start = clock(); int binaryResult = findRotationBinary(arr, size); end = clock(); timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("Binary (O(log n)): Result = %d, Time = %.3f ms\n", binaryResult, timeTaken); free(arr); } /* Demonstrates edge cases */ void demonstrateEdgeCases(void) { printf("\n=== Edge Cases ===\n"); /* Case 1: Empty array */ int arr1[] = {}; printf("Case 1: Empty array (n = 0)\n"); printf(" Result: %d (error code)\n", findRotationBinary(arr1, 0)); /* Case 2: Single element */ int arr2[] = {42}; printf("\nCase 2: Single element\n"); printf(" Array: "); printArray(arr2, 1); printf(" Rotation: %d\n", findRotationBinary(arr2, 1)); /* Case 3: Two elements, rotated once */ int arr3[] = {2, 1}; printf("\nCase 3: Two elements, rotated once\n"); printf(" Array: "); printArray(arr3, 2); printf(" Rotation: %d\n", findRotationBinary(arr3, 2)); /* Case 4: Already sorted */ int arr4[] = {1, 2, 3, 4, 5}; printf("\nCase 4: Already sorted\n"); printf(" Array: "); printArray(arr4, 5); printf(" Rotation: %d\n", findRotationBinary(arr4, 5)); /* Case 5: Rotated by n-1 */ int arr5[] = {2, 3, 4, 5, 1}; printf("\nCase 5: Rotated by n-1 = 4\n"); printf(" Array: "); printArray(arr5, 5); printf(" Rotation: %d\n", findRotationBinary(arr5, 5)); /* Case 6: All same elements */ int arr6[] = {5, 5, 5, 5}; printf("\nCase 6: All same elements\n"); printf(" Array: "); printArray(arr6, 4); printf(" Rotation: %d\n", findRotationBinary(arr6, 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: Linear scan finds minimum; its index is the rotation count.
  2. Binary: Uses sorted property to narrow search to O(log n).
  3. Verification: Both approaches must produce identical results.
  4. Edge cases: Empty, single element, duplicates, already sorted.

Expected Output (sample run)

text

=== Rotation Count Finder === --- 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: [15, 18, 2, 3, 6, 12] Naive: 2 Binary: 2 Array 2: [7, 9, 11, 12, 5] Naive: 4 Binary: 4 Array 3 (already sorted): [7, 9, 11, 12, 15] Naive: 0 Binary: 0 Array 4: [3, 4, 5, 1, 2] Naive: 3 Binary: 3 --- Menu --- ... Enter choice: 3 === Performance Comparison (n = 100000) === Array size: 100000, Actual rotation: 12345 Naive (O(n)): Result = 12345, Time = 0.412 ms Binary (O(log n)): Result = 12345, Time = 0.001 ms --- Menu --- ... Enter choice: 4 === Edge Cases === Case 1: Empty array (n = 0) Result: -1 (error code) Case 2: Single element Array: [42] Rotation: 0 Case 3: Two elements, rotated once Array: [2, 1] Rotation: 1 Case 4: Already sorted Array: [1, 2, 3, 4, 5] Rotation: 0 Case 5: Rotated by n-1 = 4 Array: [2, 3, 4, 5, 1] Rotation: 4 Case 6: All same elements Array: [5, 5, 5, 5] Rotation: 0

Common Pitfalls, Edge Cases & Memory Considerations

1. Empty Array

Accessing arr[0] on an empty array is undefined behavior.

c

if (n <= 0) return -1; // Required

Solution: Check n <= 0 before processing.

2. Single Element

A single-element array is trivially sorted; rotation count is 0.

c

if (n == 1) return 0;

Solution: Handle n == 1 explicitly.

3. Already Sorted Array

If the array is already sorted, rotation count is 0.

c

if (arr[low] <= arr[high]) return low; // Returns 0 for sorted

Solution: The base case handles this correctly.

4. Duplicate Elements

Duplicates can break binary search assumptions.

c

int arr[] = {2, 2, 2, 0, 2}; // Ambiguous minimum

Solution: For duplicates, fall back to linear scan or handle edge cases.

5. Integer Overflow in Mid Calculation

(low + high) / 2 can overflow for large indices.

c

int mid = (low + high) / 2; // BUG: potential overflow

Solution: Use low + (high - low) / 2.

6. Off-by-One in Binary Search

Incorrect loop bounds skip elements.

c

while (low < high) { // Correct for finding minimum /* ... */ }

Solution: Use consistent bounds.

7. All Elements Same

All elements same means rotation count is ambiguous (return 0).

c

int arr[] = {5, 5, 5, 5}; /* Any rotation gives same array; k = 0 is correct */

Solution: Return 0 for sorted-or-equal arrays.

8. Not Handling k = n

If rotated n times, array is back to original; k = 0.

Solution: d %= n or handle in logic.

9. Left Rotation Confusion

This problem uses right rotation. Left rotation count equals n - (right rotation count).

c

/* Right rotation count = index of minimum */ /* Left rotation count = n - index of minimum */

Solution: Document rotation direction clearly.

10. Memory Considerations

  • Both approaches use O(1) extra space.
  • No dynamic allocation required.
  • Array size should fit in int or size_t.
  • Binary search is preferred for large arrays.

Summary & Best Practices

Finding the rotation count in a right-rotated sorted array is a classic problem with an elegant O(log n) binary search solution. The rotation count equals the index of the minimum element.

Key Takeaways

  • Right rotation count = index of minimum element.
  • Naive approach: O(n) linear scan.
  • Binary search approach: O(log n).
  • Handle empty arrays, single elements, and already-sorted arrays.
  • Duplicates may complicate binary search.
  • Use low + (high - low) / 2 to avoid overflow.

Best Practices

  1. Use binary search for O(log n) performance.
  2. Check for empty arrays (n <= 0).
  3. Handle single-element arrays (n == 1).
  4. Return 0 for already-sorted arrays.
  5. Document rotation direction (left vs right).
  6. Test with duplicates if the array may contain them.
  7. Use size_t for array sizes where appropriate.
  8. Avoid integer overflow in mid calculation.
  9. Verify with naive approach during testing.
  10. Profile performance for large arrays.

By mastering this problem, you’ll understand binary search variations and be prepared for related problems like searching in rotated sorted arrays.

Learn to count zeros in a sorted binary array using linear scan and binary search. Master O(log n) algorithm with edge cases and performance tips.

Count Zeros in Sorted Binary Array: C Guide

Zeros Count in Sorted Binary Array: A Comprehensive Tutorial

Counting zeros in a sorted binary array is a classic algorithmic problem that demonstrates efficient searching techniques. A sorted binary array contains only 0s and 1s, with all 0s appearing before all 1s (or vice versa). Finding the count of zeros efficiently is essential for understanding binary search, time complexity analysis, and optimization techniques.

This tutorial provides an in-depth exploration of counting zeros in a sorted binary array: problem understanding, naive and optimized approaches, binary search implementation, edge cases, and best practices.


Conceptual Introduction & Fundamentals

What is a Sorted Binary Array?

A sorted binary array is an array that contains only two distinct values: 0 and 1. The array is sorted, meaning all 0s come first, followed by all 1s (ascending order). For example:

text

[0, 0, 0, 1, 1, 1] // Three zeros followed by three ones [0, 0, 0, 0] // All zeros [1, 1, 1, 1] // All ones [] // Empty array

Problem Statement

Given a sorted binary array (all 0s followed by all 1s), count the number of zeros in the array.

Examples:

  • Input: [0, 0, 0, 1, 1] → Output: 3
  • Input: [0, 0, 0, 0] → Output: 4
  • Input: [1, 1, 1, 1] → Output: 0
  • Input: [] → Output: 0

Why This Problem Matters

  • Algorithm design: Demonstrates linear search vs. binary search trade-offs.
  • Time complexity analysis: O(n) vs. O(log n) comparison.
  • Foundation for advanced problems: Variations include finding first occurrence, last occurrence, and count of any element in sorted arrays.
  • Interview preparation: Common coding interview question.
  • Real-world applications: Counting occurrences in sorted datasets, database queries, and data analysis.

Approaches Overview

ApproachTime ComplexitySpace Complexity
Linear scanO(n)O(1)
Binary search (find first 1)O(log n)O(1)
Binary search (find last 0)O(log n)O(1)
Standard library bsearchO(log n)O(1)

Syntax & Core Mechanics

Approach 1: Linear Scan (Naive)

Traverse the array from left to right, counting zeros until a 1 is encountered.

c

#include <stdio.h> int countZerosLinear(const int arr[], int n) { int count = 0; for (int i = 0; i < n; i++) { if (arr[i] == 0) { count++; } else { break; // Since sorted, first 1 means no more zeros } } return count; } int main(void) { int arr[] = {0, 0, 0, 1, 1, 1}; int n = sizeof(arr) / sizeof(arr[0]); printf("Zeros: %d\n", countZerosLinear(arr, n)); return 0; }

Expected output:

text

Zeros: 3

Time complexity: O(n) in worst case (all zeros).

Approach 2: Binary Search (Find First 1)

Since the array is sorted, use binary search to find the index of the first 1. The count of zeros equals that index.

c

#include <stdio.h> int countZerosBinary(const int arr[], int n) { int left = 0; int right = n - 1; int firstOneIndex = n; // Default: no 1 found while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == 1) { firstOneIndex = mid; // Record position right = mid - 1; // Search left half } else { left = mid + 1; // Search right half } } return firstOneIndex; // Number of zeros = index of first 1 } int main(void) { int arr[] = {0, 0, 0, 1, 1, 1}; int n = sizeof(arr) / sizeof(arr[0]); printf("Zeros: %d\n", countZerosBinary(arr, n)); return 0; }

Expected output:

text

Zeros: 3

Time complexity: O(log n).

Binary Search Logic Explained

  1. Initialize left = 0, right = n - 1, firstOneIndex = n.
  2. While left <= right:
    • Calculate mid = left + (right - left) / 2 (avoids overflow).
    • If arr[mid] == 1, update firstOneIndex = mid and search left half (right = mid - 1).
    • If arr[mid] == 0, search right half (left = mid + 1).
  3. After loop, firstOneIndex is the index of the first 1.
  4. The count of zeros equals firstOneIndex.

Approach 3: Binary Search (Find Last 0)

Alternatively, find the index of the last 0. The count of zeros equals lastZeroIndex + 1.

c

#include <stdio.h> int countZerosLastZero(const int arr[], int n) { int left = 0; int right = n - 1; int lastZeroIndex = -1; // Default: no 0 found while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == 0) { lastZeroIndex = mid; // Record position left = mid + 1; // Search right half } else { right = mid - 1; // Search left half } } return lastZeroIndex + 1; // Count of zeros } int main(void) { int arr[] = {0, 0, 0, 1, 1, 1}; int n = sizeof(arr) / sizeof(arr[0]); printf("Zeros: %d\n", countZerosLastZero(arr, n)); return 0; }

Expected output:

text

Zeros: 3

Handling Edge Cases

c

#include <stdio.h> int countZerosEdge(const int arr[], int n) { /* Empty array */ if (n == 0) return 0; /* All ones: first element is 1 */ if (arr[0] == 1) return 0; /* All zeros: last element is 0 */ if (arr[n - 1] == 0) return n; /* Mixed: binary search */ int left = 0; int right = n - 1; int firstOneIndex = n; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == 1) { firstOneIndex = mid; right = mid - 1; } else { left = mid + 1; } } return firstOneIndex; }

Full Practical Code Example with Explanation

This program demonstrates multiple approaches to count zeros in a sorted binary array, including linear scan, binary search, and recursive binary search, with performance comparison.

Program: Zero Counter for Sorted Binary Arrays

c

/** * count_zeros_sorted_binary.c * Counts zeros in a sorted binary array using multiple approaches. * Demonstrates linear scan, iterative binary search, and recursive binary search. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <errno.h> #define MAX_LINE_LEN 100 /* Function prototypes */ int countZerosLinear(const int arr[], int n); int countZerosBinary(const int arr[], int n); int countZerosRecursive(const int arr[], int left, int right); int countZerosWrapper(const int arr[], int n); int readInteger(const char *prompt, int *outValue); void demonstrateApproach(const char *name, int result, clock_t start, clock_t end); void printArray(const int arr[], int n); int main(void) { int choice; char buffer[MAX_LINE_LEN]; /* Test arrays */ int arr1[] = {0, 0, 0, 1, 1, 1}; int n1 = sizeof(arr1) / sizeof(arr1[0]); int arr2[] = {0, 0, 0, 0, 0}; int n2 = sizeof(arr2) / sizeof(arr2[0]); int arr3[] = {1, 1, 1, 1}; int n3 = sizeof(arr3) / sizeof(arr3[0]); int arr4[] = {}; int n4 = 0; int arr5[] = {0}; int n5 = sizeof(arr5) / sizeof(arr5[0]); printf("=== Zero Counter for Sorted Binary Arrays ===\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. 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: printf("\n=== Sample Arrays ===\n"); printf("\nArray 1: "); printArray(arr1, n1); printf("Zeros (linear): %d\n", countZerosLinear(arr1, n1)); printf("Zeros (binary): %d\n", countZerosBinary(arr1, n1)); printf("Zeros (recursive): %d\n", countZerosWrapper(arr1, n1)); printf("\nArray 2 (all zeros): "); printArray(arr2, n2); printf("Zeros (linear): %d\n", countZerosLinear(arr2, n2)); printf("Zeros (binary): %d\n", countZerosBinary(arr2, n2)); printf("Zeros (recursive): %d\n", countZerosWrapper(arr2, n2)); printf("\nArray 3 (all ones): "); printArray(arr3, n3); printf("Zeros (linear): %d\n", countZerosLinear(arr3, n3)); printf("Zeros (binary): %d\n", countZerosBinary(arr3, n3)); printf("Zeros (recursive): %d\n", countZerosWrapper(arr3, n3)); printf("\nArray 4 (empty): "); printArray(arr4, n4); printf("Zeros (linear): %d\n", countZerosLinear(arr4, n4)); printf("Zeros (binary): %d\n", countZerosBinary(arr4, n4)); printf("Zeros (recursive): %d\n", countZerosWrapper(arr4, n4)); printf("\nArray 5 (single zero): "); printArray(arr5, n5); printf("Zeros (linear): %d\n", countZerosLinear(arr5, n5)); printf("Zeros (binary): %d\n", countZerosBinary(arr5, n5)); printf("Zeros (recursive): %d\n", countZerosWrapper(arr5, n5)); break; case 2: { int n; if (readInteger("Enter array size (0-100): ", &n) != 0) { fprintf(stderr, "Invalid size.\n"); break; } if (n < 0 || n > 100) { printf("Size must be 0-100.\n"); break; } int *customArr = malloc(n * sizeof(int)); if (customArr == NULL && n > 0) { fprintf(stderr, "Memory allocation failed.\n"); break; } printf("Enter %d elements (only 0s and 1s, sorted):\n", n); for (int i = 0; i < n; i++) { int value; char prompt[50]; snprintf(prompt, sizeof(prompt), " Element %d: ", i); if (readInteger(prompt, &value) != 0) { fprintf(stderr, "Invalid element.\n"); free(customArr); break; } if (value != 0 && value != 1) { printf("Only 0 and 1 allowed. Please try again.\n"); i--; continue; } /* Check sorted order */ if (i > 0 && value < customArr[i - 1]) { printf("Array must be sorted (all 0s before all 1s).\n"); i--; continue; } customArr[i] = value; } printf("\nCustom array: "); printArray(customArr, n); printf("Zeros (linear): %d\n", countZerosLinear(customArr, n)); printf("Zeros (binary): %d\n", countZerosBinary(customArr, n)); printf("Zeros (recursive): %d\n", countZerosWrapper(customArr, n)); free(customArr); break; } case 3: { printf("\n=== Performance Comparison ===\n"); /* Create a large array with half zeros, half ones */ int largeSize = 10000000; int *largeArr = malloc(largeSize * sizeof(int)); if (largeArr == NULL) { fprintf(stderr, "Memory allocation failed.\n"); break; } /* Fill: first half zeros, second half ones */ for (int i = 0; i < largeSize / 2; i++) { largeArr[i] = 0; } for (int i = largeSize / 2; i < largeSize; i++) { largeArr[i] = 1; } clock_t start, end; /* Linear scan */ start = clock(); int linearResult = countZerosLinear(largeArr, largeSize); end = clock(); demonstrateApproach("Linear scan", linearResult, start, end); /* Binary search */ start = clock(); int binaryResult = countZerosBinary(largeArr, largeSize); end = clock(); demonstrateApproach("Binary search", binaryResult, start, end); /* Recursive binary search */ start = clock(); int recursiveResult = countZerosWrapper(largeArr, largeSize); end = clock(); demonstrateApproach("Recursive binary search", recursiveResult, start, end); free(largeArr); break; } case 4: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Linear scan: O(n) */ int countZerosLinear(const int arr[], int n) { int count = 0; for (int i = 0; i < n; i++) { if (arr[i] == 0) { count++; } else { break; /* Since sorted, no more zeros after first 1 */ } } return count; } /* Iterative binary search: O(log n) */ int countZerosBinary(const int arr[], int n) { int left = 0; int right = n - 1; int firstOneIndex = n; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == 1) { firstOneIndex = mid; right = mid - 1; } else { left = mid + 1; } } return firstOneIndex; } /* Recursive binary search helper */ int countZerosRecursive(const int arr[], int left, int right) { if (left > right) { return left; /* Base case: first 1 index */ } int mid = left + (right - left) / 2; if (arr[mid] == 1) { return countZerosRecursive(arr, left, mid - 1); } else { return countZerosRecursive(arr, mid + 1, right); } } /* Wrapper for recursive binary search */ int countZerosWrapper(const int arr[], int n) { if (n == 0) return 0; return countZerosRecursive(arr, 0, n - 1); } /* Displays approach name, result, and time */ void demonstrateApproach(const char *name, int result, clock_t start, clock_t end) { double timeTaken = ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0; printf("%-25s: Result = %d, Time = %.3f ms\n", name, result, timeTaken); } /* Prints an array */ void printArray(const int arr[], int n) { if (n == 0) { printf("[]"); return; } printf("["); for (int i = 0; i < n; i++) { printf("%d", arr[i]); if (i < n - 1) printf(", "); } printf("]"); } /* 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. countZerosLinear: O(n) linear scan, stops at first 1.
  2. countZerosBinary: O(log n) iterative binary search to find first 1.
  3. countZerosRecursive: O(log n) recursive binary search.
  4. countZerosWrapper: Handles empty array for recursive approach.
  5. Performance comparison: Demonstrates time difference on large arrays.

Expected Output (sample run)

text

=== Zero Counter for Sorted Binary Arrays === --- Menu --- 1. Demonstrate with Sample Arrays 2. Enter Custom Array 3. Performance Comparison 4. Exit Enter choice: 1 === Sample Arrays === Array 1: [0, 0, 0, 1, 1, 1] Zeros (linear): 3 Zeros (binary): 3 Zeros (recursive): 3 Array 2 (all zeros): [0, 0, 0, 0, 0] Zeros (linear): 5 Zeros (binary): 5 Zeros (recursive): 5 Array 3 (all ones): [1, 1, 1, 1] Zeros (linear): 0 Zeros (binary): 0 Zeros (recursive): 0 Array 4 (empty): [] Zeros (linear): 0 Zeros (binary): 0 Zeros (recursive): 0 Array 5 (single zero): [0] Zeros (linear): 1 Zeros (binary): 1 Zeros (recursive): 1 --- Menu --- ... Enter choice: 3 === Performance Comparison === Linear scan : Result = 5000000, Time = 8.420 ms Binary search : Result = 5000000, Time = 0.001 ms Recursive binary search : Result = 5000000, Time = 0.002 ms

Common Pitfalls, Edge Cases & Memory Considerations

1. Empty Array

Accessing arr[0] or arr[n-1] on an empty array is undefined behavior.

c

int countZeros(const int arr[], int n) { if (arr[0] == 1) return 0; // BUG: empty array if (arr[n - 1] == 0) return n; // BUG: empty array }

Solution: Check n == 0 first: if (n == 0) return 0;

2. Integer Overflow in Mid Calculation

(left + right) / 2 can overflow for large values.

c

int mid = (left + right) / 2; // Potential overflow

Solution: Use left + (right - left) / 2.

3. Off-by-One in Binary Search

Incorrect loop bounds can skip elements or cause infinite loops.

c

while (left < right) { // May miss the correct element // ... }

Solution: Use while (left <= right) for inclusive bounds.

4. Unsorted Input

The binary search assumes sorted input. Unsorted arrays will give incorrect results.

c

int arr[] = {1, 0, 1, 0}; // Not sorted countZerosBinary(arr, 4); // Incorrect result

Solution: Validate sorted order or sort first.

5. Non-Binary Values

Input containing values other than 0 and 1 breaks the algorithm’s assumptions.

c

int arr[] = {0, 0, 2, 3, 1}; // Invalid binary array

Solution: Validate elements are 0 or 1.

6. Recursive Depth

Recursive binary search depth is O(log n), which is safe for typical array sizes. No stack overflow risk for n up to millions.

7. clock() Precision

clock() measures CPU time and may not be precise for very fast operations.

Solution: Use larger arrays or repeat operations for measurable differences.

8. Memory Allocation Failure

Large arrays may fail to allocate memory.

c

int *arr = malloc(10000000 * sizeof(int)); if (arr == NULL) { // Always check fprintf(stderr, "Memory allocation failed\n"); }

Solution: Always check malloc return value.

9. Array Size Type

Using int for array size can overflow for very large arrays.

c

int n = 10000000000; // Overflow

Solution: Use size_t for array sizes.

10. Cache Performance

Linear scan has better cache locality for small arrays; binary search jumps around memory. For very small arrays, linear scan may be faster.

Solution: Use linear scan for small arrays (n < 100), binary search for large arrays.


Summary & Best Practices

Counting zeros in a sorted binary array is a classic problem that demonstrates the power of binary search. While linear scan is O(n), binary search achieves O(log n) by exploiting the sorted property.

Key Takeaways

  • Sorted binary array: all 0s followed by all 1s.
  • Linear scan: O(n), simple but slow for large arrays.
  • Binary search: O(log n), efficient for large arrays.
  • Count of zeros = index of first 1 = last zero index + 1.
  • Handle edge cases: empty array, all zeros, all ones.
  • Use left + (right - left) / 2 to avoid overflow.

Best Practices

  1. Use binary search for large arrays (n > 1000).
  2. Use linear scan for small arrays (n < 100) for simplicity.
  3. Check for empty array before accessing elements.
  4. Handle all-zeros and all-ones cases explicitly for optimization.
  5. Use size_t for array sizes instead of int.
  6. Avoid integer overflow in mid calculation.
  7. Validate input to ensure array is sorted and binary.
  8. Document the algorithm’s complexity in comments.
  9. Test with edge cases: empty, single element, all zeros, all ones.
  10. Prefer iterative over recursive for large arrays to avoid stack overhead.

By mastering this algorithm, you’ll understand binary search fundamentals and be prepared for variations like finding first/last occurrences, counting specific elements, and searching in rotated arrays.