CoreTechED

Loading

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.