Number of Pairs of Interchangeable Rectangles
MedPrompt
You are given n rectangles represented by a 0-indexed 2D integer array rectangles, where rectangles[i] = [widthi, heighti] denotes the width and height of the ith rectangle.
Two rectangles i and j (i < j) are considered interchangeable if they have the same width-to-height ratio. More formally, two rectangles are interchangeable if widthi/heighti == widthj/heightj (using decimal division, not integer division).
Return the number of pairs of interchangeable rectangles in rectangles.
Example 1:
Input: rectangles = [[4,8],[3,6],[10,20],[15,30]]
Output: 6
Explanation: The following are the interchangeable pairs of rectangles by index (0-indexed):
- Rectangle 0 with rectangle 1: 4/8 == 3/6.
- Rectangle 0 with rectangle 2: 4/8 == 10/20.
- Rectangle 0 with rectangle 3: 4/8 == 15/30.
- Rectangle 1 with rectangle 2: 3/6 == 10/20.
- Rectangle 1 with rectangle 3: 3/6 == 15/30.
- Rectangle 2 with rectangle 3: 10/20 == 15/30.Example 2:
Input: rectangles = [[4,5],[7,8]]
Output: 0
Explanation: There are no interchangeable pairs of rectangles.
Constraints:
n == rectangles.length1 <= n <= 105rectangles[i].length == 21 <= widthi, heighti <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem statement into code. It involves checking every possible pair of rectangles to see if they are interchangeable. While simple to conceive and implement, its performance is poor for large datasets.
Algorithm
- Initialize a counter
countto 0. - Use a nested loop to iterate through all pairs of indices
(i, j)such that0 <= i < j < n, wherenis the number of rectangles. - For each pair, get their dimensions:
w1, h1for rectangleiandw2, h2for rectanglej. - To check for interchangeability (
w1/h1 == w2/h2) without using floating-point numbers, use cross-multiplication:w1 * h2 == w2 * h1. - To avoid potential integer overflow, cast the dimensions to a 64-bit integer type (
longin Java) before multiplying. - If the cross-multiplication products are equal, increment the
count. - After the loops complete, return the final
count.
Walkthrough
The brute-force method iterates through all unique pairs of rectangles. For each pair of rectangles, i and j, we compare their width-to-height ratios. A direct comparison using floating-point division (width_i / height_i == width_j / height_j) can be prone to precision errors. A more reliable method is to use integer arithmetic with cross-multiplication: width_i * height_j == width_j * height_i. Since the product of widths and heights can exceed the capacity of a standard 32-bit integer (up to 10^5 * 10^5 = 10^10), we must use a 64-bit integer type (long in Java) for the calculation to prevent overflow. We maintain a counter, which is incremented for every pair that satisfies this condition.
class Solution { public long interchangeableRectangles(int[][] rectangles) { long count = 0; int n = rectangles.length; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { long w1 = rectangles[i][0]; long h1 = rectangles[i][1]; long w2 = rectangles[j][0]; long h2 = rectangles[j][1]; if (w1 * h2 == w2 * h1) { count++; } } } return count; }}Complexity
Time
O(n^2), where n is the number of rectangles. The nested loops result in a quadratic number of comparisons.
Space
O(1), as it only uses a few variables to store loop indices and the final count, regardless of the input size.
Trade-offs
Pros
Simple to understand and implement.
Requires no extra space, making it very memory-efficient.
Cons
Extremely inefficient for large inputs due to its quadratic time complexity.
Will result in a 'Time Limit Exceeded' (TLE) error for the given constraints (n up to 10^5).
A more efficient approach involves grouping rectangles by their width-to-height ratio. By using a HashMap, we can count the occurrences of each ratio in a single pass. For each group of k rectangles with the same ratio, they form k * (k-1) / 2 pairs. This method avoids the expensive quadratic pair-wise comparison.
Algorithm
- Initialize a total pair count
countto 0. - Create a HashMap,
ratioCounts, to store the frequency of each ratio. The key will be thedoubleratio, and the value will be its frequency (long). - Iterate through each
rectanglein the input arrayrectangles. - For the current rectangle, calculate its width-to-height ratio as a
double:ratio = (double)width / height. - Look up this
ratioin theratioCountsmap to find how many rectangles with this same ratio have been seen before. Let this bepreviousCount. - The current rectangle can form a new pair with each of those
previousCountrectangles. So, addpreviousCountto the totalcount. - Increment the frequency of the current
ratioin the map.ratioCounts.put(ratio, previousCount + 1). - After iterating through all rectangles, return the total
count.
Walkthrough
Instead of comparing every rectangle with every other rectangle, we can optimize the process by grouping rectangles that have the same ratio. The problem then becomes counting the number of pairs we can form within each group.
We can iterate through the list of rectangles once. For each rectangle, we calculate its width-to-height ratio. We use a HashMap to keep track of how many times we've seen each specific ratio so far.
When we encounter a rectangle with a certain ratio, we check the HashMap. If we have already seen k rectangles with this same ratio, it means our current rectangle can form k new interchangeable pairs. We add k to our total count of pairs. Then, we update the HashMap to reflect that we have now seen k+1 rectangles with this ratio. This process is repeated for all rectangles, and the final accumulated count is the answer.
import java.util.HashMap;import java.util.Map; class Solution { public long interchangeableRectangles(int[][] rectangles) { Map<Double, Long> ratioCounts = new HashMap<>(); long count = 0; for (int[] rect : rectangles) { double ratio = (double) rect[0] / rect[1]; long currentCount = ratioCounts.getOrDefault(ratio, 0L); count += currentCount; ratioCounts.put(ratio, currentCount + 1); } return count; }}Complexity
Time
O(n), where n is the number of rectangles. We iterate through the array once, and each HashMap operation (get and put) takes, on average, O(1) time.
Space
O(U), where `U` is the number of unique ratios. In the worst-case scenario where all rectangles have different ratios, the space complexity is O(n).
Trade-offs
Pros
Highly efficient with a linear time complexity, making it suitable for large inputs.
Conceptually elegant, solving the problem by counting groups rather than individual pairs.
Cons
Requires extra space for the HashMap, which can be up to O(n) in the worst case.
Using
doubleas a HashMap key can be risky due to floating-point precision issues, though it often works for this type of problem. A more robust (but slightly more complex) implementation would simplify the fractionwidth/heightusing the Greatest Common Divisor (GCD) and use the resulting pair or a string representation as the key.
Solutions
Solution
class Solution {public long interchangeableRectangles(int[][] rectangles) { long ans = 0; int n = rectangles.length + 1; Map<Long, Integer> cnt = new HashMap<>(); for (var e : rectangles) { int w = e[0], h = e[1]; int g = gcd(w, h); w /= g; h /= g; long x = (long)w * n + h; ans += cnt.getOrDefault(x, 0); cnt.merge(x, 1, Integer : : sum); } return ans; }private int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }}/** * @param {number[][]} rectangles * @return {number} */ var interchangeableRectangles = function (rectangles) { const cnt = new Map(); let ans = 0; for (let [w, h] of rectangles) { const g = gcd(w, h); w = Math.floor(w / g); h = Math.floor(h / g); const x = w * (rectangles.length + 1) + h; ans += cnt.get(x) | 0; cnt.set(x, (cnt.get(x) | 0) + 1); } return ans; };function gcd(a, b) { if (b == 0) { return a; } return gcd(b, a % b);}class Solution {public: long long interchangeableRectangles(vector<vector<int>> &rectangles) { long long ans = 0; int n = rectangles.size(); unordered_map<long long, int> cnt; for (auto &e : rectangles) { int w = e[0], h = e[1]; int g = gcd(w, h); w /= g; h /= g; long long x = 1ll * w * (n + 1) + h; ans += cnt[x]; cnt[x]++; } return ans; }};class Solution: def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: ans = 0 cnt = Counter() for w, h in rectangles: g = gcd(w, h) w, h = w // g, h // g ans += cnt[(w, h)] cnt[(w, h)] += 1 return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.