3011. Find if Array Can Be Sorted
Problem Description
In this problem, we are given an array of positive integers called nums
, and our task is to determine if we can sort it using a specific operation. The operation allows us to swap any two adjacent elements, but only if those elements have the same number of 1s in their binary representations; this is known as the number of set bits. We can perform this operation as many times as needed, including not at all, if the array is already sorted.
In essence, we need to check if, by using the allowed swaps, we can arrange the array in non-decreasing order. If we find it's possible to achieve this, we should return true
. Otherwise, we return false
if it's impossible to sort the array under these constraints.
Intuition
The solution hinges on understanding that we cannot change the relative order of elements with different numbers of set bits since they can't be swapped directly based on the rule. Therefore, sorting the array essentially breaks it down into subarrays, each composed of elements with the same number of set bits, which could be ordered in any way since swaps among them are allowed.
With this in mind, we can iterate through the array and identify these subarrays. Within each subarray, we keep track of the minimum and maximum value encountered so far. A crucial insight is that if the minimum value in a subarray is less than the maximum value of the previous subarray, it indicates that sorting is impossible. This is because, in a sorted array, all elements in a previous (and thus lower) subarray should be less than or equal to all the elements in the following subarray.
To implement this, we use two pointers as we traverse the array. The first pointer, i
, marks the start of a subarray, and the second pointer, j
, explores the rest of the array to find the end of this subarray. The while
loops ensure that we progress through the array, from one subarray to the next, assessing the minimum and maximum for each set bit count. If we successfully traverse the entire array without encountering a condition that makes sorting impossible, we return true
. Otherwise, we return false
as soon as such a condition is found.
Learn more about Sorting patterns.
Solution Approach
The implementation follows a straightforward two-pointer approach to tackle the problem:
-
Initialize a variable
pre_mx
to negative infinity. This will keep track of the maximum value of the previous subarray as we progress throughnums
. -
Set up two pointers,
i
andj
, starting from 0 (i
) and 1 (j
), respectively. Pointeri
represents the beginning of the current subarray, whilej
will be used to find the end of this subarray. -
Loop over
nums
usingi
to iterate through the array. For each positioni
, we: a. Find the number of set bits ofnums[i]
, which determines the current subarray we're evaluating. This can be done using the.bit_count()
method in Python. b. Initializemi
andmx
to the value ofnums[i]
. These will track the minimum and maximum values within the current subarray, respectively. -
While inside the loop, initiate a nested loop that keeps running as long as
j
is within the bounds of the array andnums[j]
shares the same number of set bits asnums[i]
. Within this loop: a. Updatemi
andmx
to be the minimum and maximum of the current subarray by comparing them tonums[j]
. b. Incrementj
to check the next element. -
After the nested loop terminates (meaning we reached the end of the current subarray with matching set bits), check if
pre_mx
is greater thanmi
. If it is, it means that the previous subarray had a value greater than the smallest value of the current subarray, which implies it is impossible to sort the array. Hence, returnFalse
. -
Update
pre_mx
with the largest value in the current subarray,mx
, because this now becomes the maximum of the last processed subarray for the next iteration. -
Set pointer
i
toj
to start the algorithm for the next subarray of elements with equal set bit count. -
If the algorithm completes the iteration over the array without returning
False
, it means sorting the array is possible under the given rules, so it returnsTrue
.
This approach efficiently applies the rules of the problem to determine if sorting is feasible, relying on the fact that only elements with the same number of set bits can be adjacent in the final sorted array. It uses python's built-in .bit_count()
method to count set bits and utilizes comparison operations to ensure order within and between identified subarrays.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach using a small example with the array nums = [3, 1, 2, 4]
. The binary representations are 11
, 01
, 10
, and 100
, with set bit counts of 2
, 1
, 1
, and 1
, respectively.
-
Initialize
pre_mx
to negative infinity. Start withi = 0
andj = 1
. -
At
i = 0
,nums[i] = 3
. Number of set bits in3
(11
in binary) is2
. Initialize min (mi
) and max (mx
) of the current subarray to the value ofnums[i]
which is3
. -
Move to
j
which is1
. Number of set bits innums[j]
(1
in binary) is1
, which is different from the2
set bits we have fornums[i]
. So we close this subarray here because we cannot form a subarray with different set bit counts. Since we cannot includenums[j]
in the current subarray, we skip updatingmi
andmx
and proceed to step 5. -
We check if
pre_mx > mi
which means if-inf > 3
. This is not true, so we move on and updatepre_mx
tomx
which is3
. -
We set
i
toj
, thereforei
is now1
and incrementj
to2
to start evaluating the next subarray. -
At
i = 1
,nums[i]
is1
. The number of set bits is1
. Initializemi
andmx
to1
. -
j = 2
,nums[j] = 2
which also has1
set bit. We includenums[j]
in the current subarray and updatemi
tomin(mi, nums[j])
(which remains1
) andmx
tomax(mx, nums[j])
(which becomes2
). Incrementj
to3
. -
j = 3
,nums[j] = 4
which also has1
set bit. We includenums[j]
in the current subarray and updatemi
tomin(mi, nums[j])
(which is still1
) andmx
tomax(mx, nums[j])
(which becomes4
). After this,j
is out of bounds (sincenums
has only four elements), so we exit this nested loop. -
We check if
pre_mx > mi
which means if3 > 1
. This is not true, so we updatepre_mx
tomx
which is now4
. -
Since we've reached the end of the array, we've verified that each subarray with the same set bit count is such that its minimum value is not less than the maximum value of the previous subarray.
The result here is that the array can be sorted using the allowed operations: swap 1
and 2
to get [3, 2, 1, 4]
, which is now in the non-decreasing order. Our algorithm returns True
.
Solution Implementation
1from math import inf
2
3class Solution:
4 def canSortArray(self, nums: List[int]) -> bool:
5 # Initialize previous maximum to negative infinity.
6 previous_max = -inf
7 # Initialize index and get the length of the list.
8 i, n = 0, len(nums)
9
10 # Iterate through the list of numbers.
11 while i < n:
12 # Initialize the next index and get the bit count of the current number.
13 j = i + 1
14 bit_count = bin(nums[i]).count('1')
15 # Keep track of the minimum and maximum for the current bit count.
16 current_min = current_max = nums[i]
17
18 # Slide through the array to find consecutive numbers with the same bit count.
19 while j < n and bin(nums[j]).count('1') == bit_count:
20 current_min = min(current_min, nums[j])
21 current_max = max(current_max, nums[j])
22 j += 1
23
24 # If the previous maximum number is greater than the current minimum,
25 # the array cannot be sorted based on the conditions, hence return False.
26 if previous_max > current_min:
27 return False
28 # Update previous maximum for the next iteration.
29 previous_max = current_max
30 # Move to the next segment with a different bit count.
31 i = j
32
33 # If we've reached this point, the array can be sorted, therefore return True.
34 return True
35
1class Solution {
2 public boolean canSortArray(int[] nums) {
3 // Initialize the previous maximum value to the lowest possible value.
4 int prevMax = Integer.MIN_VALUE;
5
6 // Index 'i' will track the start of each segment with equal bit count.
7 int i = 0;
8 // 'n' holds the length of the input array.
9 int n = nums.length;
10
11 // Loop through the elements of the array.
12 while (i < n) {
13 // 'j' will track the end of the current segment.
14 int j = i + 1;
15 // 'bitCount' stores the number of 1-bits in current array element.
16 int bitCount = Integer.bitCount(nums[i]);
17 // 'min' and 'max' track the minimum and maximum of the current segment.
18 int min = nums[i], max = nums[i];
19
20 // Continue to next elements if they have the same bit count.
21 while (j < n && Integer.bitCount(nums[j]) == bitCount) {
22 min = Math.min(min, nums[j]);
23 max = Math.max(max, nums[j]);
24 j++;
25 }
26
27 // If the max value of the previous segment is greater than the min of the current, it can't be sorted.
28 if (prevMax > min) {
29 return false;
30 }
31 // Update the prevMax to the max value of the current segment.
32 prevMax = max;
33
34 // Move 'i' to the start of the next segment.
35 i = j;
36 }
37 // If all segments are in the correct order, return true.
38 return true;
39 }
40}
41
1#include <vector>
2#include <algorithm>
3using namespace std;
4
5class Solution {
6public:
7 bool canSortArray(vector<int>& nums) {
8 int previousMax = -300; // Initial value considering the constraints for possible integer values
9 int currentIndex = 0, numSize = nums.size();
10
11 // Loop through all elements in the array
12 while (currentIndex < numSize) {
13 int nextIndex = currentIndex + 1;
14 int currentPopCount = __builtin_popcount(nums[currentIndex]);
15 int currentMin = nums[currentIndex], currentMax = nums[currentIndex];
16
17 // Find subsequence where all elements have the same number of set bits (1s)
18 while (nextIndex < numSize && __builtin_popcount(nums[nextIndex]) == currentPopCount) {
19 currentMin = min(currentMin, nums[nextIndex]);
20 currentMax = max(currentMax, nums[nextIndex]);
21 nextIndex++;
22 }
23
24 // If the maximum value from previous segment is greater than minimum of the current,
25 // then the array can't be sorted based on the rules given
26 if (previousMax > currentMin) {
27 return false;
28 }
29 previousMax = currentMax; // Update previousMax to the max of the current segment
30 currentIndex = nextIndex; // Move to the next segment
31 }
32
33 // If the loop completes without returning false, it means the array can be sorted
34 return true;
35 }
36};
37
1function canSortArray(nums: number[]): boolean {
2 let previousMax = -300; // Initiate the previous maximum to a value lower than any element in nums
3 const length = nums.length;
4
5 // Iterate over the array
6 for (let i = 0; i < length; ) {
7 let j = i + 1; // Start from the next element
8 const bitCountOfCurrent = bitCount(nums[i]); // Get the bit count of the current element
9
10 // Find min and max element within the same bit count group
11 let minElement = nums[i];
12 let maxElement = nums[i];
13
14 // Keep updating min/max in the group where the bit count is the same
15 while (j < length && bitCount(nums[j]) === bitCountOfCurrent) {
16 minElement = Math.min(minElement, nums[j]);
17 maxElement = Math.max(maxElement, nums[j]);
18 j++;
19 }
20
21 // If the max of the previous group is greater than the min of the current group, return false
22 if (previousMax > minElement) {
23 return false;
24 }
25
26 // Update the previousMax to the max of the current group
27 previousMax = maxElement;
28 i = j; // Move to the next group
29 }
30
31 // If the entire array can be separate into groups with incremental mins, return true
32 return true;
33}
34
35function bitCount(i: number): number {
36 // Apply bit manipulation tricks to count the bits set to 1
37 i = i - ((i >>> 1) & 0x55555555);
38 i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
39 i = (i + (i >>> 4)) & 0x0f0f0f0f;
40 i = i + (i >>> 8);
41 i = i + (i >>> 16);
42 return i & 0x3f; // Return the count of bits set to 1
43}
44
Time and Space Complexity
Time Complexity
The time complexity of the code is O(n)
because there is a single while loop that iterates through each element of the array nums
exactly once. Inside the loop, the .bit_count()
method is called, which is expected to run in constant time, and basic arithmetic operations and comparisons are performed, which also take constant time per iteration. Even though there is a nested loop, it does not increase the overall number of iterations; it just continues from where the outer loop left off.
Space Complexity
The space complexity of the code is O(1)
since it uses a fixed amount of extra space: variables pre_mx
, i
, n
, j
, cnt
, mi
, and mx
. There is no use of any data structures that grow with the size of the input array nums
, which keeps the space complexity constant.
Learn more about how to find time and space complexity quickly using problem constraints.
Which data structure is used in a depth first search?
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
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!