1885. Count Pairs in Two Arrays
Problem Description
The problem is to determine the number of specific pairs of indices in two arrays, nums1
and nums2
. Both arrays have the same length n
. A valid pair (i, j)
must satisfy two conditions: i < j
(meaning the first index must be less than the second one), and nums1[i] + nums1[j] > nums2[i] + nums2[j]
(meaning the sum of nums1
elements at these indices is greater than the sum of nums2
elements at the same indices). We need to count how many such pairs exist.
The task is, given the two arrays nums1
and nums2
, to return the total count of pairs (i, j)
that meet these conditions.
Intuition
To solve this problem, we leverage the fact that if we fix one index and sort all the potential pair values, we can then use binary search to efficiently find how many values meet our condition for each fixed index. Here's a step-by-step explanation of the solution:
-
First, compute the difference between the elements at the same indices in
nums1
andnums2
and store these differences in a new arrayd
. This transformation simplifies the problem, as we're now looking for indices whered[i] + d[j] > 0
. -
Sort the array
d
in non-decreasing order. Sorting enables us to use binary search to efficiently find indices satisfying our condition. -
For each element
d[i]
ind
, we want to find the count of elementsd[j]
such thatj > i
andd[i] + d[j] > 0
. We can rewrite this condition tod[j] > -d[i]
. Using binary search on the sorted arrayd
, we look for the right-most position to which-d[i]
could be inserted while maintaining the sorted order. This gives us the index beyond which all elements ofd[j]
would result ind[i] + d[j] > 0
. -
The
bisect_right
function from Python'sbisect
module is used for this purpose. For eachi
, it returns the index beyond which-d[i]
would go in the sorted array. -
The count of valid
j
for eachi
is the number of elements ind
beyond the index found in step 4, which is simplyn - (index found by bisect_right)
. -
The total count of valid pairs is obtained by summing the count from step 5 for each
i
.
Using this method, we reduce a potentially O(n^2) problem (checking each pair directly) to O(n log n) due to sorting and binary search for each element.
Learn more about Binary Search and Sorting patterns.
Solution Approach
The implemented solution follows these steps, using a mix of algorithmic techniques and Python-specific functionalities:
-
Difference Calculation and Store in Array
d
: The first step is to calculate the difference arrayd
, where each elementd[i]
is the difference betweennums1[i]
andnums2[i]
fori
from0
ton-1
. This subtraction is done using a list comprehension, which is a concise way to create lists in Python.d = [nums1[i] - nums2[i] for i in range(n)]
-
Sorting the Array
d
: The arrayd
is then sorted in non-decreasing order. This sorting is crucial as it prepares the array for a binary search operation. The sorted property ofd
allows us to apply the bisect algorithm effectively.d.sort()
-
Using Binary Search to Find Count of Valid Pairs: For each element in
d
, we use thebisect_right
function from thebisect
module to find the insertion point for-d[i]
intod
such that the array remains sorted.The function
bisect_right
is a binary search algorithm that returns the index in the sorted listd
, where the value-d[i]
should be inserted to maintain the sorted order. Thelo
parameter signifies the start position for the search which in this case isi + 1
, ensuring thatj > i
.The subtraction from
n
gives us the number of elements larger than-d[i]
, effectively counting how manyj
indices will satisfy the conditiond[i] + d[j] > 0
.sum(n - bisect_right(d, -v, lo=i + 1) for i, v in enumerate(d))
-
Summing the Counts for Each
i
: The sum operation in the final line adds up the valid pairs count for each value ofi
. It iterates over the sorted arrayd
and for each element calculates the number of valid pairs(i, j)
wherei < j
and the sum of the original elements fromnums1
at these indices is greater than the sum of the elements fromnums2
at the same indices, which corresponds to the conditiond[i] + d[j] > 0
post-transformation.
This solution uses a combination of algorithmic concepts:
- Transformation: to simplify the original condition to a more manageable form.
- Sorting: to prepare data for efficient searching.
- Binary Search: to reduce the search space for the pairs from O(n) to O(log n), greatly enhancing the overall algorithm efficiency.
- Prefix Sum: implicit in the adding up of counts for each index, effectively reducing the number of direct comparisons needed.
Returning the sum at the end gives us the desired count of pairs that fulfill the problem’s conditions efficiently.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's use a small example with the arrays nums1 = [3, -1, 7]
and nums2 = [4, 0, 5]
to illustrate the solution approach step-by-step. The length of both arrays, n
, is 3. Our goal is to find the count of valid pairs (i, j)
for which i < j
and nums1[i] + nums1[j] > nums2[i] + nums2[j]
.
Step 1: Calculate difference array d
First, find the difference between corresponding elements of nums1
and nums2
:
d[0] = nums1[0] - nums2[0] = 3 - 4 = -1
d[1] = nums1[1] - nums2[1] = -1 - 0 = -1
d[2] = nums1[2] - nums2[2] = 7 - 5 = 2
So the difference array d
is [-1, -1, 2]
.
Step 2: Sort the array d
We sort the array d
to get [-1, -1, 2]
. In this small case, sorting does not change the order, as the list is already in non-decreasing order.
Step 3: Use binary search for each i
We use bisect_right
to find where -d[i]
can be inserted:
- For
i = 0 (d[0] = -1)
: We search for where1
can be inserted after index 0.bisect_right([-1, -1, 2], 1, lo=0 + 1) = 3
. - For
i = 1 (d[1] = -1)
: We search for where1
can be inserted after index 1.bisect_right([-1, -1, 2], 1, lo=1 + 1) = 3
. - We do not search for
i = 2
because it's the last element, and noj
can satisfyi < j
.
Step 4: Calculate the valid j
indices and sum them up
- For
i = 0
: The count of validj
indices isn - 3 = 3 - 3 = 0
. - For
i = 1
: The count of validj
indices isn - 3 = 3 - 3 = 0
.
The total count of valid pairs (i, j)
is the sum of the counts above: 0 + 0 = 0
.
Therefore, for the given arrays nums1
and nums2
, there are no valid pairs (i, j)
that meet the condition nums1[i] + nums1[j] > nums2[i] + nums2[j]
.
Solution Implementation
1from typing import List
2from bisect import bisect_right
3
4class Solution:
5 def countPairs(self, nums1: List[int], nums2: List[int]) -> int:
6 # Length of the input lists
7 length = len(nums1)
8 # Calculate the difference between the two lists element-wise
9 differences = [nums1[i] - nums2[i] for i in range(length)]
10 # Sort the differences to prepare for binary search
11 differences.sort()
12
13 # Initialize count of pairs to 0
14 count = 0
15 # Loop through the sorted differences list
16 for i, value in enumerate(differences):
17 # For each element, find the number of elements in the sorted
18 # list that would create a negative sum with the current element.
19 # The `bisect_right` function is used to find the position to
20 # insert `-value` which gives the number of such elements.
21 # Subtract this position from the total number of elements that
22 # can be paired with the current element, which is (length - i - 1).
23 # We use `lo=i+1` because we shouldn't pair an element with itself.
24 count += length - bisect_right(differences, -value, lo=i + 1)
25
26 # Return the total count of valid pairs
27 return count
28
1class Solution {
2 public long countPairs(int[] nums1, int[] nums2) {
3 // Get the length of the arrays
4 int n = nums1.length;
5
6 // Create a new array to store the differences between nums1 and nums2
7 int[] differences = new int[n];
8 for (int i = 0; i < n; ++i) {
9 differences[i] = nums1[i] - nums2[i];
10 }
11
12 // Sort the array of differences
13 Arrays.sort(differences);
14
15 // Initialize answer to count the valid pairs
16 long answer = 0;
17
18 // Iterate over each element in the differences array
19 for (int i = 0; i < n; ++i) {
20 // Use binary search to find the number of valid pairs
21 int left = i + 1, right = n;
22 while (left < right) {
23 int mid = (left + right) / 2;
24 // Check if this position contributes to a valid pair
25 if (differences[mid] > -differences[i]) {
26 right = mid;
27 } else {
28 left = mid + 1;
29 }
30 }
31 // Add the count of valid pairs for this position to the answer
32 answer += n - left;
33 }
34
35 // Return the total number of valid pairs
36 return answer;
37 }
38}
39
1class Solution {
2public:
3 long long countPairs(vector<int>& nums1, vector<int>& nums2) {
4 // Get the size of the input vectors
5 int size = nums1.size();
6
7 // Create a difference vector to store differences of nums1[i] - nums2[i]
8 vector<int> diff(size);
9
10 // Populate the difference vector
11 for (int i = 0; i < size; ++i) {
12 diff[i] = nums1[i] - nums2[i];
13 }
14
15 // Sort the difference vector in non-decreasing order
16 sort(diff.begin(), diff.end());
17
18 // Initialize result variable to store the final count of pairs
19 long long result = 0;
20
21 // Iterate through each element in the difference vector
22 for (int i = 0; i < size; ++i) {
23 // Find the index of the first element that is greater than -diff[i]
24 // This is done to ensure that for any pair (i, j), diff[i] + diff[j] > 0
25 int j = upper_bound(diff.begin() + i + 1, diff.end(), -diff[i]) - diff.begin();
26
27 // Increment the result by the number of valid pairs with the current element at index i
28 result += size - j;
29 }
30
31 // Return the computed number of valid pairs
32 return result;
33 }
34};
35
1function countPairs(nums1: number[], nums2: number[]): bigint {
2 // Get the size of the input arrays
3 const size: number = nums1.length;
4
5 // Create a difference array to store differences of nums1[i] - nums2[i]
6 let diff: number[] = new Array<number>(size);
7
8 // Populate the difference array
9 for (let i = 0; i < size; i++) {
10 diff[i] = nums1[i] - nums2[i];
11 }
12
13 // Sort the difference array in non-decreasing order (ascending)
14 diff.sort((a, b) => a - b);
15
16 // Initialize result variable to store the final count of pairs
17 let result: bigint = BigInt(0);
18
19 // Iterate through each element in the difference array
20 for (let i = 0; i < size; i++) {
21 // Find the index of the first element that is strictly greater than -diff[i]
22 // This is done to ensure that for any pair (i, j), diff[i] + diff[j] > 0
23 let j: number = findUpperBound(diff, i + 1, size, -diff[i]);
24
25 // Increment the result by the number of valid pairs with the current element at index i
26 result += BigInt(size - j);
27 }
28
29 // Return the computed number of valid pairs
30 return result;
31}
32
33function findUpperBound(arr: number[], start: number, end: number, value: number): number {
34 // Binary search for the first element in the sorted array that is strictly greater than the given value
35 let low: number = start;
36 let high: number = end;
37
38 while (low < high) {
39 const mid: number = low + Math.floor((high - low) / 2);
40 if (arr[mid] <= value) {
41 low = mid + 1;
42 } else {
43 high = mid;
44 }
45 }
46
47 // Return the index where the value would be inserted (first index greater than the value)
48 return low;
49}
50
Time and Space Complexity
Time Complexity
The time complexity of the code can be broken down into several steps:
- Create a difference list
d
by subtractingnums2
fromnums1
. This step isO(n)
wheren
is the length of the input lists. - Sort the difference list
d
. Sorting algorithms generally have a time complexity ofO(n log n)
. - For each element in
d
, perform a binary search usingbisect_right
. Since we perform a binary search (O(log n)
) for each element in the list, this step has a time complexity ofO(n log n)
.
Adding these up, the overall time complexity is dominated by the sorting and binary search steps, which leads to O(n log n)
.
Space Complexity
The space complexity is evaluated as follows:
- We are creating a difference list
d
of sizen
, therefore requiringO(n)
additional space. - Sorting the list in-place (as Python’s sort does) has a space complexity of
O(log n)
, as typical implementations of sorting algorithms, like Timsort (used by Python's sort method), useO(log n)
space. - The binary search itself does not use additional space (aside from a few pointers), so the space used remains
O(log n)
.
As the additional space required for the difference list is the largest contributor, the overall space complexity is O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Want a Structured Path to Master System Design Too? Don’t Miss This!