Fiveable
Fiveable
AP Computer Science A
Find gaps with guided practice
Guided practice grid visualization
Table of Contents

💻ap computer science a review

10.2 Recursive Searching and Sorting

Verified for the 2025 AP Computer Science A examLast Updated on June 18, 2024

Searching and sorting are fundamental operations in computer science, and recursion provides elegant solutions for both. This study guide explores how recursion can be applied to create efficient search algorithms like binary search and powerful sorting algorithms like merge sort. These recursive approaches demonstrate how complex problems can be broken down into simpler subproblems to achieve efficient solutions.

Binary Search: Divide and Conquer

Binary search is an efficient algorithm for finding a target value in a sorted collection. Unlike sequential search (checking each element one by one), binary search eliminates half of the remaining elements in each step.

  • The data must be sorted before using binary search
  • Works with String, arrays, or ArrayLists

How Binary Search Works

  1. Find the middle element of the collection
  2. Compare the middle element with the target value
  3. If they match, we've found the target
  4. If the target is less than the middle, search the left half
  5. If the target is greater than the middle, search the right half
  6. Repeat until the target is found or the search space is empty

Recursive Binary Search Implementation

public static int binarySearch(int[] arr, int target, int left, int right) {
    // Base case: element not found
    if (left > right) {
        return -1;
    }
    
    // Find middle index
    int mid = (left + right) / 2;
    
    // Base case: element found
    if (arr[mid] == target) {
        return mid;
    }
    // Recursive case: search left half
    else if (target < arr[mid]) {
        return binarySearch(arr, target, left, mid - 1);
    }
    // Recursive case: search right half
    else {
        return binarySearch(arr, target, mid + 1, right);
    }
}

// Initial call
int index = binarySearch(myArray, targetValue, 0, myArray.length - 1);

Binary Search with ArrayList

public static int binarySearch(ArrayList<Integer> list, int target, int left, int right) {
    if (left > right) {
        return -1;
    }
    
    int mid = (left + right) / 2;
    
    if (list.get(mid) == target) {
        return mid;
    }
    else if (target < list.get(mid)) {
        return binarySearch(list, target, left, mid - 1);
    }
    else {
        return binarySearch(list, target, mid + 1, right);
    }
}

Binary Search with Strings

public static int binarySearch(String[] arr, String target, int left, int right) {
    if (left > right) {
        return -1;
    }
    
    int mid = (left + right) / 2;
    
    int comparison = target.compareTo(arr[mid]);
    
    if (comparison == 0) {
        return mid;  // Found it!
    }
    else if (comparison < 0) {
        return binarySearch(arr, target, left, mid - 1);  // Search left
    }
    else {
        return binarySearch(arr, target, mid + 1, right);  // Search right
    }
}

Binary Search Efficiency

Search AlgorithmTime Complexity
Sequential SearchO(n) – Linear time
Binary SearchO(log n) – Logarithmic time

For large datasets, the efficiency improvement is dramatic:

Array SizeSequential Search (worst case)Binary Search (worst case)
1,0001,000 comparisons~10 comparisons
1,000,0001,000,000 comparisons~20 comparisons
1,000,000,0001,000,000,000 comparisons~30 comparisons

Let's trace the execution of binary search on the array [10, 20, 30, 40, 50, 60, 70] looking for the value 30:

Callleftrightmidarr[mid]Action
1st0634030 < 40, search left half
2nd0212030 > 20, search right half
3rd2223030 == 30, return 2

Merge Sort: Divide, Sort, and Merge

Merge sort is a recursive sorting algorithm that follows the divide-and-conquer approach:

  1. Divide: Split the array into two halves
  2. Conquer: Recursively sort each half
  3. Combine: Merge the sorted halves

Merge Sort Implementation

public static void mergeSort(int[] arr, int left, int right) {
    if (left < right) {
        // Find the middle point
        int mid = (left + right) / 2;
        
        // Sort first and second halves
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        
        // Merge the sorted halves
        merge(arr, left, mid, right);
    }
}

private static void merge(int[] arr, int left, int mid, int right) {
    // Calculate sizes of subarrays
    int n1 = mid - left + 1;
    int n2 = right - mid;
    
    // Create temporary arrays
    int[] leftArray = new int[n1];
    int[] rightArray = new int[n2];
    
    // Copy data to temp arrays
    for (int i = 0; i < n1; ++i)
        leftArray[i] = arr[left + i];
    for (int j = 0; j < n2; ++j)
        rightArray[j] = arr[mid + 1 + j];
    
    // Merge temp arrays
    int i = 0, j = 0;
    int k = left;
    while (i < n1 && j < n2) {
        if (leftArray[i] <= rightArray[j]) {
            arr[k] = leftArray[i];
            i++;
        } else {
            arr[k] = rightArray[j];
            j++;
        }
        k++;
    }
    
    // Copy remaining elements
    while (i < n1) {
        arr[k] = leftArray[i];
        i++;
        k++;
    }
    while (j < n2) {
        arr[k] = rightArray[j];
        j++;
        k++;
    }
}

// Initial call
mergeSort(myArray, 0, myArray.length - 1);

Merge Sort with ArrayList

public static void mergeSort(ArrayList<Integer> list, int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        
        mergeSort(list, left, mid);
        mergeSort(list, mid + 1, right);
        
        merge(list, left, mid, right);
    }
}

private static void merge(ArrayList<Integer> list, int left, int mid, int right) {
    // Create temporary lists
    ArrayList<Integer> leftList = new ArrayList<>();
    ArrayList<Integer> rightList = new ArrayList<>();
    
    // Copy data to temp lists
    for (int i = left; i <= mid; i++)
        leftList.add(list.get(i));
    for (int i = mid + 1; i <= right; i++)
        rightList.add(list.get(i));
    
    // Merge temp lists
    int i = 0, j = 0, k = left;
    while (i < leftList.size() && j < rightList.size()) {
        if (leftList.get(i) <= rightList.get(j)) {
            list.set(k, leftList.get(i));
            i++;
        } else {
            list.set(k, rightList.get(j));
            j++;
        }
        k++;
    }
    
    // Copy remaining elements
    while (i < leftList.size()) {
        list.set(k, leftList.get(i));
        i++;
        k++;
    }
    while (j < rightList.size()) {
        list.set(k, rightList.get(j));
        j++;
        k++;
    }
}

Visualizing Merge Sort

For the array [38, 27, 43, 3, 9, 82, 10]:

  1. Divide the array into [38, 27, 43, 3] and [9, 82, 10]
  2. Divide again: [38, 27], [43, 3], [9, 82], [10]
  3. Divide once more: [38], [27], [43], [3], [9], [82], [10]
  4. Merge sorted arrays: [27, 38], [3, 43], [9, 82], [10]
  5. Merge again: [3, 27, 38, 43], [9, 10, 82]
  6. Final merge: [3, 9, 10, 27, 38, 43, 82]

Merge Sort Efficiency

Sort AlgorithmTime Complexity
Selection SortO(n²) – Quadratic time
Insertion SortO(n²) – Quadratic time
Merge SortO(n log n) – Linearithmic time

For large datasets, the efficiency gain is significant:

Array SizeSelection/Insertion Sort (worst case)Merge Sort (worst case)
1,000~1,000,000 operations~10,000 operations
1,000,000~1,000,000,000,000 operations~20,000,000 operations

Iterative vs. Recursive Implementations

Both binary search and merge sort can be implemented iteratively or recursively:

// Iterative binary search
public static int binarySearchIterative(int[] arr, int target) {
    int left = 0;
    int right = arr.length - 1;
    
    while (left <= right) {
        int mid = (left + right) / 2;
        
        if (arr[mid] == target) {
            return mid;
        }
        else if (target < arr[mid]) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    
    return -1;
}

The recursive version can be more elegant, but both approaches have the same time complexity. The iterative version uses constant space, while the recursive version uses O(log n) space for the call stack.

Summary

Recursive searching and sorting algorithms demonstrate the power of the divide-and-conquer approach in computer science. Binary search provides efficient lookup in sorted collections by eliminating half of the remaining elements in each step, achieving logarithmic time complexity. Merge sort offers efficient sorting through recursive division and merging, achieving linearithmic time complexity. Both algorithms can be implemented recursively or iteratively, with recursive implementations often providing more elegant and intuitive solutions at the cost of additional memory usage for the call stack. These algorithms showcase how recursion can be applied to solve complex problems by breaking them down into simpler subproblems.

Key Terms to Review (36)

Abstraction/Method Writing: Abstraction is the process of simplifying complex systems by breaking them down into smaller, more manageable parts. Method writing refers to defining behaviors or actions that an object can perform.
Algorithms: Algorithms are step-by-step procedures or instructions for solving problems or performing tasks. They provide clear instructions on how to solve problems efficiently.
Algorithm Analysis: Algorithm analysis is the process of evaluating the efficiency and performance of an algorithm. It involves analyzing factors such as time complexity, space complexity, and scalability to determine how well an algorithm will perform in different scenarios.
Artificial Intelligence and Machine Learning: Artificial Intelligence (AI) refers to the development of computer systems capable of performing tasks that typically require human intelligence, such as speech recognition or decision-making. Machine Learning (ML), a subset of AI, involves training algorithms on large datasets to make predictions or take actions without being explicitly programmed.
Control Structures: Control structures are programming constructs that determine the flow of execution in a program. They allow you to make decisions and repeat actions based on certain conditions.
Data Structure: A data structure is a way of organizing and storing data in a computer so that it can be accessed and used efficiently. It provides a systematic way to manage and manipulate data.
Data Structures: Data structures are ways to organize, store, and manipulate data efficiently. They provide different methods of accessing, inserting, deleting, or searching for data elements based on specific requirements.
Discrete Math: Discrete math is a branch of mathematics that deals with countable and distinct objects. It focuses on topics like sets, logic, relations, functions, and combinatorics.
Encapsulation/Class Writing: Encapsulation is the practice of hiding internal details and providing a public interface for interacting with an object. Class writing involves defining the blueprint or template for creating objects in object-oriented programming.
Graphs: Graphs are data structures that consist of nodes (vertices) and edges, where the edges represent relationships between the nodes. They are used to model connections or relationships between different entities.
Graphics: Graphics involve creating visual representations using computers. It encompasses various techniques such as rendering 2D or 3D images, manipulating pixels, applying transformations, and simulating realistic effects like lighting and shadows.
GUIs (Graphical User Interfaces): GUIs, short for Graphical User Interfaces, are visual interfaces that allow users to interact with software applications using graphical elements such as buttons, menus, and windows. They provide an intuitive way for users to navigate through an application without needing extensive knowledge of programming languages.
Hash maps: Hash maps are data structures that store key-value pairs, where each key is unique. They use a hash function to convert the key into an index in an array, allowing for efficient retrieval and insertion of values.
Inheritance: Inheritance is a concept in object-oriented programming where a class inherits the properties and behaviors of another class. It allows for code reuse and promotes the creation of hierarchical relationships between classes.
Iterative Code: Iterative code refers to programming constructs or algorithms that involve repetition using loops. It allows for executing a block of code multiple times until certain conditions are met.
Linear Search: Linear search is a simple searching algorithm that sequentially checks each element in a list until it finds the target value or reaches the end of the list.
Logic and Proofs: Logic and proofs involve the study of formal reasoning, including deductive reasoning, symbolic logic, truth tables, and proof techniques such as direct proof, contrapositive proof, and proof by contradiction.
Memory Usage: Memory usage refers to the amount of computer memory (RAM) that a program or process requires to run. It is a measure of how much space is occupied by data and instructions in the computer's memory.
Memory Analysis: Memory analysis refers to the process of examining and understanding the contents of a computer's memory. It involves analyzing data structures, variables, and other information stored in memory to gain insights into how a program is functioning or to identify potential security vulnerabilities.
Merge Sort: Merge Sort is an efficient, comparison-based sorting algorithm that divides an unsorted list into smaller sublists, sorts those sublists recursively, and then merges them back together to obtain a sorted list.
Object-oriented programming (OOP): Object-oriented programming is a programming paradigm that organizes code into objects, which are instances of classes. It emphasizes the use of objects to represent real-world entities and their interactions.
Polymorphism: Polymorphism refers to the ability of objects to take on multiple forms or have multiple types. In programming, it allows different objects to be treated as instances of a common superclass, enabling flexibility and extensibility.
Probability: Probability is the branch of mathematics that studies uncertainty or randomness. It involves calculating the likelihood or chance that an event will occur based on mathematical models.
Programming Language Construction: Programming language construction involves designing and implementing programming languages. It includes creating syntax rules, defining data types, and developing compilers or interpreters that translate code written in a specific language into machine-readable instructions.
Recursive Call: A recursive call is when a function calls itself within its own code, allowing for repetitive execution of the same set of instructions.
Recursion: Recursion is a programming technique where a function calls itself to solve a problem by breaking it down into smaller, similar subproblems.
Runtime Analysis: Runtime analysis refers to analyzing the efficiency of an algorithm by measuring its execution time and memory usage. It helps determine how well an algorithm scales with input size.
Searching Algorithms: Searching algorithms are methods used to find specific elements or values within a collection of data.
Selection Sort: Selection Sort is a simple sorting algorithm that repeatedly finds the minimum element from an unsorted portion of the list and swaps it with the first element of that portion until the entire list is sorted.
Sequential Search: Sequential search is another term for linear search, where each element in a list is checked one by one until the target value is found or the end of the list is reached.
Sets: Sets are collections of unique elements with no specific order. They do not allow duplicate values and provide operations like union, intersection, and difference.
Sorting Algorithms: Sorting algorithms are techniques used to arrange elements in a specific order (e.g., ascending or descending) within a collection of data.
State Machines: State machines are computational models that consist of a set of states and transitions between those states. They are used to represent systems that change from one state to another based on inputs or events.
Trees: Trees are hierarchical data structures consisting of nodes connected by edges. Each node has zero or more child nodes, except for the root node which has no parent. Trees are commonly used for organizing data in a hierarchical manner.
Unit 7: Unit 7 refers to a specific section or module of study in the AP Computer Science A curriculum that focuses on topics such as arrays, ArrayLists, and searching algorithms.
Web Development: Web development refers to the process of creating and maintaining websites. It involves designing, coding, and implementing various elements such as web pages, user interfaces, and databases.