Given two arrays a1[] and a2[], find the maximum number of pairs (i, j), such that 2*a1[i] ≤ a2[j].
Note: Any array element can be part of a single pair.
Examples:
Input: a1[] = [3, 1, 2], a2[] = [3, 4, 2, 1]
Output: 2
Explanation: Only two pairs can be chosen:
(1, 3): Choose elements a1[2] and a2[1].
(2, 2): Choose elements a1[3] and a2[2].
Input: a1[] = [40], a2[] = [10, 20, 30, 40]
Output: 0
Explanation: There is no such pair exists.
Table of Content
[Naive Approach]: Using Sorting - O(n*m) Time and O(n+m) Space
The simplest approach is to first sort both the arrays and Then, for each element in a1[], calculate
2*a1[i] and find the first unused element in a2[] that is greater than or equal to this value, for finding the first unused element we will use a visited array, after we found a valid pair we will mark that element as used (using avisitedarray) and increment the pair count. This ensures each element in a2[] is used at most once while maximizing valid pairs.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int numberOfPairs(vector<int>& a1, vector<int>& a2) {
// Sort both arrays
sort(a1.begin(), a1.end());
sort(a2.begin(), a2.end());
int count = 0;
// Track used elements from a2
vector<bool> visited(a2.size(), false);
// Traverse a1[]
for (int i = 0; i < a1.size(); i++) {
int target = 2 * a1[i];
// Traverse a2[] to find first unused element >= target
for (int j = 0; j < a2.size(); j++) {
if (!visited[j] && a2[j] >= target) {
count++;
// Mark as used
visited[j] = true;
break;
}
}
}
return count;
}
// Driver code
int main() {
vector<int> a1 = {3, 1, 2};
vector<int> a2 = {3, 4, 2, 1};
int ans = numberOfPairs(a1, a2);
cout << ans << endl;
return 0;
}
import java.util.*;
public class Main {
static int numberOfPairs(int[] a1, int[] a2) {
// Sort both arrays
Arrays.sort(a1);
Arrays.sort(a2);
int count = 0;
// Track used elements from a2
boolean[] visited = new boolean[a2.length];
// Traverse a1[]
for (int i = 0; i < a1.length; i++) {
int target = 2 * a1[i];
// Traverse a2[] to find first unused element >= target
for (int j = 0; j < a2.length; j++) {
if (!visited[j] && a2[j] >= target) {
count++;
// Mark as used
visited[j] = true;
break;
}
}
}
return count;
}
// Driver code
public static void main(String[] args) {
int[] a1 = {3, 1, 2};
int[] a2 = {3, 4, 2, 1};
int ans = numberOfPairs(a1, a2);
System.out.println(ans);
}
}
def numberOfPairs(a1, a2):
# Sort both arrays
a1.sort()
a2.sort()
count = 0
# Track used elements from a2
visited = [False] * len(a2)
# Traverse a1[]
for i in range(len(a1)):
target = 2 * a1[i]
# Traverse a2[] to find first unused element >= target
for j in range(len(a2)):
if not visited[j] and a2[j] >= target:
count += 1
# Mark as used
visited[j] = True
break
return count
# Driver code
a1 = [3, 1, 2]
a2 = [3, 4, 2, 1]
ans = numberOfPairs(a1, a2)
print(ans)
using System;
class Program
{
static int NumberOfPairs(int[] a1, int[] a2)
{
// Sort both arrays
Array.Sort(a1);
Array.Sort(a2);
int count = 0;
// Track used elements from a2
bool[] visited = new bool[a2.Length];
// Traverse a1[]
for (int i = 0; i < a1.Length; i++)
{
int target = 2 * a1[i];
// Traverse a2[] to find first unused element >= target
for (int j = 0; j < a2.Length; j++)
{
if (!visited[j] && a2[j] >= target)
{
count++;
// Mark as used
visited[j] = true;
break;
}
}
}
return count;
}
// Driver code
static void Main()
{
int[] a1 = { 3, 1, 2 };
int[] a2 = { 3, 4, 2, 1 };
int ans = NumberOfPairs(a1, a2);
Console.WriteLine(ans);
}
}
function numberOfPairs(a1, a2) {
// Sort both arrays
a1.sort((a, b) => a - b);
a2.sort((a, b) => a - b);
let count = 0;
// Track used elements from a2
let visited = new Array(a2.length).fill(false);
// Traverse a1[]
for (let i = 0; i < a1.length; i++) {
let target = 2 * a1[i];
// Traverse a2[] to find first unused element >= target
for (let j = 0; j < a2.length; j++) {
if (!visited[j] && a2[j] >= target) {
count++;
// Mark as used
visited[j] = true;
break;
}
}
}
return count;
}
// Driver code
const a1 = [3, 1, 2];
const a2 = [3, 4, 2, 1];
const ans = numberOfPairs(a1, a2);
console.log(ans);
Output
2
[Expected Approach 1]: Using Two Pointers – O(n*logn + m*logm) Time and O(1) Space
In this, we will first sort both arrays, and then the idea is to use the Two-Pointer Technique to find an element in a2[] that is just greater than or equal to
2*a1[i]. We start with two pointers — one for a1[] and one for a2[]. If a2[j] is greater than or equal to2*a1[i], we have found a valid pair, so we increment both pointers. Otherwise, we move thea2pointer ahead to find a suitable match. This process continues until we traverse one of the arrays, ensuring the maximum number of valid pairs.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int numberOfPairs(vector<int>& a1, vector<int>& a2) {
// Sort both arrays
sort(a1.begin(), a1.end());
sort(a2.begin(), a2.end());
int count = 0;
// Two pointers
int i = 0, j = 0;
// Traverse both arrays
while (i < a1.size() && j < a2.size()) {
int target = 2 * a1[i];
// If a2[j] satisfies condition, form a pair
if (a2[j] >= target) {
count++;
i++;
j++;
}
// Otherwise, move j to find a bigger element
else {
j++;
}
}
return count;
}
// Driver code
int main() {
vector<int> a1 = {3, 1, 2};
vector<int> a2 = {3, 4, 2, 1};
int ans = numberOfPairs(a1, a2);
cout << ans << endl;
return 0;
}
import java.util.Arrays;
public class Main {
static int numberOfPairs(int[] a1, int[] a2) {
// Sort both arrays
Arrays.sort(a1);
Arrays.sort(a2);
int count = 0;
int i = 0, j = 0;
// Traverse both arrays using two pointers
while (i < a1.length && j < a2.length) {
int target = 2 * a1[i];
// If a2[j] satisfies condition, form a pair
if (a2[j] >= target) {
count++;
i++;
j++;
}
// Otherwise, move j to find a bigger element
else {
j++;
}
}
return count;
}
// Driver code
public static void main(String[] args) {
int[] a1 = {3, 1, 2};
int[] a2 = {3, 4, 2, 1};
int ans = numberOfPairs(a1, a2);
System.out.println(ans);
}
}
def numberOfPairs(a1, a2):
# Sort both lists
a1.sort()
a2.sort()
count = 0
# Two pointers
i, j = 0, 0
# Traverse both lists
while i < len(a1) and j < len(a2):
target = 2 * a1[i]
# If a2[j] satisfies condition, form a pair
if a2[j] >= target:
count += 1
i += 1
j += 1
# Otherwise, move j to find a bigger element
else:
j += 1
return count
# Driver code
if __name__ == "__main__":
a1 = [3, 1, 2]
a2 = [3, 4, 2, 1]
ans = numberOfPairs(a1, a2)
print(ans)
using System;
class Program
{
static int NumberOfPairs(int[] a1, int[] a2)
{
// Sort both arrays
Array.Sort(a1);
Array.Sort(a2);
int count = 0;
int i = 0, j = 0;
// Two-pointer traversal
while (i < a1.Length && j < a2.Length)
{
int target = 2 * a1[i];
// If a2[j] satisfies the condition, form a pair
if (a2[j] >= target)
{
count++;
i++;
j++;
}
else
{
j++;
}
}
return count;
}
// Driver code
static void Main()
{
int[] a1 = { 3, 1, 2 };
int[] a2 = { 3, 4, 2, 1 };
int ans = NumberOfPairs(a1, a2);
Console.WriteLine(ans);
}
}
function numberOfPairs(a1, a2) {
// Sort both arrays
a1.sort((a, b) => a - b);
a2.sort((a, b) => a - b);
let count = 0;
// Two pointers
let i = 0, j = 0;
// Traverse both arrays
while (i < a1.length && j < a2.length) {
let target = 2 * a1[i];
// If a2[j] satisfies condition, form a pair
if (a2[j] >= target) {
count++;
i++;
j++;
}
// Otherwise, move j to find a bigger element
else {
j++;
}
}
return count;
}
// Driver code
const a1 = [3, 1, 2];
const a2 = [3, 4, 2, 1];
const ans = numberOfPairs(a1, a2);
console.log(ans);
Output
2
[Expected Approach 2]: Using Max Heap– O(n*logn + m*logm) Time and O(1) Space
The idea is to use the Greedy Algorithm for finding an element in a2[] that is just greater than or equal to the value 2*a1[i], we will use max heap which will arrange all the elements of a2[] in descending order and it's top element represents the largest element which will allows us efficient access and removal of the largest element each time, which helps in finding a suitable pair for each element of
a1[].
Steps to solve the problem:
- Sort the array a1[] and initialize a variable ans to store the maximum number of pairs.
- Add all the elements of a2[] in a Max Heap.
- Traverse the array a1[] from i = (n - 1) to 0 in non-increasing order.
- For each element a1[i], remove the peek element from the Max Heap until 2*a1[i] becomes smaller than or equal to the peek element and increment ans by 1 if such element is found.
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int numberOfPairs(vector<int> &a1, vector<int> &a2)
{
int n = a1.size();
int m = a2.size();
priority_queue<int> pq;
int i, j;
// Sort the array a1[]
sort(a1.begin(),a1.end());
// Push all arr2[] into Max Heap
for (j = 0; j < m; j++) {
pq.push(a2[j]);
}
int ans = 0;
// Traverse the arr1[] in decreasing order
for (i = n - 1; i >= 0; i--) {
// Remove element until a
// required pair is found
if (pq.top() >= 2 * a1[i]) {
ans++;
pq.pop();
}
}
return ans;
}
int main()
{
vector<int> a1 = {3, 1, 2};
vector<int> a2 = {3, 4, 2, 1};
cout << numberOfPairs(a1, a2);
return 0;
}
import java.util.*;
public class Main {
static int numberOfPairs(int[] a1, int[] a2) {
int n = a1.length;
int m = a2.length;
// Max Heap to add values of arr2[]
PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());
// Sort the array arr1[]
Arrays.sort(a1);
// Push all arr2[] into Max Heap
for (int j = 0; j < m; j++) {
pq.add(a2[j]);
}
int ans = 0;
// Traverse the arr1[] in decreasing order
for (int i = n - 1; i >= 0; i--) {
// Remove element until a required pair is found
if (!pq.isEmpty() && pq.peek() >= 2 * a1[i]) {
ans++;
pq.poll();
}
}
return ans;
}
public static void main(String[] args) {
// Given arrays
int[] a1 = {3, 1, 2};
int[] a2 = {3, 4, 2, 1};
int N = a1.length;
int M = a2.length;
System.out.println(numberOfPairs(a1, a2));
}
}
import heapq
def numberOfPairs(a1, a2):
n = len(a1)
m = len(a2)
# Max Heap to add values of arr2[]
pq = []
# Sort the array arr1[]
a1.sort()
# Push all arr2[] into Max Heap (use negative for max-heap behavior)
for j in range(m):
heapq.heappush(pq, -a2[j])
ans = 0
# Traverse the arr1[] in decreasing order
for x in reversed(a1):
if pq and -pq[0] >= 2 * x:
ans += 1
heapq.heappop(pq)
return ans
if __name__ == '__main__':
# Given arrays
a1 = [3, 1, 2]
a2 = [3, 4, 2, 1]
print(numberOfPairs(a1, a2))
using System;
using System.Collections.Generic;
class GFG
{
static int NumberOfPairs(int[] a1, int[] a2)
{
int n = a1.Length;
int m = a2.Length;
// Max Heap to add values of arr2[]
List<int> pq = new List<int>();
int i, j;
// Sort the array arr1[]
Array.Sort(a1);
// Push all arr2[] into Max Heap
for (j = 0; j < m; j++)
{
pq.Add(a2[j]);
}
int ans = 0;
// Traverse the arr1[] in decreasing order
for (i = n - 1; i >= 0; i--)
{
if (pq.Count == 0)
break;
// Sort pq in descending order (simulate max-heap behavior)
pq.Sort((a, b) => b.CompareTo(a));
// Remove element until a required pair is found
if (pq[0] >= 2 * a1[i])
{
ans++;
pq.RemoveAt(0);
}
}
return ans;
}
static void Main()
{
// Given arrays
int[] a1 = { 3, 1, 2 };
int[] a2 = { 3, 4, 2, 1 };
Console.WriteLine(NumberOfPairs(a1, a2));
}
}
function numberOfPairs(a1, a2) {
const n = a1.length;
const m = a2.length;
// Max Heap to add values of arr2[]
let pq = [];
// Sort the array arr1[]
a1.sort((a, b) => a - b);
// Push all arr2[] into Max Heap
for (let j = 0; j < m; j++)
pq.push(a2[j]);
let ans = 0;
// Traverse the arr1[] in decreasing order
let i = n - 1;
while (i >= 0) {
// Sort pq in decreasing order (simulate max heap)
pq.sort((a, b) => b - a);
if (pq.length === 0)
break;
// Remove element until a required pair is found
if (pq[0] >= 2 * a1[i]) {
ans += 1;
pq.shift(); // remove top element
}
i -= 1;
}
return ans;
}
// Driver Code
let a1 = [3, 2, 1];
let a2 = [3, 4, 2, 1];
// Function Call
console.log(numberOfPairs(a1, a2));
Output
2