{x}
blog image

Rank Transform of an Array

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:

  • Rank is an integer starting from 1.
  • The larger the element, the larger the rank. If two elements are equal, their rank must be the same.
  • Rank should be as small as possible.

 

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

Solution Explanation for Rank Transform of an Array

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.

Approach 1: Discretization

This approach efficiently handles the ranking process by:

  1. 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.

  2. 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.

Approach 2: Sorting + Hash Map

This approach leverages a hash map (or dictionary) to store the rank of each unique element.

  1. 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.

  2. 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.

  3. 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.

Code Examples (Python and JavaScript)

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.