/**
* Given two sorted arrays A, B of size m and n respectively. Find the k-th
* smallest element in the union of A and B. You can assume that there are no
* duplicate elements.
*
* Tags: Array,
*/
class KthSmallestSortedArrays {
public static void main(String[] args) {
int[] A = { 1, 2, 3, 4, 5, 6 };
int[] B = { 2, 3, 4, 5, 6, 7 };
System.out.println(kthSmallest(0, A, B));
System.out.println(kthSmallest(1, A, B));
System.out.println(kthSmallest(10, A, B));
System.out.println(kthSmallest(11, A, B));
System.out.println(kthSmallest(12, A, B));
System.out.println(kthSmallest(13, A, B));
}
/**
* Binary Search of A and B
* O(lg m + lg n)
* make i + j = k - 1
* If Bj-1 < Ai < Bj, then Ai must be the k-th smallest,
* or else if Ai-1 < Bj < Ai, then Bj must be the k-th smallest.
*/
public static int kthSmallestRec(int k, int[] A, int[] B) {
/*keep smaller array first one*/
int n = A.length;
int m = B.length;
if (n > m)
return kthSmallestRec(k, B, A);
int k = (n + m - 1) / 2;
int l = 0, r = Math.min(k, n); // r is n, NOT n-1, this is important!!
while (l < r) {
int midA = (l + r) / 2;
int midB = k - midA;
if (A[midA] < B[midB])
l = midA + 1;
else
r = midA;
}
// after binary search, we almost get the median because it must be between these 4 numbers: A[l-1], A[l], B[k-l], and B[k-l+1]
int a = Math.max(l > 0 ? A[l - 1] : Integer.MIN_VALUE, k - l >= 0 ? B[k - l] : Integer.MIN_VALUE);
if (((n + m) & 1) == 1) // total number is odd
return (double) a;
/*total number is even*/
int b = Math.min(l < n ? A[l] : Integer.MAX_VALUE, k - l + 1 < m ? B[k - l + 1] : Integer.MAX_VALUE);
return (a + b) / 2.0; // median of 2
}
/**
* Two pointers start from the head of A & B
* O(k)
*/
public static int kthSmallest(int k, int[] A, int[] B) {
k -= 1;
int lenA = A.length;
int lenB = B.length;
if (k < 0 || k > lenA + lenB - 1) return -1; // out of range, 1 <= k <= A.length + B.length
if (k == 0) return A[0] < B[0] ? A[0] : B[0];
if (k == lenA + lenB - 1) return A[lenA - 1] < B[lenB - 1] ? B[lenB - 1] : A[lenA - 1];
int i = 0;
int j = 0;
while (i + j < k) {
if (A[i] < B[j] && i < A.length) {
i++;
} else if (A[i] >= B[j] && j < B.length){
j++;
}
if (i + j == k && A[i] < B[j]) {
return A[i];
} else if (i + j == k && A[i] >= B[j]) {
return B[j];
}
}
return -1;
}
}