diff --git a/problems/1331-rank-transform-of-an-array/analysis.md b/problems/1331-rank-transform-of-an-array/analysis.md new file mode 100644 index 0000000..d1693c6 --- /dev/null +++ b/problems/1331-rank-transform-of-an-array/analysis.md @@ -0,0 +1,52 @@ +# 1331. Rank Transform of an Array + +[LeetCode Link](https://leetcode.com/problems/rank-transform-of-an-array/) + +Difficulty: Easy +Topics: Array, Hash Table, Sorting +Acceptance Rate: 71.4% + +## Hints + +### Hint 1 + +The rank of an element only depends on how many *distinct* values are smaller than it. Think about what happens if you look at the values in sorted order — the smallest distinct value gets rank 1, the next distinct value gets rank 2, and so on. Which two classic tools let you both order the data and remember a mapping? + +### Hint 2 + +You need to preserve the original positions while assigning ranks based on sorted order. A common trick: work on a *copy* of the array, sort the copy, then build a lookup (hash map) from each value to its rank. Finally, walk the original array and replace each element via the lookup. + +### Hint 3 + +Equal elements must share a rank, and ranks must be "as small as possible" (no gaps). So when you scan the sorted copy, only assign a new rank when you encounter a value you haven't seen before. Storing `value -> rank` in a map means duplicates naturally map to the same rank, and the rank counter increments exactly once per distinct value. + +## Approach + +The rank of an element is `1 + (number of distinct values strictly smaller than it)`. This gives us a clean, deterministic way to compute every rank. + +Step by step: + +1. **Copy and sort.** Make a copy of `arr` and sort the copy in ascending order. We sort a copy so the original indices are preserved for the final pass. +2. **Build the rank map.** Iterate over the sorted copy. Maintain a running `rank` starting at 1. For each value, if it is not already a key in the map, insert `value -> rank` and then increment `rank`. Because the array is sorted, all occurrences of a value are contiguous, so the first time we see a value it receives the smallest possible rank, and subsequent duplicates are skipped (they'd map to the same value anyway). +3. **Rebuild the answer.** Create a result slice the same length as `arr`. For each original element, look up its rank in the map and store it at the same index. + +Example with `arr = [37,12,28,9,100,56,80,5,12]`: + +- Sorted copy: `[5,9,12,12,28,37,56,80,100]` +- Rank map (assigned on first sight): `5->1, 9->2, 12->3, 28->4, 37->5, 56->6, 80->7, 100->8` +- Map back over the original: `[5,3,4,2,8,6,7,1,3]` ✅ + +The map handles duplicates for free (both `12`s map to rank 3), and sorting guarantees the "as small as possible" requirement since ranks are assigned in increasing value order with no gaps. + +## Complexity Analysis + +Time Complexity: O(n log n) — dominated by sorting the copy. Building the map and rebuilding the result are each O(n). +Space Complexity: O(n) — the sorted copy, the value→rank map, and the result slice each use O(n) space. + +## Edge Cases + +- **Empty array (`arr.length == 0`).** The constraints allow a length-0 array. The code should return an empty (non-nil is nice but not required) slice without indexing errors. Sorting an empty copy and looping zero times handles this naturally. +- **Single element.** Always maps to rank 1; a good sanity check that the counter starts at 1. +- **All equal elements (e.g. `[100,100,100]`).** Every element must share the same rank `1`. The map ensures the value is inserted once, so all positions resolve to rank 1. +- **Negative values and full int range (`-10^9 .. 10^9`).** Ranks are based on relative ordering, not magnitude, so negatives work the same as positives — no special handling needed. Values comfortably fit in Go's `int`. +- **Already sorted / reverse sorted input.** Doesn't change correctness; it's just a specific ordering the general algorithm already covers. diff --git a/problems/1331-rank-transform-of-an-array/problem.md b/problems/1331-rank-transform-of-an-array/problem.md new file mode 100644 index 0000000..cda4b8c --- /dev/null +++ b/problems/1331-rank-transform-of-an-array/problem.md @@ -0,0 +1,60 @@ +--- +number: "1331" +frontend_id: "1331" +title: "Rank Transform of an Array" +slug: "rank-transform-of-an-array" +difficulty: "Easy" +topics: + - "Array" + - "Hash Table" + - "Sorting" +acceptance_rate: 7143.3 +is_premium: false +created_at: "2026-07-12T04:05:50.492583+00:00" +fetched_at: "2026-07-12T04:05:50.492583+00:00" +link: "https://leetcode.com/problems/rank-transform-of-an-array/" +date: "2026-07-12" +--- + +# 1331. 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` diff --git a/problems/1331-rank-transform-of-an-array/solution.go b/problems/1331-rank-transform-of-an-array/solution.go new file mode 100644 index 0000000..b244675 --- /dev/null +++ b/problems/1331-rank-transform-of-an-array/solution.go @@ -0,0 +1,33 @@ +package main + +import "sort" + +// arrayRankTransform replaces each element with its rank. +// +// Approach: the rank of a value equals 1 plus the number of distinct values +// strictly smaller than it. We sort a copy of the array, then walk it once to +// build a value -> rank map, assigning a new rank only when we meet a value we +// have not seen before (so equal values share a rank and there are no gaps). +// Finally we map the original array back through the lookup, preserving order. +// +// Time: O(n log n) for the sort. Space: O(n) for the copy, map, and result. +func arrayRankTransform(arr []int) []int { + sorted := make([]int, len(arr)) + copy(sorted, arr) + sort.Ints(sorted) + + rankOf := make(map[int]int, len(sorted)) + rank := 1 + for _, v := range sorted { + if _, seen := rankOf[v]; !seen { + rankOf[v] = rank + rank++ + } + } + + result := make([]int, len(arr)) + for i, v := range arr { + result[i] = rankOf[v] + } + return result +} diff --git a/problems/1331-rank-transform-of-an-array/solution_test.go b/problems/1331-rank-transform-of-an-array/solution_test.go new file mode 100644 index 0000000..180ef4c --- /dev/null +++ b/problems/1331-rank-transform-of-an-array/solution_test.go @@ -0,0 +1,59 @@ +package main + +import ( + "reflect" + "testing" +) + +func TestSolution(t *testing.T) { + tests := []struct { + name string + input []int + expected []int + }{ + { + name: "example 1: distinct increasing ranks", + input: []int{40, 10, 20, 30}, + expected: []int{4, 1, 2, 3}, + }, + { + name: "example 2: all equal share rank", + input: []int{100, 100, 100}, + expected: []int{1, 1, 1}, + }, + { + name: "example 3: duplicates with mixed order", + input: []int{37, 12, 28, 9, 100, 56, 80, 5, 12}, + expected: []int{5, 3, 4, 2, 8, 6, 7, 1, 3}, + }, + { + name: "edge case: empty input", + input: []int{}, + expected: []int{}, + }, + { + name: "edge case: single element", + input: []int{42}, + expected: []int{1}, + }, + { + name: "edge case: negative values", + input: []int{-5, -10, 0, -10, 5}, + expected: []int{2, 1, 3, 1, 4}, + }, + { + name: "edge case: reverse sorted", + input: []int{9, 7, 5, 3, 1}, + expected: []int{5, 4, 3, 2, 1}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := arrayRankTransform(tt.input) + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("arrayRankTransform(%v) = %v, want %v", tt.input, result, tt.expected) + } + }) + } +}