1122. Relative Sort Array
Problem Description
You are given two integer arrays arr1
and arr2
where:
- All elements in
arr2
are distinct (no duplicates) - Every element in
arr2
also exists inarr1
Your task is to sort arr1
according to the following rules:
-
For elements that appear in both arrays: Sort them in
arr1
based on their order inarr2
. If elementa
appears before elementb
inarr2
, thena
should appear beforeb
in the sortedarr1
. -
For elements that only appear in
arr1
: Place them at the end of the sorted array in ascending order.
For example, if arr1 = [2,3,1,3,2,4,6,7,9,2,19]
and arr2 = [2,1,4,3,9,6]
, the output would be [2,2,2,1,4,3,3,9,6,7,19]
because:
- Elements
2,1,4,3,9,6
are sorted according to their order inarr2
- Elements
7,19
(which don't appear inarr2
) are placed at the end in ascending order
The solution uses a custom sorting approach by creating a position mapping for elements in arr2
. Each element in arr1
is assigned a sorting key: if it exists in arr2
, it gets its position index; otherwise, it gets a large value (1000 + x)
to ensure it appears at the end while maintaining ascending order among elements not in arr2
.
Intuition
The core challenge is to sort arr1
based on two different criteria: elements in arr2
follow a custom order, while elements not in arr2
follow standard ascending order.
Think of this as creating a priority system. Elements that appear in arr2
have higher priority and should come first, ordered according to their position in arr2
. Elements not in arr2
have lower priority and should come last, sorted numerically.
To achieve this dual sorting behavior, we can leverage Python's built-in sorted()
function with a custom key. The key insight is to map each element to a value that represents its sorting priority:
-
For elements in
arr2
: We assign them their position index (0, 1, 2, ...). This ensures they maintain the same relative order as inarr2
. -
For elements not in
arr2
: We need to assign them values that are guaranteed to be larger than any position index fromarr2
. By using1000 + x
, we ensure these elements will be placed after all elements fromarr2
(sincearr2
can have at most 1000 elements based on typical constraints).
The beauty of this approach is that 1000 + x
not only pushes these elements to the end but also preserves their natural ascending order. For example, if we have elements 7
and 19
not in arr2
, they'll get keys 1007
and 1019
respectively, maintaining 7 < 19
in the final sorted result.
This transforms our complex two-criteria sorting problem into a simple single-key sorting problem, where the key function lambda x: pos.get(x, 1000 + x)
elegantly handles both cases in one expression.
Learn more about Sorting patterns.
Solution Approach
The implementation uses a hash table and custom sorting to achieve the desired result. Let's break down the solution step by step:
Step 1: Create Position Mapping
pos = {x: i for i, x in enumerate(arr2)}
We build a dictionary pos
that maps each element in arr2
to its index position. For example, if arr2 = [2,1,4,3]
, then pos = {2:0, 1:1, 4:2, 3:3}
. This allows us to quickly look up the desired position for any element that appears in arr2
.
Step 2: Sort with Custom Key Function
return sorted(arr1, key=lambda x: pos.get(x, 1000 + x))
We sort arr1
using a custom key function. For each element x
in arr1
, the key function works as follows:
- If
x
exists inpos
(meaning it's inarr2
), usepos[x]
as the sorting key - If
x
doesn't exist inpos
, use1000 + x
as the sorting key
How the Sorting Works:
When Python's sorted()
function compares elements, it actually compares their key values:
- Elements from
arr2
get keys in range[0, len(arr2)-1]
- Elements not in
arr2
get keys starting from1000
For example, with arr1 = [2,3,1,3,2,4]
and arr2 = [2,1,4]
:
- Element
2
→ key =0
- Element
1
→ key =1
- Element
4
→ key =2
- Element
3
→ key =1003
(not inarr2
)
After sorting by these keys: [0,0,1,2,1003,1003]
, we get the final array: [2,2,1,4,3,3]
.
Time Complexity: O(n log n)
where n
is the length of arr1
, dominated by the sorting operation.
Space Complexity: O(m)
where m
is the length of arr2
, for storing the position mapping.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Given:
arr1 = [5, 3, 1, 2, 3, 5, 6]
arr2 = [3, 1, 5]
Step 1: Create Position Mapping
First, we build a dictionary that maps each element in arr2
to its position:
pos = {3: 0, 1: 1, 5: 2}
This tells us that 3
should come first (position 0), 1
should come second (position 1), and 5
should come third (position 2).
Step 2: Assign Sorting Keys
Now, for each element in arr1
, we determine its sorting key:
Element in arr1 | In arr2? | Sorting Key | Explanation |
---|---|---|---|
5 | Yes | 2 | pos[5] = 2 |
3 | Yes | 0 | pos[3] = 0 |
1 | Yes | 1 | pos[1] = 1 |
2 | No | 1002 | 1000 + 2 |
3 | Yes | 0 | pos[3] = 0 |
5 | Yes | 2 | pos[5] = 2 |
6 | No | 1006 | 1000 + 6 |
Step 3: Sort by Keys
We sort the elements based on their keys:
- Keys in order:
[0, 0, 1, 2, 2, 1002, 1006]
- Corresponding elements:
[3, 3, 1, 5, 5, 2, 6]
Notice how:
- Elements with the same key (like both
3
s with key0
) maintain their relative order - Elements from
arr2
(keys 0-2) come before elements not inarr2
(keys 1002+) - Elements not in
arr2
(2 and 6) are sorted in ascending order at the end
Final Result: [3, 3, 1, 5, 5, 2, 6]
This matches our expected output where elements follow the order defined by arr2 = [3, 1, 5]
, and remaining elements [2, 6]
are appended in ascending order.
Solution Implementation
1class Solution:
2 def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
3 # Create a dictionary mapping each element in arr2 to its index position
4 # This will be used to determine the sorting priority
5 position_map = {element: index for index, element in enumerate(arr2)}
6
7 # Sort arr1 using a custom key function:
8 # - Elements present in arr2 get their position index as the key
9 # - Elements not in arr2 get (1000 + element_value) as the key
10 # This ensures they appear after all arr2 elements and in ascending order
11 return sorted(arr1, key=lambda element: position_map.get(element, 1000 + element))
12
1class Solution {
2 public int[] relativeSortArray(int[] arr1, int[] arr2) {
3 // Create a map to store the position of each element in arr2
4 Map<Integer, Integer> positionMap = new HashMap<>(arr2.length);
5 for (int i = 0; i < arr2.length; i++) {
6 positionMap.put(arr2[i], i);
7 }
8
9 // Create a 2D array to store each element with its sorting key
10 // Each row contains: [original value, sorting key]
11 int[][] elementWithSortKey = new int[arr1.length][2];
12 for (int i = 0; i < arr1.length; i++) {
13 elementWithSortKey[i] = new int[] {
14 arr1[i], // Store the original value
15 // If element exists in arr2, use its position;
16 // otherwise, use arr2.length + value to ensure it comes after arr2 elements
17 positionMap.getOrDefault(arr1[i], arr2.length + arr1[i])
18 };
19 }
20
21 // Sort the array based on the sorting key (second element of each row)
22 Arrays.sort(elementWithSortKey, (a, b) -> a[1] - b[1]);
23
24 // Extract the sorted values back to arr1
25 for (int i = 0; i < elementWithSortKey.length; i++) {
26 arr1[i] = elementWithSortKey[i][0];
27 }
28
29 return arr1;
30 }
31}
32
1class Solution {
2public:
3 vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
4 // Create a map to store the position/index of each element in arr2
5 unordered_map<int, int> elementPosition;
6 for (int i = 0; i < arr2.size(); ++i) {
7 elementPosition[arr2[i]] = i;
8 }
9
10 // Create pairs of (sort_key, value) for custom sorting
11 // Elements in arr2 get their index as sort_key
12 // Elements not in arr2 get arr2.size() as sort_key (to appear at the end)
13 vector<pair<int, int>> sortPairs;
14 for (int i = 0; i < arr1.size(); ++i) {
15 int sortKey = elementPosition.count(arr1[i]) ? elementPosition[arr1[i]] : arr2.size();
16 sortPairs.emplace_back(sortKey, arr1[i]);
17 }
18
19 // Sort pairs based on sort_key (first element of pair)
20 // Elements with same sort_key will maintain relative order by value
21 sort(sortPairs.begin(), sortPairs.end());
22
23 // Extract the sorted values back into arr1
24 for (int i = 0; i < arr1.size(); ++i) {
25 arr1[i] = sortPairs[i].second;
26 }
27
28 return arr1;
29 }
30};
31
1/**
2 * Sorts arr1 according to the relative order defined in arr2.
3 * Elements not in arr2 are placed at the end in ascending order.
4 *
5 * @param arr1 - The array to be sorted
6 * @param arr2 - The array defining the relative order
7 * @returns The sorted array based on arr2's order
8 */
9function relativeSortArray(arr1: number[], arr2: number[]): number[] {
10 // Create a map to store the position/index of each element in arr2
11 const elementPositionMap: Map<number, number> = new Map<number, number>();
12
13 // Populate the map with elements from arr2 and their positions
14 for (let index = 0; index < arr2.length; index++) {
15 elementPositionMap.set(arr2[index], index);
16 }
17
18 // Create an array of tuples [sortKey, originalValue]
19 // sortKey determines the ordering priority
20 const sortablePairs: number[][] = [];
21
22 // Process each element in arr1
23 for (const element of arr1) {
24 // Get position from map, or use arr2.length for elements not in arr2
25 // This ensures elements not in arr2 come after those in arr2
26 const sortKey: number = elementPositionMap.get(element) ?? arr2.length;
27 sortablePairs.push([sortKey, element]);
28 }
29
30 // Sort by sortKey first, then by element value for elements with same sortKey
31 // Elements not in arr2 will have the same sortKey and be sorted by value
32 sortablePairs.sort((pairA: number[], pairB: number[]) => {
33 return pairA[0] - pairB[0] || pairA[1] - pairB[1];
34 });
35
36 // Extract and return only the original values from sorted pairs
37 return sortablePairs.map((pair: number[]) => pair[1]);
38}
39
Time and Space Complexity
Time Complexity: O(n × log n + m)
The time complexity breaks down as follows:
- Creating the position dictionary
pos
fromarr2
takesO(m)
time, wherem
is the length ofarr2
- Sorting
arr1
using Python's built-insorted()
function takesO(n × log n)
time, wheren
is the length ofarr1
- During sorting, the key function
lambda x: pos.get(x, 1000 + x)
is calledO(n)
times, and each dictionary lookup takesO(1)
average time - Therefore, the total time complexity is
O(m) + O(n × log n) = O(n × log n + m)
Space Complexity: O(n + m)
The space complexity consists of:
- The dictionary
pos
stores all elements fromarr2
, requiringO(m)
space - The
sorted()
function creates a new sorted list containing all elements fromarr1
, requiringO(n)
space - The sorting algorithm itself (Timsort in Python) uses
O(n)
auxiliary space in the worst case - Therefore, the total space complexity is
O(m) + O(n) = O(n + m)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Overflow Risk with the Offset Value
The solution uses 1000 + x
as the sorting key for elements not in arr2
. This approach has a critical assumption that could lead to incorrect results.
The Problem:
If elements in arr2
have very large indices (e.g., if arr2
has more than 1000 elements), or if the values in arr1
that don't appear in arr2
are negative, the sorting keys could overlap or be ordered incorrectly.
Example of Failure:
- If
arr2
has 1500 elements, the last element would have key1499
- An element with value
400
not inarr2
would have key1400
- This would incorrectly place the non-arr2 element before some arr2 elements
Solution: Use a more robust offset that guarantees separation:
def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
position_map = {element: index for index, element in enumerate(arr2)}
# Use length of arr2 as offset base, then add a large constant
offset = len(arr2) + 10001 # Handles constraint: -1000 <= arr1[i] <= 1000
return sorted(arr1, key=lambda x: position_map.get(x, offset + x))
2. Not Preserving Original Order for Duplicate Elements
While Python's sorted()
is stable (preserves relative order of equal elements), relying on this behavior without understanding it can lead to confusion when implementing in other languages.
Better Approach for Clarity: Explicitly handle the counting and reconstruction:
def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
from collections import Counter
count = Counter(arr1)
result = []
# Add elements from arr2 in order
for num in arr2:
result.extend([num] * count[num])
del count[num]
# Add remaining elements in ascending order
for num in sorted(count.keys()):
result.extend([num] * count[num])
return result
3. Assuming Input Constraints Without Validation
The solution assumes all elements in arr2
exist in arr1
as stated in the problem. In real-world scenarios, this assumption might not hold.
Defensive Programming: Add validation if needed:
def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
position_map = {element: index for index, element in enumerate(arr2)}
# Validate assumption (optional based on requirements)
arr1_set = set(arr1)
for element in arr2:
if element not in arr1_set:
raise ValueError(f"Element {element} from arr2 not found in arr1")
offset = len(arr2) + 10001
return sorted(arr1, key=lambda x: position_map.get(x, offset + x))
Which two pointer techniques do you use to check if a string is a palindrome?
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!