1460. Make Two Arrays Equal by Reversing Subarrays
Problem Description
You have two integer arrays target
and arr
that have the same length. Your goal is to transform arr
to make it exactly equal to target
.
The only operation you can perform is:
- Select any non-empty subarray from
arr
and reverse it
You can perform this reversal operation as many times as you want (including zero times).
Return true
if it's possible to make arr
equal to target
through these operations, or false
if it's impossible.
Example understanding:
- If
arr = [1, 2, 3, 4]
and you reverse the subarray from index 1 to 3, you get[1, 4, 3, 2]
- A subarray can be as small as a single element or as large as the entire array
- The operation can be repeated multiple times on different (or same) subarrays
The key insight is that if both arrays contain the same elements with the same frequencies, we can always rearrange one to match the other through a series of reversals. This is because reversing subarrays allows us to move any element to any position through a sequence of operations. Therefore, checking if the sorted versions of both arrays are equal tells us whether transformation is possible.
Intuition
Let's think about what the reversal operation actually allows us to do. When we reverse a subarray, we're essentially rearranging elements within that portion of the array.
Consider what happens with repeated reversals:
- Reversing a subarray of size 2 swaps two adjacent elements
- By carefully choosing which subarrays to reverse, we can move any element to any position
For example, to move an element from position i
to position j
:
- We can use a series of adjacent swaps (reversing subarrays of size 2)
- This is similar to bubble sort, where we can bubble any element to any position
Since we can perform unlimited reversals and move any element anywhere, the question becomes: Do both arrays contain the exact same elements with the same frequencies?
If target
has two 3's, one 5, and three 7's, then arr
must also have exactly two 3's, one 5, and three 7's for transformation to be possible. The actual positions don't matter because we can rearrange them however we want through reversals.
This realization leads us to a simple check: if both arrays contain the same elements with the same counts, they can be made equal. The easiest way to verify this is to sort both arrays and compare them. If sorted(target) == sorted(arr)
, then:
- They have the same elements
- Each element appears the same number of times in both arrays
- Therefore, we can transform one into the other through reversals
Learn more about Sorting patterns.
Solution Approach
The solution implements the sorting-based approach we identified from our intuition. Here's how it works:
Algorithm Steps:
- Sort both the
target
array and thearr
array - Compare the sorted arrays for equality
- Return
true
if they're equal,false
otherwise
Implementation Details:
The Python solution is remarkably concise:
return sorted(target) == sorted(arr)
This single line accomplishes everything we need:
sorted(target)
creates a new sorted list from the target arraysorted(arr)
creates a new sorted list from the arr array- The
==
operator compares these two sorted lists element by element - If all elements match at their respective positions, it returns
true
; otherwisefalse
Why This Works:
When we sort both arrays:
- Elements with the same value are grouped together
- If both arrays have the same elements with the same frequencies, their sorted versions will be identical
- For example:
target = [3, 7, 1, 3]
becomes[1, 3, 3, 7]
after sortingarr = [1, 3, 7, 3]
becomes[1, 3, 3, 7]
after sorting- Since the sorted arrays are equal, we can transform one into the other
Time and Space Complexity:
- Time Complexity:
O(n log n)
wheren
is the length of the arrays, due to the sorting operation - Space Complexity:
O(n)
for creating the sorted copies of the arrays
This approach elegantly reduces the problem from finding a sequence of reversals to simply checking if two arrays contain the same multiset of elements.
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 concrete example to see how the solution works.
Example 1: Arrays that can be made equal
Given:
target = [3, 7, 9, 3]
arr = [3, 9, 3, 7]
Step 1: Sort both arrays
sorted(target) = [3, 3, 7, 9]
sorted(arr) = [3, 3, 7, 9]
Step 2: Compare sorted arrays
- Both sorted arrays are
[3, 3, 7, 9]
- They are equal, so we return
true
Verification through reversals:
To confirm this works, let's actually transform arr
to target
:
- Start:
arr = [3, 9, 3, 7]
, wanttarget = [3, 7, 9, 3]
- Reverse subarray from index 1 to 2:
[3, 3, 9, 7]
- Reverse subarray from index 1 to 3:
[3, 7, 9, 3]
- We've successfully transformed
arr
intotarget
!
Example 2: Arrays that cannot be made equal
Given:
target = [1, 2, 2, 3]
arr = [1, 1, 2, 3]
Step 1: Sort both arrays
sorted(target) = [1, 2, 2, 3]
sorted(arr) = [1, 1, 2, 3]
Step 2: Compare sorted arrays
sorted(target)
has two 2's at positions 1 and 2sorted(arr)
has two 1's at positions 0 and 1- The sorted arrays are different, so we return
false
Why reversals can't help:
No matter how many reversals we perform on arr
, we can't create a second 2 or eliminate a 1. Reversals only rearrange existing elements; they don't change what elements we have or their frequencies.
Solution Implementation
1class Solution:
2 def canBeEqual(self, target: List[int], arr: List[int]) -> bool:
3 """
4 Determine if two arrays can be made equal through any number of operations.
5
6 The operation allowed is: reverse any subarray of arr.
7 Since we can reverse any subarray, we can effectively perform any permutation.
8 Therefore, two arrays can be made equal if they contain the same elements
9 with the same frequencies.
10
11 Args:
12 target: The target array to match
13 arr: The array to transform through operations
14
15 Returns:
16 True if arr can be transformed to equal target, False otherwise
17 """
18 # Sort both arrays and compare - if they have the same elements
19 # with same frequencies, their sorted versions will be identical
20 return sorted(target) == sorted(arr)
21
1class Solution {
2 /**
3 * Determines if two arrays can be made equal by rearranging elements.
4 *
5 * The approach sorts both arrays and checks if they contain the same elements
6 * with the same frequencies. If the sorted arrays are equal, then one can be
7 * rearranged to match the other.
8 *
9 * @param target The target array to match
10 * @param arr The array to be rearranged
11 * @return true if arr can be rearranged to equal target, false otherwise
12 */
13 public boolean canBeEqual(int[] target, int[] arr) {
14 // Sort both arrays to compare their elements
15 Arrays.sort(target);
16 Arrays.sort(arr);
17
18 // Check if the sorted arrays are identical
19 // If they are, arr can be rearranged to match target
20 return Arrays.equals(target, arr);
21 }
22}
23
1class Solution {
2public:
3 /**
4 * Determines if two arrays can be made equal by reordering elements.
5 *
6 * @param target The target array to compare against
7 * @param arr The array to check if it can be rearranged to match target
8 * @return true if arr can be rearranged to equal target, false otherwise
9 */
10 bool canBeEqual(vector<int>& target, vector<int>& arr) {
11 // Sort both arrays in ascending order
12 sort(target.begin(), target.end());
13 sort(arr.begin(), arr.end());
14
15 // After sorting, if arrays have the same elements with same frequencies,
16 // they will be identical
17 return target == arr;
18 }
19};
20
1/**
2 * Determines if two arrays can be made equal by reordering elements
3 * @param target - The target array to compare against
4 * @param arr - The array to check if it can be reordered to match target
5 * @returns true if arrays contain the same elements with same frequencies, false otherwise
6 */
7function canBeEqual(target: number[], arr: number[]): boolean {
8 // Sort both arrays in ascending order
9 target.sort((a, b) => a - b);
10 arr.sort((a, b) => a - b);
11
12 // Check if every element at each index matches after sorting
13 // If all elements match, the arrays contain the same elements with same frequencies
14 return target.every((element, index) => element === arr[index]);
15}
16
Time and Space Complexity
The time complexity is O(n Γ log n)
, where n
is the length of the arrays. This is because the sorted()
function in Python uses Timsort algorithm, which has a time complexity of O(n Γ log n)
for sorting each array. Since we sort both target
and arr
, we perform two sorting operations, but this still results in O(n Γ log n)
overall complexity.
The space complexity is O(n)
for the general case. The sorted()
function creates new sorted lists for both target
and arr
, requiring O(n)
space for each sorted array. However, the reference answer states O(log n)
space complexity, which would be accurate if we consider only the auxiliary space used by the sorting algorithm itself (the recursion stack depth in Timsort), excluding the space for the output sorted arrays. In the context of this specific problem where we're creating new sorted arrays, the total space used is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Attempting to Track Actual Reversal Operations
A common mistake is trying to find the actual sequence of reversals needed to transform arr
into target
. Developers might attempt complex algorithms involving:
- Tracking positions and movements of elements
- Implementing actual reversal operations
- Using BFS/DFS to explore different reversal sequences
Why it's problematic: This approach is unnecessarily complex and can lead to exponential time complexity as there are many possible sequences of operations.
Solution: Recognize that we only need to determine if transformation is possible, not find the actual steps. The sorting approach elegantly sidesteps this complexity.
2. Using a Hash Map/Counter Without Proper Comparison
Some might use a frequency counter but implement the comparison incorrectly:
# Incorrect approach - only checks keys, not frequencies
def canBeEqual(self, target, arr):
return set(target) == set(arr) # Wrong! Ignores duplicates
Why it's problematic: Using sets removes duplicate values, so [1, 1, 2]
and [1, 2, 2]
would incorrectly return true
.
Solution: If using a frequency-based approach, properly count occurrences:
from collections import Counter
def canBeEqual(self, target, arr):
return Counter(target) == Counter(arr)
3. Modifying Input Arrays In-Place
Attempting to sort arrays in-place to save space:
# Problematic if arrays shouldn't be modified
def canBeEqual(self, target, arr):
target.sort() # Modifies original array
arr.sort() # Modifies original array
return target == arr
Why it's problematic: This modifies the input arrays, which might be unexpected behavior for the caller and could cause issues if the arrays are used elsewhere.
Solution: Use sorted()
which returns new sorted lists without modifying the originals, or explicitly copy arrays first if in-place sorting is needed for space optimization.
4. Overlooking Edge Cases with Empty Arrays
Not considering whether empty arrays or single-element arrays need special handling.
Why it's problematic: Developers might add unnecessary special case handling:
def canBeEqual(self, target, arr):
if len(target) == 0 or len(arr) == 0:
return len(target) == len(arr) # Unnecessary
return sorted(target) == sorted(arr)
Solution: The sorting approach naturally handles all edge cases including empty arrays and single elements. No special handling is needed.
Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?
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!