Given an array of integers arr
, replace each element with its rank.
The rank represents how large the element is. The rank has the following rules:
Example 1:
Input: arr = [40,10,20,30] Output: [4,1,2,3] Explanation: 40 is the largest element. 10 is the smallest. 20 is the second smallest. 30 is the third smallest.
Example 2:
Input: arr = [100,100,100] Output: [1,1,1] Explanation: Same elements share the same rank.
Example 3:
Input: arr = [37,12,28,9,100,56,80,5,12] Output: [5,3,4,2,8,6,7,1,3]
Constraints:
0 <= arr.length <= 105
-109 <= arr[i] <= 109
The problem asks to transform an array of integers by replacing each element with its rank. The rank is determined by the element's magnitude relative to other elements in the array, with equal elements sharing the same rank. The solution uses two main approaches: Discretization and Sorting + Hash Map.
This approach efficiently handles the ranking process by:
Creating a Sorted and Deduplicated Set: It first creates a sorted set (t
) containing unique elements from the input array (arr
). Sorting ensures that elements are arranged in ascending order, while deduplication removes duplicate values, leading to a strictly monotonically increasing sequence. This step is crucial for efficient rank assignment.
Binary Search for Rank: For each element in the original array, it uses binary search (bisect_right
in Python, Arrays.binarySearch
in Java, upper_bound
in C++) to find its position within the sorted unique element set. The position (plus one, to start ranks from 1) represents the element's rank. Binary search provides an efficient way to find the rank of each element without having to iterate through the entire sorted set for each element.
Time Complexity: O(n log n), dominated by the sorting and the n binary search operations. Binary search has a time complexity of O(log n), where n is the length of the array.
Space Complexity: O(n), to store the sorted unique elements set and the result array.
This approach leverages a hash map (or dictionary) to store the rank of each unique element.
Creating Sorted Unique Element Set: A set is created to remove duplicate elements from the input array, and then this set is sorted to have the unique elements in increasing order.
Mapping Elements to Ranks: A hash map (map
) is used to store the rank of each unique element. The unique elements are iterated through, and each element is assigned a rank, incrementing the rank count (c
) for each unique element.
Assigning Ranks to Original Array: Finally, the original array arr
is iterated through and each element's rank is retrieved from the hash map.
Time Complexity: O(n log n), dominated by the sorting operation. The hash map operations (insertion and lookup) take, on average, O(1) time.
Space Complexity: O(n), to store the sorted unique elements set and the hash map.
The code examples below showcase both approaches in Python and JavaScript.
Python (Discretization):
class Solution:
def arrayRankTransform(self, arr: List[int]) -> List[int]:
t = sorted(list(set(arr))) #Sorted set
ranks = [bisect_right(t, x) for x in arr] #Binary search for each element in the sorted set
return ranks
JavaScript (Sorting + Hash Map):
function arrayRankTransform(arr) {
const uniqueSorted = [...new Set(arr)].sort((a, b) => a - b);
const rankMap = new Map();
let rank = 1;
for (const num of uniqueSorted) {
rankMap.set(num, rank++);
}
return arr.map(num => rankMap.get(num));
}
Both approaches achieve the same result. The choice between them depends on personal preference and the specific requirements of the coding environment. The discretization approach might be slightly more efficient in some scenarios due to the optimized binary search operation, while the hash map approach might be easier to understand for some programmers.