Facebook Pixel

1122. Relative Sort Array

EasyArrayHash TableCounting SortSorting
Leetcode Link

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 in arr1

Your task is to sort arr1 according to the following rules:

  1. For elements that appear in both arrays: Sort them in arr1 based on their order in arr2. If element a appears before element b in arr2, then a should appear before b in the sorted arr1.

  2. 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 in arr2
  • Elements 7,19 (which don't appear in arr2) 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. For elements in arr2: We assign them their position index (0, 1, 2, ...). This ensures they maintain the same relative order as in arr2.

  2. For elements not in arr2: We need to assign them values that are guaranteed to be larger than any position index from arr2. By using 1000 + x, we ensure these elements will be placed after all elements from arr2 (since arr2 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 in pos (meaning it's in arr2), use pos[x] as the sorting key
  • If x doesn't exist in pos, use 1000 + 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 from 1000

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 in arr2)

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 Evaluator

Example 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 arr1In arr2?Sorting KeyExplanation
5Yes2pos[5] = 2
3Yes0pos[3] = 0
1Yes1pos[1] = 1
2No10021000 + 2
3Yes0pos[3] = 0
5Yes2pos[5] = 2
6No10061000 + 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 3s with key 0) maintain their relative order
  • Elements from arr2 (keys 0-2) come before elements not in arr2 (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 from arr2 takes O(m) time, where m is the length of arr2
  • Sorting arr1 using Python's built-in sorted() function takes O(n × log n) time, where n is the length of arr1
  • During sorting, the key function lambda x: pos.get(x, 1000 + x) is called O(n) times, and each dictionary lookup takes O(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 from arr2, requiring O(m) space
  • The sorted() function creates a new sorted list containing all elements from arr1, requiring O(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 key 1499
  • An element with value 400 not in arr2 would have key 1400
  • 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))
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which two pointer techniques do you use to check if a string is a palindrome?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More