Relative Sort Array
EasyPrompt
Given two arrays arr1 and arr2, the elements of arr2 are distinct, and all elements in arr2 are also in arr1.
Sort the elements of arr1 such that the relative ordering of items in arr1 are the same as in arr2. Elements that do not appear in arr2 should be placed at the end of arr1 in ascending order.
Example 1:
Input: arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
Output: [2,2,2,1,4,3,3,9,6,7,19]Example 2:
Input: arr1 = [28,6,22,8,44,17], arr2 = [22,28,8,6]
Output: [22,28,8,6,17,44]
Constraints:
1 <= arr1.length, arr2.length <= 10000 <= arr1[i], arr2[i] <= 1000- All the elements of
arr2are distinct. - Each
arr2[i]is inarr1.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach directly simulates the process described in the problem. It first iterates through arr2 to place the common elements in the correct relative order. Then, it performs another pass to find all elements not in arr2, sorts them, and appends them to the end.
Algorithm
- Initialize an empty list
resultto store the sorted elements. - Iterate through each element
val2inarr2. - For each
val2, iterate througharr1. If an elementval1inarr1is equal toval2, addval1to theresultlist. - Create another list
remainingfor elements fromarr1not present inarr2. - Iterate through
arr1. For eachval1, perform a linear scan througharr2to check for its existence. - If
val1is not found inarr2, add it to theremaininglist. - Sort the
remaininglist in ascending order. - Append the sorted
remaininglist to theresultlist. - Convert the
resultlist to an array and return it.
Walkthrough
The brute-force method involves a straightforward, step-by-step implementation of the problem's requirements without optimizing for performance.
First, we construct the initial part of the sorted array. We iterate through each element in arr2, and for each of these elements, we scan the entire arr1 to find all matching occurrences, which are then added to a result list. This ensures the relative order from arr2 is maintained.
Next, we need to handle the elements that are in arr1 but not in arr2. We create a separate list for these remaining elements. We iterate through arr1 again. For each element, we check if it exists in arr2 by performing another full scan of arr2. If it doesn't exist, we add it to our list of remaining elements.
Finally, we sort this list of remaining elements in ascending order and append it to our main result list. The list is then converted into an array to be returned.
import java.util.ArrayList;import java.util.Collections;import java.util.List; class Solution { public int[] relativeSortArray(int[] arr1, int[] arr2) { List<Integer> result = new ArrayList<>(); // Part 1: Add elements from arr1 that are in arr2, in order of arr2 for (int val2 : arr2) { for (int val1 : arr1) { if (val1 == val2) { result.add(val1); } } } // Part 2: Add elements from arr1 that are not in arr2 List<Integer> remaining = new ArrayList<>(); for (int val1 : arr1) { boolean found = false; for (int val2 : arr2) { if (val1 == val2) { found = true; break; } } if (!found) { remaining.add(val1); } } // Sort the remaining elements and add to the result Collections.sort(remaining); result.addAll(remaining); // Convert List to array int[] finalResult = new int[result.size()]; for (int i = 0; i < result.size(); i++) { finalResult[i] = result.get(i); } return finalResult; }}Complexity
Time
O(N * M), where N is the length of `arr1` and M is the length of `arr2`. The nested loops for finding elements and checking for non-existence dominate the runtime.
Space
O(N), where N is the length of `arr1`. This space is used for the `result` and `remaining` lists.
Trade-offs
Pros
Simple to understand and implement.
Directly follows the problem statement.
Cons
Highly inefficient due to multiple nested loops.
Will likely result in a 'Time Limit Exceeded' error for larger inputs.
This approach leverages a custom comparator to sort arr1. We first create a map to store the desired order of elements present in arr2. Then, we use this map within a comparator to define the sorting logic for all elements in arr1, handling both elements present in arr2 and those that are not.
Algorithm
- Create a
HashMap<Integer, Integer>calledorderMapto store the relative order of elements inarr2. - Iterate through
arr2and populate the map:orderMap.put(arr2[i], i). - Convert the primitive
int[] arr1to anInteger[]array to allow sorting with a custom comparator. - Sort the
Integer[]array usingArrays.sortand a custom lambda comparator. - The comparator logic for two elements
aandbis:- If both
aandbare inorderMap, compare their mapped indices. - If only
ais inorderMap,acomes first. - If only
bis inorderMap,bcomes first. - If neither is in
orderMap, compare them numerically (a - b).
- If both
- Copy the sorted elements from the
Integer[]array back to the originalint[]array.
Walkthrough
A more optimized approach is to define a custom sorting rule and use a standard sorting algorithm. The key is to establish a clear order for any two elements from arr1.
-
Create an Order Map: We first need a way to quickly look up the relative order of elements defined by
arr2. A HashMap is perfect for this. We create a maporderMapwhere keys are the numbers fromarr2and values are their indices. This map can be built in O(M) time, where M is the length ofarr2. -
Custom Sort: We then sort
arr1. Since Java'sArrays.sortfor primitive arrays doesn't accept a custom comparator, we first convertarr1into anInteger[]array. Then, we sort this new array using a custom comparator that implements our logic:- For any two elements
aandb, we check if they exist in ourorderMap. - Case 1 (Both in
arr2): If bothaandbare in the map, their order is determined by their original indices inarr2. We compareorderMap.get(a)andorderMap.get(b). - Case 2 (One in
arr2): If only one element is in the map, it must come before the one that isn't. - Case 3 (Neither in
arr2): If neither is in the map, they are 'extra' elements and should be sorted by their natural numerical order.
- For any two elements
After sorting, we copy the elements back into the original int[] array.
import java.util.Arrays;import java.util.HashMap;import java.util.Map; class Solution { public int[] relativeSortArray(int[] arr1, int[] arr2) { Map<Integer, Integer> orderMap = new HashMap<>(); for (int i = 0; i < arr2.length; i++) { orderMap.put(arr2[i], i); } Integer[] arr1Boxed = new Integer[arr1.length]; for (int i = 0; i < arr1.length; i++) { arr1Boxed[i] = arr1[i]; } Arrays.sort(arr1Boxed, (a, b) -> { boolean aInArr2 = orderMap.containsKey(a); boolean bInArr2 = orderMap.containsKey(b); if (aInArr2 && bInArr2) { return orderMap.get(a) - orderMap.get(b); } else if (aInArr2) { return -1; // a comes first } else if (bInArr2) { return 1; // b comes first } else { return a - b; // natural order } }); for (int i = 0; i < arr1.length; i++) { arr1[i] = arr1Boxed[i]; } return arr1; }}Complexity
Time
O(M + N log N), where N is the length of `arr1` and M is the length of `arr2`. It takes O(M) to build the map and O(N log N) to sort `arr1`.
Space
O(M + N). O(M) for the HashMap and O(N) for the boxed `Integer` array needed for the custom sort. The sorting algorithm itself might use additional space (e.g., O(log N) or O(N)).
Trade-offs
Pros
A general-purpose solution that works even if element values are not constrained to a small range.
Much more efficient than the brute-force approach.
Cons
Requires converting the primitive
int[]to anInteger[], which adds memory and time overhead.Not as efficient as counting sort when element values are within a small, known range.
This is the most efficient approach, taking advantage of the constraint that element values are between 0 and 1000. It uses a frequency array (a form of counting sort) to count all elements in arr1 and then reconstructs the array in the desired order in a single pass.
Algorithm
- Create a frequency array
countsof size 1001 (for values 0-1000) and initialize it to all zeros. - Iterate through
arr1and populate thecountsarray: for eachnuminarr1, incrementcounts[num]. - Initialize a pointer
index = 0for the result array (which will bearr1modified in-place). - Iterate through
arr2. For eachvalinarr2:- While
counts[val] > 0, placevalatarr1[index++]and decrementcounts[val].
- While
- Iterate through the
countsarray fromnum = 0to 1000.- While
counts[num] > 0, placenumatarr1[index++]and decrementcounts[num].
- While
- Return the modified
arr1.
Walkthrough
Given the constraint that all numbers are within the range [0, 1000], we can use a counting-based approach for a linear time solution.
-
Frequency Count: We declare an auxiliary array,
counts, of size 1001. We iterate througharr1, and for each numbernum, we incrementcounts[num]. After this pass,counts[i]will hold the frequency of the numberiinarr1. -
Reconstruct Array: We can now rebuild
arr1in-place. We use an indexito keep track of the current position to fill inarr1.- Part 1 (Relative Order): We iterate through
arr2. For each numbervalinarr2, we know its count fromcounts[val]. We writevalintoarr1counts[val]times, advancing our indexieach time. To ensure these numbers are not processed again, we can setcounts[val]to 0 after we are done with it. - Part 2 (Remaining Elements): After processing all numbers from
arr2, we iterate through ourcountsarray from index 0 to 1000. Ifcounts[num]is still greater than 0, it meansnumwas not inarr2. We appendnumtoarr1counts[num]times. Since we iterate from 0 to 1000, these remaining elements are automatically added in ascending order.
- Part 1 (Relative Order): We iterate through
This method processes each array and the counts array a constant number of times, leading to a very efficient solution.
class Solution { public int[] relativeSortArray(int[] arr1, int[] arr2) { int[] counts = new int[1001]; // Count frequencies of each number in arr1 for (int num : arr1) { counts[num]++; } int index = 0; // Place elements of arr2 in order for (int val : arr2) { while (counts[val] > 0) { arr1[index++] = val; counts[val]--; } } // Place remaining elements in ascending order for (int num = 0; num < counts.length; num++) { while (counts[num] > 0) { arr1[index++] = num; counts[num]--; } } return arr1; }}Complexity
Time
O(N + M + C), where N is `arr1.length`, M is `arr2.length`, and C is the range of values (1001). This simplifies to linear time as C is a constant.
Space
O(C), where C is the range of values (1001 in this case). This space is for the `counts` array. This is constant space with respect to the input size N and M.
Trade-offs
Pros
Extremely fast with linear time complexity.
Simple to implement and avoids the overhead of complex data structures or comparators.
Sorts the array in-place (if allowed), saving space.
Cons
This approach is only efficient because the range of element values is small and known (0-1000).
It would be impractical for arrays with a very large or unbounded range of values due to high space requirements.
Solutions
Solution
class Solution {public int[] relativeSortArray(int[] arr1, int[] arr2) { Map<Integer, Integer> pos = new HashMap<>(arr2.length); for (int i = 0; i < arr2.length; ++i) { pos.put(arr2[i], i); } int[][] arr = new int[arr1.length][0]; for (int i = 0; i < arr.length; ++i) { arr[i] = new int[]{arr1[i], pos.getOrDefault(arr1[i], arr2.length + arr1[i])}; } Arrays.sort(arr, (a, b)->a[1] - b[1]); for (int i = 0; i < arr.length; ++i) { arr1[i] = arr[i][0]; } return arr1; }}class Solution {public: vector<int> relativeSortArray(vector<int> &arr1, vector<int> &arr2) { unordered_map<int, int> pos; for (int i = 0; i < arr2.size(); ++i) { pos[arr2[i]] = i; } vector<pair<int, int>> arr; for (int i = 0; i < arr1.size(); ++i) { int j = pos.count(arr1[i]) ? pos[arr1[i]] : arr2.size(); arr.emplace_back(j, arr1[i]); } sort(arr.begin(), arr.end()); for (int i = 0; i < arr1.size(); ++i) { arr1[i] = arr[i].second; } return arr1; }};class Solution: def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: pos = {x: i for i, x in enumerate(arr2)} return sorted(arr1, key=lambda x: pos . get(x, 1000 + x))Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.