Taro Logo

Find if Array Can Be Sorted

Medium
Asked by:
Profile picture
Profile picture
Profile picture
Profile picture
76 views
Topics:
ArraysBit Manipulation

You are given a 0-indexed array of positive integers nums.

In one operation, you can swap any two adjacent elements if they have the same number of set bits. You are allowed to do this operation any number of times (including zero).

Return true if you can sort the array in ascending order, else return false.

Example 1:

Input: nums = [8,4,2,30,15]
Output: true
Explanation: Let's look at the binary representation of every element. The numbers 2, 4, and 8 have one set bit each with binary representation "10", "100", and "1000" respectively. The numbers 15 and 30 have four set bits each with binary representation "1111" and "11110".
We can sort the array using 4 operations:
- Swap nums[0] with nums[1]. This operation is valid because 8 and 4 have one set bit each. The array becomes [4,8,2,30,15].
- Swap nums[1] with nums[2]. This operation is valid because 8 and 2 have one set bit each. The array becomes [4,2,8,30,15].
- Swap nums[0] with nums[1]. This operation is valid because 4 and 2 have one set bit each. The array becomes [2,4,8,30,15].
- Swap nums[3] with nums[4]. This operation is valid because 30 and 15 have four set bits each. The array becomes [2,4,8,15,30].
The array has become sorted, hence we return true.
Note that there may be other sequences of operations which also sort the array.

Example 2:

Input: nums = [1,2,3,4,5]
Output: true
Explanation: The array is already sorted, hence we return true.

Example 3:

Input: nums = [3,16,8,4,2]
Output: false
Explanation: It can be shown that it is not possible to sort the input array using any number of operations.

Constraints:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 28

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. Can the input array contain negative numbers, zero, or floating-point numbers?
  2. What is the expected size range of the input array? Is memory usage a significant concern?
  3. Are there any constraints on the values within the array (e.g., maximum or minimum value)?
  4. By 'sorted,' do you mean strictly ascending order (i.e., no duplicates) or non-decreasing order (duplicates allowed)?
  5. If the array is already sorted, or inherently sortable as defined by the problem, what should the function return?

Brute Force Solution

Approach

The brute force approach to checking if an arrangement of numbers can be sorted by doing a single 'swap' involves testing every possible pair of numbers in the arrangement. We try swapping each pair and see if the resulting arrangement is then sorted from smallest to largest. If even one such swap leads to a sorted arrangement, we know the original arrangement could be sorted with a single swap.

Here's how the algorithm would work step-by-step:

  1. Start with the original arrangement of numbers.
  2. Pick any two numbers from the arrangement.
  3. Imagine switching their positions.
  4. Check if the new arrangement created by switching those two numbers is sorted correctly, from smallest to largest.
  5. If the new arrangement is sorted, then we know the original arrangement could be sorted by swapping those two numbers, so we can stop and say yes.
  6. If the new arrangement is not sorted, put the two numbers back in their original positions and try a different pair of numbers.
  7. Keep doing this, trying every possible pair of numbers to switch.
  8. If you try every possible pair, and none of the switches resulted in a sorted arrangement, then the original arrangement cannot be sorted by swapping just one pair of numbers.

Code Implementation

def find_if_array_can_be_sorted(arrangement_of_numbers):
    list_length = len(arrangement_of_numbers)

    for first_index in range(list_length):
        for second_index in range(first_index + 1, list_length):

            # Create a copy to avoid modifying the original list
            swapped_arrangement = arrangement_of_numbers[:]
            swapped_arrangement[first_index], swapped_arrangement[second_index] = swapped_arrangement[second_index], swapped_arrangement[first_index]

            # Check if the swapped arrangement is sorted
            is_sorted = True
            for index in range(list_length - 1):
                # Compare adjacent elements to confirm proper ordering.
                if swapped_arrangement[index] > swapped_arrangement[index + 1]:
                    is_sorted = False
                    break

            if is_sorted:
                return True

    # No swap resulted in a sorted arrangement
    return False

Big(O) Analysis

Time Complexity
O(n³)The algorithm iterates through all possible pairs of elements in the array to consider swapping them. This involves nested loops, where the outer loop iterates n times and the inner loop iterates approximately n times (resulting in n * n iterations). For each pair, the algorithm checks if the array is sorted after the swap. Checking if the array is sorted requires iterating through all n elements of the array. Therefore, the overall time complexity is O(n * n * n) which simplifies to O(n³).
Space Complexity
O(1)The described brute force approach operates directly on the input array. It only involves swapping elements in place and checking if the modified array is sorted. No auxiliary data structures, like temporary arrays or hash maps, are created to store intermediate results. The space used remains constant regardless of the input array's size, N, as only a few index variables are needed to track the elements being swapped.

Optimal Solution

Approach

The goal is to determine if we can sort the given sequence with only one type of operation: reversing a sub-section. The trick is to figure out if reversing one section is enough to put everything in order. We will check if the sequence is nearly sorted already.

Here's how the algorithm would work step-by-step:

  1. First, check if the sequence is already sorted. If it is, the answer is yes, we don't need any operation.
  2. If it's not sorted, find the start of the out-of-order section. This is where the sequence goes from increasing to decreasing.
  3. Next, find the end of the out-of-order section. This is where the sequence goes back to increasing.
  4. Reverse only this section of the sequence.
  5. Finally, check if the entire sequence is now sorted after the reversal. If it is, the answer is yes. Otherwise, the answer is no because one reversal was not enough.

Code Implementation

def can_array_be_sorted(sequence):
    number_of_elements = len(sequence)

    # Check if the array is already sorted
    if all(sequence[i] <= sequence[i + 1] for i in range(number_of_elements - 1)):
        return True

    start_of_out_of_order_section = -1
    for i in range(number_of_elements - 1):
        if sequence[i] > sequence[i + 1]:
            start_of_out_of_order_section = i
            break

    end_of_out_of_order_section = -1
    if start_of_out_of_order_section != -1:
        for i in range(start_of_out_of_order_section, number_of_elements - 1):
            if i + 2 < number_of_elements and sequence[i + 1] > sequence[i + 2]:
                continue
            elif i+1 < number_of_elements:
                end_of_out_of_order_section = i + 1
                break
        else:
            end_of_out_of_order_section = number_of_elements - 1

    # If the array is not sorted, reverse the out-of-order section
    if start_of_out_of_order_section != -1:

        #Reverse only this section of the sequence
        reversed_section = sequence[start_of_out_of_order_section:end_of_out_of_order_section + 1][::-1]
        sequence[start_of_out_of_order_section:end_of_out_of_order_section + 1] = reversed_section

    # Check if the array is sorted after reversal.
    # If it is, one reversal was enough.
    if all(sequence[i] <= sequence[i + 1] for i in range(number_of_elements - 1)):
        return True

    return False

Big(O) Analysis

Time Complexity
O(n)The algorithm involves iterating through the input array of size n multiple times, but each iteration is linear. The initial check for sorted order takes O(n) time. Finding the start and end of the out-of-order section each take O(n) time. Reversing the sub-section also takes O(n) time. Finally, checking if the entire sequence is sorted after reversal again takes O(n) time. Since we perform a fixed number of O(n) operations sequentially, the overall time complexity is O(n).
Space Complexity
O(1)The algorithm uses a few integer variables to store the start and end indices of the out-of-order section. Reversing the sub-section is done in-place, so no additional data structures are needed to store the reversed portion. The check for sorted order also operates in place using a few variables. Therefore, the auxiliary space used is constant regardless of the input size N, leading to O(1) space complexity.

Edge Cases

Null or empty input array
How to Handle:
Return true immediately as an empty array is considered sorted.
Array with one element
How to Handle:
Return true, as an array with one element is considered sorted.
Array already sorted in ascending order
How to Handle:
The algorithm should correctly identify that no swaps are needed and return true.
Array already sorted in descending order
How to Handle:
The algorithm must perform the necessary swaps to potentially sort the array and check if it becomes sorted.
Array with all identical elements
How to Handle:
The algorithm should return true as no swaps would be needed to make it sorted because it already is.
Array with a single element out of order
How to Handle:
The algorithm should correctly identify the need for one or more swaps and then confirm if it can be sorted by those swaps.
Array where sorting requires more than one pair of adjacent swaps
How to Handle:
The algorithm must determine if performing only adjacent swaps can result in a sorted array, returning false if not.
Integer overflow if calculating indices for very large arrays
How to Handle:
Use appropriate data types (e.g., long) or consider limiting the input array size to prevent overflow when calculating indices.