Topics
Given two arrays of size and of size and an integer . Create a new array of size consisting of for . Find the -th smallest element in the array .
Idea
This problem can be solved by applying the binary search boolean array framework. We are essentially searching for the K-th smallest element, which can be rephrased as finding the smallest value x
such that there are at least k
elements in C
less than or equal to x
.
We can use binary search on the answer space for x
. For a value mid
, our check(mid)
function (defined below) returns true
if there are at least k
elements in C
that are less than or equal to mid
. The conceptual boolean array in this case consists of [F, F, F, ..., T, T, T]
, where each element represents whether there are at least k
elements in array C less than or equal to the value represented by that index. We want to find the first T
from this boolean array.
We can check how many pairs in array C have sum less than or equal to mid
as follows: For each element in A, we can find number of elements in B that forms a sum mid
. This can be done in by using binary search, or by using two pointers technique.
Tip
The code swaps
arr1
andarr2
ifn > m
. This ensures thatarr1
is always the smaller array. The outer loop in thecheck
function iterates througharr1
. Therefore, makingarr1
the smaller array reduces the number of iterations in the outer loop, providing a minor optimization.
Time Complexity:
- Using Binary Search: where = maximum possible sum in array C
- Using Two Pointers:
Space Complexity:
Code
bool check(int mid, vector<int> &arr1, vector<int> &arr2, ll k) {
// check if num items in sums array <= mid, is atleast k
ll count = 0;
for (int &x : arr1) {
// count how many sums of it are <= mid
int ub = mid - x;
count += upper_bound(all(arr2), ub) - arr2.begin();
}
return count >= k;
}
void solve() {
int n, m;
ll k;
cin >> n >> m >> k;
vector<int> arr1(n), arr2(m);
for (int i = 0; i < n; ++i)
cin >> arr1[i];
for (int i = 0; i < m; ++i)
cin >> arr2[i];
if (n > m) {
swap(n, m);
swap(arr1, arr2);
}
sort(all(arr1));
sort(all(arr2));
int lo = arr1[0] + arr2[0];
int hi = arr1[n - 1] + arr2[m - 1];
int ans = hi;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (check(mid, arr1, arr2, k)) {
ans = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
cout << ans << endl;
}
Using Two Pointers:
Instead of using binary search inside the check
function, we can use the two pointers technique. Assume both arr1
and arr2
are sorted. We initialize ptr1 = 0
(pointing to arr1
) and ptr2 = m - 1
(pointing to arr2
). For each element arr1[ptr1]
, we decrement ptr2
until arr1[ptr1] + arr2[ptr2] <= mid
. The count of valid pairs for arr1[ptr1]
would be ptr2 + 1
. This eliminates the inner binary search, replacing the factor with . We just need the check
function to use the following logic:
bool check(int mid, vector<int> &arr1, vector<int> &arr2, ll k) {
ll count = 0;
int ptr2 = arr2.size() - 1;
for(int &x : arr1){
while(ptr2 >= 0 && x + arr2[ptr2] > mid) ptr2--;
count += (ptr2 + 1);
}
return count >= k;
}