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 C array searching: linear search, binary search, sentinel search, and recursive approaches. Master O(n) and O(log n) algorithms with examples

Search an Element in an Array: C Complete Guide

Search an Element in an Array: A Comprehensive Tutorial

Searching for an element in an array is one of the most fundamental operations in programming. It involves finding whether a specific value exists in an array and, if so, determining its position. C provides several search techniques, from simple linear search to efficient binary search on sorted arrays.

This tutorial provides an in-depth exploration of searching elements in arrays using C: linear search, binary search, recursive approaches, performance analysis, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is Array Searching?

Array searching is the process of finding a specific element (target value) within an array. The search returns:

  • The index of the first occurrence of the element (if found)
  • A sentinel value (e.g., -1) indicating the element is not present

Example:

c

int arr[] = {10, 20, 30, 40, 50}; int index = search(arr, 5, 30); // Returns 2 (index of 30) int notFound = search(arr, 5, 100); // Returns -1 (not found)

Why Searching Matters

  • Data retrieval: Find specific records in databases and lists.
  • Algorithm foundation: Building block for sorting, filtering, and data processing.
  • Optimization: Different search algorithms offer trade-offs between time and memory.
  • Real-world applications: Contact lookup, inventory management, text search.
  • Interview preparation: Common coding interview topic.

Search Algorithms Overview

AlgorithmTime Complexity (Worst)Space ComplexityRequirement
Linear searchO(n)O(1)None (works on unsorted arrays)
Binary searchO(log n)O(1) iterative, O(log n) recursiveSorted array
Sentinel searchO(n)O(1)None (optimized linear search)

When to Use Which Algorithm

  • Linear search: Small arrays, unsorted arrays, single searches.
  • Binary search: Large sorted arrays, repeated searches.
  • Sentinel search: When you want to avoid bound checking in each iteration.

Syntax & Core Mechanics

Linear Search

Linear search traverses the array sequentially, comparing each element with the target.

c

#include <stdio.h> int linearSearch(const int arr[], int n, int target) { for (int i = 0; i < n; i++) { if (arr[i] == target) { return i; // Found at index i } } return -1; // Not found } int main(void) { int arr[] = {10, 20, 30, 40, 50}; int n = sizeof(arr) / sizeof(arr[0]); int index = linearSearch(arr, n, 30); if (index != -1) { printf("Found 30 at index %d\n", index); } else { printf("30 not found\n"); } int notFound = linearSearch(arr, n, 100); printf("100 %s\n", notFound != -1 ? "found" : "not found"); return 0; }

Expected output:

text

Found 30 at index 2 100 not found

Time complexity: O(n) worst case (element at end or not present).

Sentinel Linear Search

Sentinel search eliminates the bound check in each iteration by placing the target at the end of the array temporarily.

c

#include <stdio.h> int sentinelSearch(int arr[], int n, int target) { int last = arr[n - 1]; // Save last element arr[n - 1] = target; // Place sentinel int i = 0; while (arr[i] != target) { i++; } arr[n - 1] = last; // Restore original last element if (i < n - 1 || arr[n - 1] == target) { return i; // Found } return -1; // Not found } int main(void) { int arr[] = {10, 20, 30, 40, 50}; int n = sizeof(arr) / sizeof(arr[0]); int index = sentinelSearch(arr, n, 30); printf("30 found at index %d\n", index); int notFound = sentinelSearch(arr, n, 100); printf("100 %s\n", notFound != -1 ? "found" : "not found"); return 0; }

Expected output:

text

30 found at index 2 100 not found

Note: Sentinel search modifies the array temporarily; it must be non-const.

Binary Search (Iterative)

Binary search works on sorted arrays by repeatedly dividing the search interval in half.

c

#include <stdio.h> int binarySearch(const int arr[], int n, int target) { int left = 0; int right = n - 1; while (left <= right) { int mid = left + (right - left) / 2; // Avoid overflow if (arr[mid] == target) { return mid; // Found } else if (arr[mid] < target) { left = mid + 1; // Search right half } else { right = mid - 1; // Search left half } } return -1; // Not found } int main(void) { int arr[] = {10, 20, 30, 40, 50, 60, 70}; int n = sizeof(arr) / sizeof(arr[0]); int index = binarySearch(arr, n, 40); printf("40 found at index %d\n", index); int notFound = binarySearch(arr, n, 100); printf("100 %s\n", notFound != -1 ? "found" : "not found"); return 0; }

Expected output:

text

40 found at index 3 100 not found

Time complexity: O(log n).

Binary Search (Recursive)

c

#include <stdio.h> int binarySearchRecursive(const int arr[], int left, int right, int target) { if (left > right) { return -1; // Base case: not found } int mid = left + (right - left) / 2; if (arr[mid] == target) { return mid; // Found } else if (arr[mid] < target) { return binarySearchRecursive(arr, mid + 1, right, target); } else { return binarySearchRecursive(arr, left, mid - 1, target); } } int main(void) { int arr[] = {10, 20, 30, 40, 50, 60, 70}; int n = sizeof(arr) / sizeof(arr[0]); int index = binarySearchRecursive(arr, 0, n - 1, 50); printf("50 found at index %d\n", index); return 0; }

Expected output:

text

50 found at index 4

Searching for First Occurrence (Sorted Array with Duplicates)

c

#include <stdio.h> int findFirstOccurrence(const int arr[], int n, int target) { int left = 0; int right = n - 1; int result = -1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { result = mid; // Record position right = mid - 1; // Continue searching left } else if (arr[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return result; } int main(void) { int arr[] = {10, 20, 20, 20, 30, 40, 50}; int n = sizeof(arr) / sizeof(arr[0]); int index = findFirstOccurrence(arr, n, 20); printf("First occurrence of 20 at index %d\n", index); return 0; }

Expected output:

text

First occurrence of 20 at index 1

Full Practical Code Example with Explanation

This program demonstrates multiple search algorithms in a realistic scenario: an employee database with search functionality.

Program: Employee Search System

c

/** * employee_search.c * Demonstrates linear search and binary search on employee records. * Includes search by ID (sorted) and search by name (linear). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <stdbool.h> #define MAX_EMPLOYEES 100 #define MAX_NAME_LEN 50 #define MAX_LINE_LEN 100 /* Structure to represent an employee */ typedef struct { int id; char name[MAX_NAME_LEN]; double salary; } Employee; /* Function prototypes */ int linearSearchById(const Employee employees[], int count, int targetId); int binarySearchById(const Employee employees[], int count, int targetId); int linearSearchByName(const Employee employees[], int count, const char *targetName); void sortEmployeesById(Employee employees[], int count); void displayEmployee(const Employee *emp); void displayAllEmployees(const Employee employees[], int count); int readInteger(const char *prompt, int *outValue); int main(void) { /* Pre-populated employee database (sorted by ID) */ Employee employees[MAX_EMPLOYEES] = { {1001, "Alice Johnson", 55000.0}, {1002, "Bob Smith", 62000.0}, {1003, "Charlie Brown", 48000.0}, {1004, "Diana Ross", 71000.0}, {1005, "Edward Wilson", 53000.0}, {1006, "Fiona Davis", 59000.0}, {1007, "George Miller", 67000.0}, {1008, "Hannah Lee", 51000.0} }; int employeeCount = 8; int choice; char buffer[MAX_LINE_LEN]; printf("=== Employee Search System ===\n"); while (1) { printf("\n--- Search Menu ---\n"); printf("1. Search by ID (Binary Search)\n"); printf("2. Search by ID (Linear Search)\n"); printf("3. Search by Name (Linear Search)\n"); printf("4. Display All Employees\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 targetId; if (readInteger("Enter employee ID to search: ", &targetId) != 0) { fprintf(stderr, "Invalid ID.\n"); break; } int index = binarySearchById(employees, employeeCount, targetId); if (index != -1) { printf("\nEmployee found:\n"); displayEmployee(&employees[index]); } else { printf("\nEmployee with ID %d not found.\n", targetId); } break; } case 2: { int targetId; if (readInteger("Enter employee ID to search: ", &targetId) != 0) { fprintf(stderr, "Invalid ID.\n"); break; } int index = linearSearchById(employees, employeeCount, targetId); if (index != -1) { printf("\nEmployee found:\n"); displayEmployee(&employees[index]); } else { printf("\nEmployee with ID %d not found.\n", targetId); } break; } case 3: { char targetName[MAX_NAME_LEN]; printf("Enter employee name to search: "); fflush(stdout); if (fgets(targetName, sizeof(targetName), stdin) == NULL) { fprintf(stderr, "Error reading name.\n"); break; } targetName[strcspn(targetName, "\n")] = '\0'; int index = linearSearchByName(employees, employeeCount, targetName); if (index != -1) { printf("\nEmployee found:\n"); displayEmployee(&employees[index]); } else { printf("\nEmployee '%s' not found.\n", targetName); } break; } case 4: displayAllEmployees(employees, employeeCount); break; case 5: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Linear search by ID: O(n) */ int linearSearchById(const Employee employees[], int count, int targetId) { for (int i = 0; i < count; i++) { if (employees[i].id == targetId) { return i; } } return -1; } /* Binary search by ID: O(log n), requires sorted array */ int binarySearchById(const Employee employees[], int count, int targetId) { int left = 0; int right = count - 1; while (left <= right) { int mid = left + (right - left) / 2; if (employees[mid].id == targetId) { return mid; } else if (employees[mid].id < targetId) { left = mid + 1; } else { right = mid - 1; } } return -1; } /* Linear search by name: O(n) */ int linearSearchByName(const Employee employees[], int count, const char *targetName) { for (int i = 0; i < count; i++) { if (strcmp(employees[i].name, targetName) == 0) { return i; } } return -1; } /* Sorts employees by ID (bubble sort for simplicity) */ void sortEmployeesById(Employee employees[], int count) { for (int i = 0; i < count - 1; i++) { for (int j = 0; j < count - i - 1; j++) { if (employees[j].id > employees[j + 1].id) { Employee temp = employees[j]; employees[j] = employees[j + 1]; employees[j + 1] = temp; } } } } /* Displays a single employee */ void displayEmployee(const Employee *emp) { printf("ID: %d\n", emp->id); printf("Name: %s\n", emp->name); printf("Salary: $%.2f\n", emp->salary); } /* Displays all employees */ void displayAllEmployees(const Employee employees[], int count) { printf("\n=== Employee List ===\n"); printf("%-6s %-20s %-10s\n", "ID", "Name", "Salary"); for (int i = 0; i < count; i++) { printf("%-6d %-20s $%-10.2f\n", employees[i].id, employees[i].name, employees[i].salary); } } /* 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 Search Algorithms

  1. Linear Search by ID: Traverses array sequentially, O(n).
  2. Binary Search by ID: Divides search interval in half, O(log n), requires sorted array.
  3. Linear Search by Name: Sequential string comparison using strcmp.
  4. Sorting: sortEmployeesById maintains sorted order for binary search.

Expected Output (sample run)

text

=== Employee Search System === --- Search Menu --- 1. Search by ID (Binary Search) 2. Search by ID (Linear Search) 3. Search by Name (Linear Search) 4. Display All Employees 5. Exit Enter choice: 1 Enter employee ID to search: 1005 Employee found: ID: 1005 Name: Edward Wilson Salary: $53000.00 --- Search Menu --- ... Enter choice: 3 Enter employee name to search: Diana Ross Employee found: ID: 1004 Name: Diana Ross Salary: $71000.00 --- Search Menu --- ... Enter choice: 2 Enter employee ID to search: 9999 Employee with ID 9999 not found. --- Search Menu --- ... Enter choice: 4 === Employee List === ID Name Salary 1001 Alice Johnson $55000.00 1002 Bob Smith $62000.00 1003 Charlie Brown $48000.00 1004 Diana Ross $71000.00 1005 Edward Wilson $53000.00 1006 Fiona Davis $59000.00 1007 George Miller $67000.00 1008 Hannah Lee $51000.00 --- Search Menu --- ... Enter choice: 5 Exiting.

Common Pitfalls, Edge Cases & Memory Considerations

1. Searching in Unsorted Array with Binary Search

Binary search requires a sorted array. Using it on unsorted data gives incorrect results.

c

int arr[] = {50, 10, 40, 20, 30}; // Unsorted binarySearch(arr, 5, 20); // May not find 20

Solution: Sort the array before using binary search, or use linear search.

2. Integer Overflow in Mid Calculation

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

c

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

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

3. Empty Array Access

Searching an empty array without checking causes undefined behavior.

c

int arr[] = {}; int n = 0; int index = binarySearch(arr, n, 10); // Accesses arr[0] if not careful

Solution: Check n == 0 before searching.

4. Duplicate Elements

Linear search returns the first occurrence; binary search may return any occurrence.

Solution: Use modified binary search to find first or last occurrence if needed.

5. String Comparison with ==

Using == to compare strings compares pointers, not contents.

c

if (employees[i].name == targetName) { // BUG: compares pointers }

Solution: Use strcmp for string comparison.

6. Sentinel Search Modifies Array

Sentinel search temporarily modifies the array, which is unsafe for const arrays.

Solution: Use standard linear search for const arrays, or make a copy.

7. Recursive Binary Search Stack Depth

Recursive binary search depth is O(log n), safe for typical arrays. However, for extremely large arrays, recursion adds overhead.

Solution: Use iterative binary search for large arrays.

8. Negative Indices

Returning -1 for “not found” is a convention, but it must be checked by the caller.

c

int index = search(arr, n, target); if (index != -1) { // Use arr[index] }

Solution: Always check the return value before using the index.

9. Cache Performance

Binary search jumps around memory, causing cache misses. Linear search has better cache locality for small arrays.

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

10. Memory Considerations

  • Search algorithms use O(1) extra space (except recursive binary search uses O(log n) stack space).
  • No dynamic memory allocation is needed.
  • For large datasets, consider using hash tables for O(1) average search.

Summary & Best Practices

Searching for elements in arrays is a fundamental operation. Linear search works on any array but is O(n); binary search is O(log n) but requires sorted arrays.

Key Takeaways

  • Linear search: O(n), works on unsorted arrays.
  • Binary search: O(log n), requires sorted arrays.
  • Sentinel search: Optimized linear search (modifies array temporarily).
  • Return -1 (or similar sentinel) when element not found.
  • Use left + (right - left) / 2 to avoid overflow.
  • String search requires strcmp, not ==.

Best Practices

  1. Choose the right algorithm based on array size and sortedness.
  2. Use linear search for small or unsorted arrays.
  3. Use binary search for large sorted arrays.
  4. Check for empty arrays before searching.
  5. Return a sentinel value (e.g., -1) for “not found”.
  6. Validate the search result before using the index.
  7. Use strcmp for string searches.
  8. Avoid integer overflow in mid calculation.
  9. Test with edge cases: empty array, single element, element at start/end, duplicate elements.
  10. Consider cache performance when choosing search strategy.

By mastering array searching, you’ll write efficient C programs for data retrieval and build a foundation for more advanced algorithms.

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.