Day 14 (Searching Advanced)

Day 14 (Searching Advanced)

Today, I delved into advanced searching problems, focusing on applying Binary Search in creative ways. I tackled challenges like allocating the minimum number of pages, determining the minimum capacity required to ship packages within a given number of days, and finding the smallest missing positive integer. Additionally, I solved the problem of finding the median of two sorted arrays and determining the number of times a sorted array is rotated. These problems enhanced my ability to adapt Binary Search to diverse scenarios, solidifying its role as a cornerstone of efficient problem-solving.

Check out my progress in my GitHub repository: github.com/AayJayTee/100-Days-DSA.


#include <iostream>
using namespace std;
#include <vector>

// Function to check if a given maximum allowed pages is valid
bool isValid(vector<int> &arr, int n, int k, int max_allowed_pages){
    int noofstudents = 1, pages = 0; // Start with one student and zero pages allocated
    for(int i = 0; i < n; i++){
        // If a single book exceeds max_allowed_pages, allocation is not possible
        if(arr[i] > max_allowed_pages){
            return false;  
        }
        // Add pages to the current student if within the limit
        if(pages + arr[i] <= max_allowed_pages){
            pages += arr[i];  
        } 
        // Assign to a new student if pages exceed the limit
        else {
            noofstudents++;    
            pages = arr[i];    
        }
    }
    // Check if the number of students required is within the allowed limit
    return noofstudents <= k;  
}

// Function to find the minimum number of pages to allocate
int min_no_pages(vector<int> &arr, int n, int k){
    // If students are more than books, allocation is not possible
    if(k > n){
        return -1;  
    }

    int sum = 0;
    for(int i = 0; i < n; i++){
        sum += arr[i];  // Calculate total pages
    }

    int start = 0, end = sum; // Binary search boundaries
    int ans = -1; // To store the minimum valid result

    while(start < end){
        int mid = start + (end - start) / 2;  // Calculate mid value (max allowed pages)
        // If mid is valid, it could be the answer
        if(isValid(arr, n, k, mid)){
            ans = mid;  
            end = mid;  // Narrow the search space to smaller valid values
        } 
        // If mid is not valid, increase the lower bound
        else {
            start = mid + 1;  
        }
    }
    return ans; // Return the minimum valid pages
}

int main()
{
    int n, k;
    cout << "Enter no of books: "; 
    cin >> n; // Input the number of books
    cout << "No. of pages of each book?: ";
    vector<int> arr(n);
    for(int i = 0; i < n; i++){
        cin >> arr[i]; // Input pages for each book
    }
    cout << "How many students?: ";
    cin >> k; // Input the number of students
    // Output the minimum pages that can be allocated
    cout << min_no_pages(arr, n, k) << endl; 
    return 0; // Indicate successful execution
}

This code solves the problem of allocating books to a given number of students such that the maximum number of pages assigned to a student is minimized. It uses a binary search approach to find the optimal solution. The isValid function checks if it's possible to allocate the books to k students without any student receiving more than max_allowed_pages by iterating through the books and tracking the current number of pages assigned to each student. If a student exceeds the page limit, a new student is assigned the next books. The min_no_pages function performs binary search over the possible values for the maximum allowed pages, ranging from the largest single book to the total sum of pages. It iteratively narrows down the search space, checking if a valid allocation exists for each midpoint value. The result is the smallest maximum pages that can be allocated to any student while keeping the number of students within the limit. The program takes input for the number of books, the number of students, and the pages in each book, then outputs the minimum number of pages that can be allocated. If the number of students exceeds the number of books, allocation is not possible.


Q2. Solve the "minimum capacity required to ship packages within d days" problem.

#include <iostream>
using namespace std;

// Function to calculate the number of days required to ship packages with a given capacity
int daysRequired(int weights[], int n, int capacity) {
    int days = 1, load = 0; // Initialize days and current load
    for (int i = 0; i < n; i++) {
        if (weights[i] + load > capacity) { // If adding current weight exceeds capacity
            days += 1; // Increment days
            load = weights[i]; // Start new load with current weight
        } else {
            load += weights[i]; // Add current weight to the load
        }
    }
    return days; // Return total days required
}

// Function to find the minimum capacity to ship within the given days
int noOfDays(int weights[], int n, int days) {
    int max = 0; // Variable to store the sum of all weights
    for (int i = 0; i < n; i++) {
        max += weights[i]; // Calculate total weight
    }

    int start = 0, end = max; // Initialize binary search boundaries
    while (start <= end) {
        int mid = (start + end) / 2; // Calculate mid as potential capacity
        if (daysRequired(weights, n, mid) <= days) { // If mid capacity works
            end = mid - 1; // Reduce the search space to find smaller capacity
        } else {
            start = mid + 1; // Increase the search space for larger capacity
        }
    }
    return start; // Return the minimum capacity found
}

int main() {
    int n, days;
    cout << "Enter no of containers: ";
    cin >> n; // Input number of containers
    cout << "Weight of each container?: ";
    int weights[n];
    for (int i = 0; i < n; i++) {
        cin >> weights[i]; // Input weights of containers
    }
    cout << "No of days? : ";
    cin >> days; // Input number of days
    // Output the minimum capacity required
    cout << noOfDays(weights, n, days) << endl; 
    return 0; // Indicate successful execution
}

This code solves the problem of determining the minimum capacity required for shipping containers within a given number of days. It utilizes a binary search approach to find the optimal capacity. The daysRequired function calculates the number of days needed to ship all containers given a particular capacity. It does this by iterating over the container weights and adding them to the current load; if the load exceeds the given capacity, it starts a new day with the current container. The noOfDays function performs a binary search over the possible capacities, ranging from the weight of the heaviest container to the total sum of all containers' weights. It checks for each mid-point capacity if the number of days required is within the allowed days. The program finally outputs the minimum capacity that ensures all containers are shipped within the given number of days. The input consists of the number of containers, their weights, and the number of days, and the output is the minimum shipping capacity required.


Q3. Solve the "find the smallest missing positive integer" problem.

#include <iostream>
using namespace std;
#include <algorithm>

// Function to find the smallest missing positive integer
int findSmallestMissing(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        // Place each positive number within the range [1, n] in its correct position
        while (arr[i] > 0 && arr[i] <= n && arr[arr[i] - 1] != arr[i]) {
            swap(arr[arr[i] - 1], arr[i]); // Swap to move the number to its correct index
        }
    }
    for (int i = 0; i < n; i++) {
        if (arr[i] != i + 1) {
            return i + 1; // Return the smallest missing positive integer
        }
    }
    return n + 1; // If all numbers from 1 to n are present, return n+1
}

int main() {
    int n;
    cout << "Enter length of array: ";
    cin >> n; // Input the length of the array
    cout << "Enter elements of the array: ";
    int arr[n];
    for (int i = 0; i < n; i++) {
        cin >> arr[i]; // Input the elements of the array
    }
    // Output the smallest missing positive integer
    cout << findSmallestMissing(arr, n);
    return 0; // Indicate successful execution
}

This code solves the problem of finding the smallest missing positive integer in an unsorted array. The findSmallestMissing function first rearranges the elements of the array so that each positive number within the range [1, n] is placed at the correct index (i.e., arr[i] should be at index arr[i] - 1). It uses a while loop to perform swaps until the array is partially sorted such that each valid number is in its correct position. After rearranging, the function then scans through the array to find the first index where the number is not equal to index + 1. This index+1 is the smallest missing positive integer. If no such index is found (meaning all numbers from 1 to n are present), the function returns n+1 as the smallest missing integer. The main function takes user input for the array size and its elements, then outputs the smallest missing positive integer.


#include <iostream>
using namespace std;
#include <vector>
#include <climits>

// Function to find the median of two sorted arrays
double find_median(vector<int> arr1, vector<int> arr2){
    int n1=arr1.size(); // Size of the first array
    int n2=arr2.size(); // Size of the second array

    // Ensure the first array is smaller to reduce the binary search range
    if(n1>n2) return find_median(arr2,arr1); 

    int low=0, high=n1; // Binary search boundaries
    int left=(n1+n2+1)/2; // Total number of elements in the left partition
    int n=n1+n2; // Total number of elements in both arrays

    while(low<=high){
        int mid1=(low+high); // Partition index for the first array
        int mid2=left-mid1; // Partition index for the second array

        // Left and right partition values for both arrays
        int l1=INT_MIN, l2=INT_MIN; 
        int r1=INT_MAX, r2=INT_MAX;

        // Assign right partition values if within bounds
        if(mid1<n1) r1=arr1[mid1];
        if(mid2<n2) r2=arr2[mid2];

        // Assign left partition values if within bounds
        if(mid1-1>=0) l1=arr1[mid1-1];
        if(mid2-1>=0) l2=arr2[mid2-1];

        // Check if the partition is valid
        if(l1<=r2 && l2<=r1){
            if(n%2==1) return max(l1,l2); // Return the max of left partition for odd-length arrays
            return ((double)(max(l1,l2)+min(r1,r2)))/2.0; // Return the average for even-length arrays
        }
        // Adjust binary search range
        else if(l1>r2) high=mid1-1; 
        else low=mid1+1;
    }
    return 0; // Default return value in case of failure
}

int main()
{   
    int n1,n2;
    cout<<"Enter length of first array: "; 
    cin>>n1; // Input size of the first array

    vector<int> arr1(n1);
    cout<<"Enter elements of the array: ";
    for(int i=0;i<n1;i++){
        cin>>arr1[i]; // Input elements of the first array
    }

    cout<<"Enter length of second array: ";
    cin>>n2; // Input size of the second array

    vector<int> arr2(n2);
    cout<<"Enter elements of the array: ";
    for(int i=0;i<n2;i++){
        cin>>arr2[i]; // Input elements of the second array
    }

    // Output the median of the two arrays
    cout<<"The median of the two arrays are: "<<find_median(arr1,arr2);

    return 0; // Indicate successful program execution
}

This code finds the median of two sorted arrays using a binary search approach to optimize the time complexity. The function find_median takes two sorted arrays, arr1 and arr2, as input. It ensures that the first array is the smaller one to reduce the search space. Then, it uses binary search to partition the two arrays such that the left partition contains half of the combined elements. The partition is adjusted by comparing values from both arrays to ensure valid partitions: the largest element of the left partition is smaller than the smallest element of the right partition. Once a valid partition is found, if the combined number of elements is odd, it returns the maximum of the left partition. If even, it returns the average of the largest element in the left partition and the smallest element in the right partition. The main function takes user inputs for the arrays and their lengths, then calls find_median to compute and output the median of the two arrays. The solution ensures optimal time complexity of O(log(min(n1, n2))).


Q5. Solve the "number of times a sorted array is rotated" problem.

#include <iostream>
using namespace std;

// Function to find the number of times a sorted array has been rotated
int no_of_times_rotated(int arr[], int n) {
    int low = 0, high = n - 1; // Initialize search boundaries

    while (low <= high) {
        // If the array is already sorted, it hasn't been rotated
        if (arr[low] <= arr[high])
            return low;

        int mid = low + (high - low) / 2; // Calculate mid to prevent overflow

        // Check if mid is the pivot (smallest element)
        int prev = (mid - 1 + n) % n; // Previous element (circular index)
        int next = (mid + 1) % n;     // Next element (circular index)

        if (arr[mid] <= arr[prev] && arr[mid] <= arr[next])
            return mid;

        // Decide the search space
        if (arr[mid] >= arr[low])
            low = mid + 1; // Search in the right half
        else
            high = mid - 1; // Search in the left half
    }

    return -1; // Return -1 if no rotation is found (edge case, should not occur)
}

int main() {
    int n;
    cout << "Enter length of array: "; 
    cin >> n; // Input array length
    cout << "Enter elements of the array: ";
    int arr[n];
    for (int i = 0; i < n; i++) {
        cin >> arr[i]; // Input array elements
    }
    // Output the number of rotations
    cout << "The number of times the array has been rotated: " << no_of_times_rotated(arr, n); 
    return 0; // Indicate successful execution
}

This code finds the number of times a sorted array has been rotated using a binary search approach. The function no_of_times_rotated identifies the pivot element (the smallest element) in the rotated array, which corresponds to the number of rotations. It does so by comparing the mid element with its neighbors in each iteration. If the array is already sorted (i.e., the element at the start is less than or equal to the element at the end), it returns 0 rotations. Otherwise, it adjusts the search boundaries by checking whether the left or right half is sorted, narrowing the search space until the pivot is found. The index of the pivot is returned, indicating the number of rotations. This solution has a time complexity of O(log n), making it efficient for large arrays.


Today's practice deepened my understanding of advanced applications of Binary Search. From solving allocation problems to calculating medians and rotations in arrays, I learned how versatile and powerful this algorithm is in tackling complex challenges. These exercises have equipped me with valuable techniques to handle intricate searching problems more effectively. I look forward to exploring even more advanced topics in the days ahead!