🧠
Binary Search for Upper Bound
💡 Using binary search leverages the sorted array to find the farthest valid pair for each element, improving over brute force but less efficient than two pointers.
Intuition
For each element nums[i], binary search for the largest index j > i where nums[j] - nums[i] ≤ K, then count pairs between i and j.
Algorithm
- Initialize count = 0.
- For each index i from 0 to n-2:
- Use binary search to find max index j > i with nums[j] - nums[i] ≤ K.
- Add (j - i) to count if j > i.
- Return count.
💡 Binary search reduces inner loop to logarithmic time but still requires iteration over all elements.
import bisect
def count_pairs_binary_search(nums, K):
count = 0
n = len(nums)
for i in range(n - 1):
target = nums[i] + K
j = bisect.bisect_right(nums, target, i + 1) - 1
if j > i:
count += j - i
return count
# Driver code
if __name__ == '__main__':
nums = [1, 3, 4, 6, 8]
K = 3
print(count_pairs_binary_search(nums, K)) # Output: 6
Line Notes
import bisectImport bisect module for binary search utilities
for i in range(n - 1)Iterate over each element except last
target = nums[i] + KCalculate max allowed value for pair
j = bisect.bisect_right(nums, target, i + 1) - 1Find rightmost index where nums[j] ≤ target
if j > i: count += j - iAdd number of valid pairs for current i
import java.util.*;
public class CountPairs {
public static int countPairsBinarySearch(int[] nums, int K) {
int count = 0, n = nums.length;
for (int i = 0; i < n - 1; i++) {
int target = nums[i] + K;
int j = upperBound(nums, target, i + 1, n - 1) - 1;
if (j > i) {
count += j - i;
}
}
return count;
}
private static int upperBound(int[] nums, int target, int left, int right) {
int ans = right + 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
ans = mid;
right = mid - 1;
}
}
return ans;
}
public static void main(String[] args) {
int[] nums = {1, 3, 4, 6, 8};
int K = 3;
System.out.println(countPairsBinarySearch(nums, K)); // Output: 6
}
}
Line Notes
for (int i = 0; i < n - 1; i++)Iterate over each element except last
int target = nums[i] + K;Calculate max allowed value for pair
int j = upperBound(nums, target, i + 1, n - 1) - 1;Find rightmost index where nums[j] ≤ target
if (j > i) { count += j - i; }Add number of valid pairs for current i
while (left <= right)Binary search loop to find upper bound
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int countPairsBinarySearch(const vector<int>& nums, int K) {
int count = 0;
int n = nums.size();
for (int i = 0; i < n - 1; i++) {
int target = nums[i] + K;
auto it = upper_bound(nums.begin() + i + 1, nums.end(), target);
int j = (int)(it - nums.begin()) - 1;
if (j > i) {
count += j - i;
}
}
return count;
}
int main() {
vector<int> nums = {1, 3, 4, 6, 8};
int K = 3;
cout << countPairsBinarySearch(nums, K) << endl; // Output: 6
return 0;
}
Line Notes
for (int i = 0; i < n - 1; i++)Iterate over each element except last
int target = nums[i] + K;Calculate max allowed value for pair
auto it = upper_bound(nums.begin() + i + 1, nums.end(), target);Find first element greater than target
int j = (int)(it - nums.begin()) - 1;Convert iterator to index for last valid pair
if (j > i) { count += j - i; }Add number of valid pairs for current i
function upperBound(nums, target, left, right) {
let ans = right + 1;
while (left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (nums[mid] <= target) {
left = mid + 1;
} else {
ans = mid;
right = mid - 1;
}
}
return ans;
}
function countPairsBinarySearch(nums, K) {
let count = 0;
const n = nums.length;
for (let i = 0; i < n - 1; i++) {
let target = nums[i] + K;
let j = upperBound(nums, target, i + 1, n - 1) - 1;
if (j > i) {
count += j - i;
}
}
return count;
}
// Test
const nums = [1, 3, 4, 6, 8];
const K = 3;
console.log(countPairsBinarySearch(nums, K)); // Output: 6
Line Notes
function upperBound(nums, target, left, right)Binary search helper to find first index > target
while (left <= right)Binary search loop
if (nums[mid] <= target)Move left pointer if mid value valid
let j = upperBound(nums, target, i + 1, n - 1) - 1;Find last valid index for pair
if (j > i) { count += j - i; }Add count of valid pairs for current i
For each element, binary search takes O(log n), repeated n times gives O(n log n). Constant extra space used.
💡 For n=100,000, this means about 1.5 million operations, which is efficient but slower than two pointers.
Interview Verdict: Accepted
This approach is acceptable but two pointers is simpler and faster in practice.