2191. Sort the Jumbled Numbers
Problem Description
You have a special mapping system that changes how digits are interpreted. The mapping
array tells you how to transform each digit: if mapping[i] = j
, then digit i
should be read as digit j
.
Given this mapping system and an array of integers nums
, you need to:
-
Transform each number in
nums
using the mapping rules. For example, ifmapping = [2,1,3,4,5,6,7,8,9,0]
and you have the number123
, you would:- Replace digit
1
withmapping[1] = 1
- Replace digit
2
withmapping[2] = 3
- Replace digit
3
withmapping[3] = 4
- Getting mapped value
134
- Replace digit
-
Sort the array based on these mapped values in ascending order. The original numbers in
nums
stay the same - you're just sorting them based on what they map to. -
Maintain relative order for numbers that have the same mapped value (stable sort).
Example: If mapping = [8,9,4,0,2,1,3,5,7,6]
and nums = [991, 338, 38]
:
991
maps to669
(9β6, 9β6, 1β9)338
maps to007
=7
(3β0, 3β0, 8β7)38
maps to07
=7
(3β0, 8β7)
Since 338
and 38
both map to 7
, and 338
appears before 38
in the original array, the sorted result would be [338, 38, 991]
.
The solution creates tuples of (mapped_value, original_index)
for each number, sorts these tuples, then reconstructs the final array using the sorted indices.
Intuition
The key insight is that we need to sort numbers based on their "translated" values while keeping the original numbers intact. This is like sorting people by their nicknames while still remembering their real names.
Since we can't directly modify the numbers in nums
, we need a way to:
- Calculate what each number "looks like" after mapping
- Sort based on these mapped values
- Return the original numbers in the new order
The challenge is converting each digit in a number according to the mapping. For a number like 338
, we need to extract each digit (3, 3, 8), map them individually (0, 0, 7), and reconstruct the mapped number (007 = 7).
To extract digits, we can repeatedly use divmod(x, 10)
which gives us the last digit and the remaining number. As we process each digit from right to left, we multiply by increasing powers of 10 to rebuild the mapped number in the correct position.
For maintaining stable sort (elements with same mapped values keep their relative order), we pair each mapped value with its original index: (mapped_value, index)
. When Python sorts tuples, it first sorts by the first element, then by the second element for ties. Since indices naturally increase, this preserves the original order for equal mapped values.
The special case of x == 0
needs separate handling because the while loop wouldn't execute for zero (since while 0
is false), but we still need to map the single digit 0
to mapping[0]
.
Learn more about Sorting patterns.
Solution Approach
The solution uses custom sorting with a helper function to calculate mapped values.
Step 1: Create the mapping function f(x)
The function takes an integer and returns its mapped value:
- Handle the special case where
x == 0
: directly returnmapping[0]
- For other numbers, extract digits one by one using
divmod(x, 10)
:x, v = divmod(x, 10)
gives us the last digitv
and remaining numberx
- Map the digit:
v = mapping[v]
- Reconstruct the mapped number by building it from right to left:
y = k * v + y
k
tracks the current position value (1, 10, 100, ...) and multiplies by 10 each iteration
Step 2: Create sortable tuples
For each element in nums
, create a tuple (f(x), i)
where:
f(x)
is the mapped value ofnums[i]
i
is the original index
This is done using a generator expression: (f(x), i) for i, x in enumerate(nums)
Step 3: Sort the tuples
sorted()
sorts the tuples first by mapped value, then by index. Since indices are already in ascending order, this naturally maintains stability - elements with the same mapped value keep their relative order.
Step 4: Extract the result
From the sorted array of tuples arr
, extract the indices and use them to build the final result: [nums[i] for _, i in arr]
The time complexity is O(n log n)
for sorting, where n
is the length of nums
. The space complexity is O(n)
for storing the array of tuples.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with mapping = [3,1,2,5,4,0,6,7,8,9]
and nums = [24, 15, 5]
.
Step 1: Calculate mapped values using function f(x)
For 24
:
- Extract digits:
24 β 2, 4
- Starting with rightmost digit 4:
mapping[4] = 4
- Next digit 2:
mapping[2] = 2
- Reconstruct:
2 * 10 + 4 = 24
(maps to itself!)
For 15
:
- Extract digits:
15 β 1, 5
- Starting with rightmost digit 5:
mapping[5] = 0
- Next digit 1:
mapping[1] = 1
- Reconstruct:
1 * 10 + 0 = 10
For 5
:
- Single digit:
mapping[5] = 0
- Result:
0
Step 2: Create (mapped_value, index) tuples
24
at index 0 β(24, 0)
15
at index 1 β(10, 1)
5
at index 2 β(0, 2)
Tuples array: [(24, 0), (10, 1), (0, 2)]
Step 3: Sort the tuples by mapped value
Sorting by first element (mapped value):
(0, 2)
- smallest mapped value(10, 1)
- middle mapped value(24, 0)
- largest mapped value
Sorted array: [(0, 2), (10, 1), (24, 0)]
Step 4: Extract original numbers using sorted indices
From sorted tuples, take indices: 2, 1, 0
- Index 2 β
nums[2] = 5
- Index 1 β
nums[1] = 15
- Index 0 β
nums[0] = 24
Final result: [5, 15, 24]
The original array [24, 15, 5]
has been sorted to [5, 15, 24]
based on their mapped values (24β24, 15β10, 5β0).
Solution Implementation
1class Solution:
2 def sortJumbled(self, mapping: List[int], nums: List[int]) -> List[int]:
3 def get_mapped_value(number: int) -> int:
4 """
5 Convert a number to its mapped value based on the mapping array.
6 Each digit in the number is replaced by its corresponding mapped digit.
7 """
8 # Special case: if the number is 0, directly return its mapped value
9 if number == 0:
10 return mapping[0]
11
12 # Initialize result and position multiplier
13 mapped_result = 0
14 position_multiplier = 1
15
16 # Process each digit from right to left
17 while number > 0:
18 # Extract the rightmost digit using divmod
19 number, digit = divmod(number, 10)
20
21 # Get the mapped value for this digit
22 mapped_digit = mapping[digit]
23
24 # Add the mapped digit to result at the correct position
25 mapped_result = position_multiplier * mapped_digit + mapped_result
26
27 # Move to the next position (tens, hundreds, etc.)
28 position_multiplier *= 10
29
30 return mapped_result
31
32 # Create tuples of (mapped_value, original_index) for stable sorting
33 # The index ensures that elements with same mapped value maintain original order
34 mapped_with_indices = [(get_mapped_value(num), index)
35 for index, num in enumerate(nums)]
36
37 # Sort by mapped values while maintaining stability
38 mapped_with_indices.sort()
39
40 # Extract the original numbers in sorted order using the indices
41 sorted_result = [nums[index] for _, index in mapped_with_indices]
42
43 return sorted_result
44
1class Solution {
2 // Global mapping array to store digit transformations
3 private int[] digitMapping;
4
5 /**
6 * Sorts the array based on mapped values while preserving relative order for equal mapped values
7 * @param mapping - array where mapping[i] represents what digit i maps to
8 * @param nums - array of integers to be sorted
9 * @return sorted array based on mapped values
10 */
11 public int[] sortJumbled(int[] mapping, int[] nums) {
12 this.digitMapping = mapping;
13 int length = nums.length;
14
15 // Create a 2D array to store [mappedValue, originalIndex] pairs
16 int[][] mappedPairs = new int[length][2];
17
18 // Calculate mapped value for each number and store with its original index
19 for (int i = 0; i < length; i++) {
20 mappedPairs[i] = new int[] {getMappedValue(nums[i]), i};
21 }
22
23 // Sort by mapped value first, then by original index to maintain stability
24 Arrays.sort(mappedPairs, (a, b) -> {
25 if (a[0] == b[0]) {
26 return a[1] - b[1]; // If mapped values are equal, maintain original order
27 }
28 return a[0] - b[0]; // Otherwise sort by mapped value
29 });
30
31 // Build the result array using the sorted indices
32 int[] result = new int[length];
33 for (int i = 0; i < length; i++) {
34 result[i] = nums[mappedPairs[i][1]];
35 }
36
37 return result;
38 }
39
40 /**
41 * Converts a number to its mapped value based on digit mapping
42 * @param number - the original number to map
43 * @return the mapped value after transforming each digit
44 */
45 private int getMappedValue(int number) {
46 // Special case: if the number is 0, return its mapped value directly
47 if (number == 0) {
48 return digitMapping[0];
49 }
50
51 int mappedValue = 0;
52 int placeValue = 1; // Represents 1, 10, 100, 1000, etc.
53
54 // Process each digit from right to left
55 while (number > 0) {
56 int digit = number % 10; // Extract the rightmost digit
57 int mappedDigit = digitMapping[digit]; // Get the mapped value for this digit
58 mappedValue = mappedValue + (placeValue * mappedDigit); // Add to result
59 placeValue *= 10; // Move to next place value
60 number /= 10; // Remove the processed digit
61 }
62
63 return mappedValue;
64 }
65}
66
1class Solution {
2public:
3 vector<int> sortJumbled(vector<int>& mapping, vector<int>& nums) {
4 // Lambda function to convert a number based on the mapping
5 auto getMappedValue = [&](int num) {
6 // Special case: if the number is 0, directly return its mapped value
7 if (num == 0) {
8 return mapping[0];
9 }
10
11 // Build the mapped number digit by digit
12 int mappedValue = 0;
13 int placeValue = 1; // Represents 1, 10, 100, 1000, etc.
14
15 // Process each digit from right to left
16 while (num > 0) {
17 int digit = num % 10; // Extract the rightmost digit
18 int mappedDigit = mapping[digit]; // Get the mapped value for this digit
19 mappedValue += placeValue * mappedDigit; // Add to result at correct position
20 placeValue *= 10; // Move to next place value
21 num /= 10; // Remove the processed digit
22 }
23
24 return mappedValue;
25 };
26
27 // Store pairs of (mapped value, original index) for stable sorting
28 const int n = nums.size();
29 vector<pair<int, int>> mappedPairs;
30
31 // Create pairs of mapped values and their original indices
32 for (int i = 0; i < n; ++i) {
33 mappedPairs.emplace_back(getMappedValue(nums[i]), i);
34 }
35
36 // Sort by mapped values (stable sort preserves relative order for equal values)
37 sort(mappedPairs.begin(), mappedPairs.end());
38
39 // Build the result array using the sorted order
40 vector<int> result;
41 for (const auto& [mappedVal, originalIndex] : mappedPairs) {
42 result.push_back(nums[originalIndex]);
43 }
44
45 return result;
46 }
47};
48
1/**
2 * Sorts an array of numbers based on their mapped values using a digit mapping
3 * @param mapping - Array where mapping[i] represents the mapped value for digit i
4 * @param nums - Array of numbers to be sorted
5 * @returns Sorted array based on mapped values
6 */
7function sortJumbled(mapping: number[], nums: number[]): number[] {
8 /**
9 * Converts a number to its mapped value by replacing each digit
10 * @param originalNumber - The number to be mapped
11 * @returns The mapped value of the number
12 */
13 const getMappedValue = (originalNumber: number): number => {
14 // Special case: if the number is 0, return its mapped value directly
15 if (originalNumber === 0) {
16 return mapping[0];
17 }
18
19 let mappedValue = 0;
20 let placeValue = 1; // Represents 1, 10, 100, 1000, etc.
21
22 // Process each digit from right to left
23 while (originalNumber > 0) {
24 // Extract the rightmost digit
25 const currentDigit = originalNumber % 10;
26
27 // Get the mapped value for this digit
28 const mappedDigit = mapping[currentDigit];
29
30 // Add the mapped digit at its correct position
31 mappedValue += mappedDigit * placeValue;
32
33 // Move to the next digit position
34 placeValue *= 10;
35
36 // Remove the rightmost digit from the number
37 originalNumber = Math.floor(originalNumber / 10);
38 }
39
40 return mappedValue;
41 };
42
43 // Create array of [mappedValue, originalIndex] pairs for sorting
44 const mappedPairs: number[][] = nums.map((num, index) => [getMappedValue(num), index]);
45
46 // Sort by mapped value first, then by original index to maintain stability
47 mappedPairs.sort((a, b) => {
48 if (a[0] === b[0]) {
49 return a[1] - b[1]; // If mapped values are equal, sort by original index
50 }
51 return a[0] - b[0]; // Otherwise, sort by mapped value
52 });
53
54 // Extract the original numbers in their new sorted order
55 return mappedPairs.map(pair => nums[pair[1]]);
56}
57
Time and Space Complexity
Time Complexity: O(n Γ m Γ log n)
where n
is the length of the array nums
and m
is the average number of digits in the numbers.
The time complexity breaks down as follows:
- The function
f(x)
processes each digit of a number, takingO(m)
time wherem
is the number of digits - We call
f(x)
for each of then
numbers innums
, resulting inO(n Γ m)
time - The sorting operation on
n
elements takesO(n Γ log n)
time - Total complexity:
O(n Γ m) + O(n Γ log n) = O(n Γ m Γ log n)
However, if we consider m
(the maximum number of digits) as a constant since integers have a bounded number of digits in practice, the time complexity simplifies to O(n Γ log n)
.
Space Complexity: O(n)
The space complexity analysis:
- The
arr
list stores tuples(f(x), i)
for each element, requiringO(n)
space - The final list comprehension creates a new list of size
n
, requiringO(n)
space - The function
f(x)
usesO(1)
auxiliary space for variablesy
,k
, andv
- Total space complexity:
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Handling Zero
A common mistake is forgetting that 0
is a special case. When processing digits with divmod
, the loop while number > 0
will never execute for number = 0
, resulting in returning 0
instead of mapping[0]
.
Incorrect approach:
def get_mapped_value(number: int) -> int:
mapped_result = 0
position_multiplier = 1
while number > 0: # This loop never runs when number = 0!
number, digit = divmod(number, 10)
mapped_digit = mapping[digit]
mapped_result = position_multiplier * mapped_digit + mapped_result
position_multiplier *= 10
return mapped_result # Returns 0 instead of mapping[0]
Solution: Always check for zero as a special case before the loop.
2. Digit Reconstruction Order Error
Another pitfall is building the mapped number in the wrong order. Since we extract digits from right to left, we must carefully reconstruct the mapped value to maintain the correct digit positions.
Incorrect approach:
# Wrong: Simply appending digits leads to reversed number mapped_result = mapped_result * 10 + mapped_digit # This reverses the digits!
Solution: Use mapped_result = position_multiplier * mapped_digit + mapped_result
to maintain correct digit positions.
3. Using Unstable Sorting
If you don't maintain the original indices when creating the sortable data structure, you might lose the relative order of elements with the same mapped value.
Incorrect approach:
# Wrong: Only sorting by mapped values loses stability mapped_values = [get_mapped_value(num) for num in nums] # How do we know which original number each mapped value came from?
Solution: Create tuples with both mapped values and original indices: (mapped_value, index)
. Python's sort is stable and will preserve the original order for equal mapped values.
4. String Conversion Inefficiency
Some might be tempted to convert numbers to strings for digit manipulation, which is less efficient and can introduce edge cases.
Less optimal approach:
def get_mapped_value(number: int) -> int:
num_str = str(number)
mapped_str = ''.join(str(mapping[int(digit)]) for digit in num_str)
return int(mapped_str) # Potential issues with leading zeros
This approach has problems:
- Less efficient due to string operations
- May incorrectly handle cases where mapped digits create leading zeros (e.g., if
mapping[3] = 0
, then38
might become"08"
which converts to8
instead of maintaining the structure)
Solution: Stick with mathematical operations using divmod
for better performance and correctness.
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!