Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions problems/1331-rank-transform-of-an-array/analysis.md
Original file line number Diff line number Diff line change
@@ -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.
60 changes: 60 additions & 0 deletions problems/1331-rank-transform-of-an-array/problem.md
Original file line number Diff line number Diff line change
@@ -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`
33 changes: 33 additions & 0 deletions problems/1331-rank-transform-of-an-array/solution.go
Original file line number Diff line number Diff line change
@@ -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
}
59 changes: 59 additions & 0 deletions problems/1331-rank-transform-of-an-array/solution_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}