• Floor: The largest element in the array that is less than or equal to the target.
  • Ceil: The smallest element in the array that is greater than or equal to the target.

Problem Statement

Given a sorted array nums and an integer x. Find the floor and ceil of x in nums. The floor of x is the largest element in the array which is smaller than or equal to x. The ceiling of x is the smallest element in the array greater than or equal to x. If no floor or ceil exists, output -1.

Examples

Example 1:

Input: nums = [3, 4, 4, 7, 8, 10], x = 5
Output: 4, 7

Explanation: The floor value of 5 in the nums array is 4, and the ceiling value of 5 in the nums array is 7.
Example 2:

Input: nums = [3, 4, 4, 7, 8, 10], x = 8
Output: 8, 8

Explanation: As the element 8 exists in the array, so it itself is the floor and ceil value.
Example 3:

Input: nums = [3, 4, 4, 7, 8, 10], x = 11
Output: 10, -1

Explanation: As the element 11 is greater than every element of the nums array, so its floor value is the last element which is 10 and its ceil value is -1, as it itself does not exists and ther are no element which is greater than it.

Different Approaches

1️⃣ Linear Search (Brute Force)

Floor Problem Using Linear Search

Algorithm:
  1. Traverse the array from left to right.
  2. Keep track of the largest element that is less than or equal to the target. If found, update the result.
  3. After traversing the entire array, return the result.
Dry Run:

For target = 5 and array = [1, 2, 4, 6, 8, 10]:

  • Start with res = -1.
  • Traverse the array:
    • At index 0, arr[0] = 1 (≤ 5), so update res = 1.
    • At index 1, arr[1] = 2 (≤ 5), so update res = 2.
    • At index 2, arr[2] = 4 (≤ 5), so update res = 4.
    • At index 3, arr[3] = 6 (> 5), no update.
    • Continue, but no updates as all other elements are greater than 5.
  • Final result: res = 4.
Code:
#include <iostream>
#include <vector>
using namespace std;

int findFloorLinear(const vector<int>& arr, int target) {
    int res = -1;  // Initialize to handle cases when no floor is found
    
    for (int i = 0; i < arr.size(); ++i) {
        if (arr[i] <= target) {
            res = arr[i];  // Update if arr[i] is less than or equal to the target
        }
    }
    
    return res;
}

int main() {
    vector<int> arr = {1, 2, 4, 6, 8, 10};
    int target = 5;
    
    int floorValue = findFloorLinear(arr, target);
    if (floorValue != -1)
        cout << "Floor of " << target << " is " << floorValue << endl;
    else
        cout << "Floor not found" << endl;

    return 0;
}

Ceil Problem Using Linear Search

Algorithm:
  1. Traverse the array from left to right.
  2. Keep track of the smallest element that is greater than or equal to the target. If found, update the result.
  3. After traversing the entire array, return the result.
Dry Run:

For target = 5 and array = [1, 2, 4, 6, 8, 10]:

  • Start with res = -1.
  • Traverse the array:
    • At index 0, arr[0] = 1 (< 5), no update.
    • At index 1, arr[1] = 2 (< 5), no update.
    • At index 2, arr[2] = 4 (< 5), no update.
    • At index 3, arr[3] = 6 (≥ 5), so update res = 6.
    • Continue, but no updates as 6 is the smallest value greater than 5.
  • Final result: res = 6.
Code:
#include <iostream>
#include <vector>
using namespace std;

int findCeilLinear(const vector<int>& arr, int target) {
    int res = -1;  // Initialize to handle cases when no ceil is found
    
    for (int i = 0; i < arr.size(); ++i) {
        if (arr[i] >= target) {
            if (res == -1 || arr[i] < res) {
                res = arr[i];  // Update if arr[i] is closer to the target
            }
        }
    }
    
    return res;
}

int main() {
    vector<int> arr = {1, 2, 4, 6, 8, 10};
    int target = 5;
    
    int ceilValue = findCeilLinear(arr, target);
    if (ceilValue != -1)
        cout << "Ceil of " << target << " is " << ceilValue << endl;
    else
        cout << "Ceil not found" << endl;

    return 0;
}

Complexity Analysis:

  • Time Complexity:
    • Linear Search for Floor: We have to check every element, so the time complexity is O(n).
    • Linear Search for Ceil: Similarly, we have to check every element, so the time complexity is O(n).
  • Space Complexity:
    • Both approaches have a space complexity of O(1) since no extra space is used apart from a few variables.

3️⃣ Binary Search (Optimal) Approach

Intuition:

For getting the floor value of x in nums, divide the array in two halves and compare the middle values with x. When x is less than or equal to the middle element, store the middle element as answer and will try to find a larger number that satisfies the same condition.
From the given statements, we can understand that the ceiling value of x, is basically the lower bound of x.

Approach:

Find floor
  1. Use two pointers, low and high initialized with the first and last indexes which will make our search space. Use an answer variable initialized with -1 to store the floor value.
  2. Calculate the middle element, and compare its value as described below :
    1. When the middle element is less than or equal to the given element, store it as a possible answer and eliminate the left half to look for a greater element in the right part of the array.
      1. In case the middle element is greater than the given element, eliminate the right half and reduce the search space to left half to look for a smaller element.
      2. Repeat these steps until the low pointer lesser than or equal to the high pointer.

Dry Run:

       +-----+-----+-----+-----+-----+-----+
nums = |  3  |  4  |  4  |  7  |  8  |  9  |
       +-----+-----+-----+-----+-----+-----+
          0     1     2     3     4     5

Target = 5
First Iteration:
Target = 5

Left = 0
Right = 5
floorVal = -1
Mid = Left + (Right - Left) / 2
      0 + (5 - 0) / 2 = 2

       +-----+-----+-----+-----+-----+-----+
 arr = |  3  |  4  |  4  |  7  |  8  |  9  |
       +-----+-----+-----+-----+-----+-----+
          0     1     2     3     4     5
          ^           ^                 ^
          |           |                 |
         Left        Mid              Right

Since arr[Mid] <= Target
	4 <= 5
	True

We would set the search space to the right side
	floorVal = arr[Mid]
	floorVal = 4
	Left = Mid + 1
Second Iteration:
Target = 5

Left = 3
Right = 5
floorVal = 4
Mid = Left + (Right - Left) / 2
      3 + (5 - 3) / 2 = 3 + 2/2 = 4

       +-----+-----+-----+-----+-----+-----+
 arr = |  3  |  4  |  4  |  7  |  8  |  9  |
       +-----+-----+-----+-----+-----+-----+
          0     1     2     3     4     5
                            ^     ^     ^
                            |     |     |
                           Left  Mid   Right

Since arr[Mid] <= Target
	8 <= 5
	false

We would set the search space to the left side, floorVal will remain unchanged
	
	Right = Mid - 1
	Right = 4 - 1 = 3
Third Iteration:
Target = 5

Left = 3
Right = 3
floorVal = 4
Mid = Left + (Right - Left) / 2
      3 + (3 - 3) / 2 = 3 + 0 = 3

       +-----+-----+-----+-----+-----+-----+
 arr = |  3  |  4  |  4  |  7  |  8  |  9  |
       +-----+-----+-----+-----+-----+-----+
          0     1     2     3     4     5
                            ^
                            |
                           Left
                           Mid
                           Right

Since arr[Mid] <= Target
	7 <= 5
	false

We would set the search space to the left side, floorVal will remain unchanged
	
	Right = Mid - 1
	Right = 3 - 1 = 2
Loop Termination:
floorVal = 4
       +-----+-----+-----+-----+-----+-----+
 arr = |  3  |  4  |  4  |  7  |  8  |  9  |
       +-----+-----+-----+-----+-----+-----+
          0     1     2     3     4     5
                      ^     ^
                      |     |
                    Right  Left
                           Mid
Since Left <= Right
		3 <= 2
		false
Hence the loop will be terminated,
and return floorVal.

Code:

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

// Helper function to find the floor of x
    int findFloor(vector<int>& nums, int n, int x) {
        int low = 0, high = n - 1;
        int ans = -1;

        // Perform binary search to find floor value
        while (low <= high) {
            int mid = low + (high - low) / 2;
            
            /*Check if mid element lesser than 
			      or equal to x, if it is update ans 
		        and eliminate the left half */
            if (nums[mid] <= x) {
                // Go right
                ans = nums[mid];
                low = mid + 1;
            } else {
                // Go left
                high = mid - 1;
            }
        }
        return ans;
    }

int main() {
	vector<int> nums = {3, 4, 4, 7, 8, 9};
	int target = 5;
	int floor = findFloor(nums, nums.size(), target);
}

Complexity Analysis:

  • Time Complexity:O(logN), where N is the size of the given array. We are using the Binary Search algorithm, which divides the search space in half each time, resulting in a logarithmic time complexity.
  • Space Complexity:O(1), as we are not using any extra space to solve this problem.

Find Ceil

  • For calculating the ceil value, follow a similar approach, but compare the values depending upon the below points:
    • In this case, store the middle element as a possible answer when the middle elements value is greater than or equal to the given element. Eliminate the right half to look for a smaller element in the left half which satisfies the same condition.
    • When the middle element is smaller than the given element, eliminate the left half to search for a larger element in the right half of the given array.

Algorithm

  1. Set left = 0 and right = n - 1.
  2. Initialize res = -1 to store the potential ceil element.
  3. While left <= right:
    • Calculate mid = left + (right - left) / 2.
    • If arr[mid] is greater than or equal to the target, update res = arr[mid] (as it's a potential ceil) and move right to mid - 1.
    • Otherwise, move left to mid + 1.
  4. Return res, which will contain the ceil of the target.

Code:

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

// Helper function to find the ceil of x
 int findCeil(vector<int>& nums, int n, int x) {
        int low = 0, high = n - 1;
        int ans = -1;

        // Perform binary search to find ceil value
        while (low <= high) {
            int mid = (low + high) / 2;
            
             /*Check if mid element greater than 
			      or equal to x, if it is update ans 
		        and eliminate the left half */
            if (nums[mid] >= x) {
                // Go left
                ans = nums[mid];
                high = mid - 1;
            } else {
                // Go right
                low = mid + 1;
            }
        }

        return ans;
    }
    
int main() {
	vector<int> nums = {3, 4, 4, 7, 8, 9};
	int target = 5;
	int floor = findCeil(nums, nums.size(), target);
}

Complexity Analysis:

  • Time Complexity:O(logN), where N is the size of the given array. We are using the Binary Search algorithm, which divides the search space in half each time, resulting in a logarithmic time complexity.
  • Space Complexity:O(1), as we are not using any extra space to solve this problem.

Combined Code:

class Solution {
public:
    // Function to find the floor and ceiling values of 'x' in a sorted array 'nums'
    vector<int> getFloorAndCeil(vector<int> nums, int x) {
        int left = 0;                   // Initialize left pointer to the start of the array
        int right = nums.size() - 1;    // Initialize right pointer to the end of the array
        int floor = -1;                 // Default value for floor if no floor is found

        // Binary search to find the floor of 'x'
        while(left <= right) {
            int mid = left + (right - left) / 2; // Calculate mid-point to avoid overflow

            if(nums[mid] == x) {        // If mid element equals 'x'
                floor = nums[mid];      // Set floor to x
                // Optional: break here as both floor and ceil are 'x'
                left = mid + 1;         // Move right to check for duplicate elements of 'x'
            } else if(nums[mid] > x) {  // If mid element is greater than 'x'
                right = mid - 1;        // Move to the left half
            } else {                    // If mid element is less than 'x'
                floor = nums[mid];      // Update floor to current mid element
                left = mid + 1;         // Move to the right half
            }
        }

        // Reset left and right pointers to search for the ceiling of 'x'
        left = 0;
        right = nums.size() - 1;
        int ceil = -1;                  // Default value for ceil if no ceil is found

        // Binary search to find the ceiling of 'x'
        while(left <= right) {
            int mid = left + (right - left) / 2; // Calculate mid-point to avoid overflow

            if(nums[mid] == x) {        // If mid element equals 'x'
                ceil = nums[mid];       // Set ceil to x
                // Optional: break here as both floor and ceil are 'x'
                right = mid - 1;        // Move left to check for duplicate elements of 'x'
            } else if (nums[mid] > x) { // If mid element is greater than 'x'
                ceil = nums[mid];       // Update ceil to current mid element
                right = mid - 1;        // Move to the left half
            } else {                    // If mid element is less than 'x'
                left = mid + 1;         // Move to the right half
            }
        }

        return {floor, ceil};           // Return the values of floor and ceil
    }
};