Create Sorted Array Through Instructions in Java

Given an array of integers called instructions. The task is to construct a sorted array from these elements. We have an empty container called nums. We will process each element of instructions from left to right. When inserting an element into nums, the cost of insertion is determined by the number of elements already in nums that are either strictly less than or strictly greater than the current element. We should minimize this cost at each insertion.

Example 1:

Input: instructions = [1,5,6,2]

Output: 1

Explanation: Begin with nums = [].

Insert 1 with cost min(0, 0) = 0, now nums = [1].

Insert 5 with cost min(1, 0) = 0, now nums = [1,5].

Insert 6 with cost min(2, 0) = 0, now nums = [1,5,6].

Insert 2 with cost min(1, 2) = 1, now nums = [1,2,5,6].

The total cost is 0 + 0 + 0 + 1 = 1.

Example 2:

Input: instructions = [1,2,3,6,5,4]

Output: 3

Explanation: Begin with nums = [].

Insert 1 with cost min(0, 0) = 0, now nums = [1].

Insert 2 with cost min(1, 0) = 0, now nums = [1,2].

Insert 3 with cost min(2, 0) = 0, now nums = [1,2,3].

Insert 6 with cost min(3, 0) = 0, now nums = [1,2,3,6].

Insert 5 with cost min(3, 1) = 1, now nums = [1,2,3,5,6].

Insert 4 with cost min(3, 2) = 2, now nums = [1,2,3,4,5,6].

The total cost is 0 + 0 + 0 + 0 + 1 + 2 = 3.

Example 3:

Input: instructions = [1,3,3,3,2,4,2,1,2]

Output: 4

Explanation: Begin with nums = [].

Insert 1 with cost min(0, 0) = 0, now nums = [1].

Insert 3 with cost min(1, 0) = 0, now nums = [1,3].

Insert 3 with cost min(1, 0) = 0, now nums = [1,3,3].

Insert 3 with cost min(1, 0) = 0, now nums = [1,3,3,3].

Insert 2 with cost min(1, 3) = 1, now nums = [1,2,3,3,3].

Insert 4 with cost min(5, 0) = 0, now nums = [1,2,3,3,3,4].

Insert 2 with cost min(1, 4) = 1, now nums = [1,2,2,3,3,3,4].

Insert 1 with cost min(0, 6) = 0, now nums = [1,1,2,2,3,3,3,4].

Insert 2 with cost min(2, 4) = 2, now nums = [1,1,2,2,2,3,3,3,4].

The total cost is 0 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 2 = 4.

Approach 1: Using Brute Force

The Brute Force approach involves iterating through the instructions array and processing each element one by one. For each element in the instructions array, the code determines the number of elements smaller and larger than the current element that already exists in the sorted array constructed so far. It then calculates the cost of insertion based on these counts and updates the total cost accordingly.

Algorithm

Step 1: Initialize a variable cost to 0 to keep track of the total cost.

Step 2: Initialize an empty list nums to store the elements.

Step 3: Iterate through each element in the instructions array.

Step 4: For each element, iterate through the elements already in nums to count the number of elements smaller and larger than the current element.

Step 5: Calculate the cost of insertion as the minimum of the smaller and larger counts.

Step 6: Increment the total cost by the calculated cost.

Step 7: Insert the current element from the instructions into nums at the position determined by the smaller count.

Step 8: Repeat the steps for each element in the instructions.

Step 9: Return the total cost.

Filename: SortedArrayCostUsingBruteForce.java

import java.util.ArrayList;

import java.util.List;

public class SortedArrayCostUsingBruteForce {

    // Method to create a sorted array and calculate the total cost of insertion

    public static int createSortedArray(int[] instructions) {

        int cost = 0; // Initialize the total cost to 0

        List<Integer> nums = new ArrayList<>(); // Create an empty list to store elements

        // Iterate through each element in the instructions array

        for (int i = 0; i < instructions.length; i++) {

            int smaller = 0, larger = 0; // Initialize counters for elements smaller and larger than the current element

            // Iterate through each element already in nums

            for (int num : nums) {

                if (num < instructions[i]) // If the element is smaller than the current element in instructions

                    smaller++; // Increment the smaller counter

                else if (num > instructions[i]) // If the element is larger than the current element in instructions

                    larger++; // Increment the larger counter

            }

            // Calculate the cost of insertion as the minimum of smaller and larger counters

            cost = (cost + Math.min(smaller, larger)) % 1000000007;

            // Insert the current element from instructions into nums at the position determined by the smaller counter

            nums.add(smaller, instructions[i]);

        }

        return cost; // Return the total cost

    }

    // Main method to test the createSortedArray method

    public static void main(String[] args) {

        int[] instructions = {1, 5, 6, 2};

        System.out.println("Total cost: " + createSortedArray(instructions));

    }

}

Output

Total cost: 1

Time Complexity: The time complexity of an algorithm is O(n^2), where n is the number of elements in the instructions array. This is because, for each element in instructions, we can iterate through all the elements already in nums to count the smaller and larger elements.

Space Complexity: The space complexity of an algorithm is O(n), where n is the number of elements in the instructions array. This is because we use an additional list nums to store the elements.

Approach 2: Using Binary Indexed Tree

The Binary Indexed Tree is also known as Fenwick Tree. This approach is to efficiently calculate the number of elements smaller and larger than the current element during each insertion.

Algorithm

Step 1: Initialize a binary indexed tree (BIT) with size equal to the maximum element in the instructions array.

Step 2: Iterate through each element in the instructions array.

Step 3: For each element, query the BIT to find the number of elements smaller than the current element and the number of elements larger than the current element.

Step 4: Calculate the cost of insertion as the minimum of the smaller and larger counts.

Step 5: Update the BIT to reflect the insertion of the current element.

Step 6: Accumulate the total cost of insertion.

Step 7: Repeat steps for each element in the instructions array.

Step 8: Return the total cost.

Filename: SortedArrayCostUsingBIT.java

import java.util.Arrays;

public class SortedArrayCostUsingBIT {

    // Method to create a sorted array and calculate the total cost of insertion

    public static int createSortedArray(int[] instructions) {

        int mod = (int)1e9 + 7; // Modulus value

        int cost = 0; // Initialize the total cost to 0

        int maxValue = Arrays.stream(instructions).max().getAsInt(); // Find the maximum element in the instructions array

        int[] bit = new int[maxValue + 1]; // Create a Binary Indexed Tree (BIT) with size equal to the maximum element + 1

        // Iterate through each element in the instructions array

        for (int i = 0; i < instructions.length; i++) {

            // Query the BIT to find the number of elements smaller than the current element

            int smaller = query(bit, instructions[i] - 1);

            // Calculate the number of elements larger than the current element by subtracting the count of smaller elements from the current index

            int larger = i - query(bit, instructions[i]);

            // Calculate the cost of insertion as the minimum of smaller and larger counts

            cost = (cost + Math.min(smaller, larger)) % mod;

            // Update the BIT to reflect the insertion of the current element

            update(bit, instructions[i], 1);

        }

        return cost; // Return the total cost

    }

    // Method to query the BIT and calculate the sum of elements from index 1 to index

    public static int query(int[] bit, int index) {

        int sum = 0; // Initialize sum to 0

        // Traverse the BIT upwards from the given index and add the values to sum

        while (index > 0) {

            sum = (sum + bit[index]) % 1000000007; // Add the value at the current index to sum

            index -= index & (-index); // Move to the parent node in the BIT

        }

        return sum; // Return the sum

    }

    // Method to update the BIT with the given index and value

    public static void update(int[] bit, int index, int value) {

        // Traverse the BIT upwards from the given index and update the values

        while (index < bit.length) {

            bit[index] = (bit[index] + value) % 1000000007; // Update the value at the current index

            index += index & (-index); // Move to the next node in the BIT

        }

    }

    // Main method to test the createSortedArray method

    public static void main(String[] args) {

        int[] instructions = {1, 5, 6, 2};

        System.out.println("Total cost: " + createSortedArray(instructions));

    }

}

Output

Total cost: 1

Time Complexity: The time complexity of an algorithm is O(n * log(m)), where n is the number of elements in the instructions array and m is the maximum element in the instructions array. This is because each query and update operation on the BIT takes O(log(m)) time, and there is n such operations.

Space Complexity: The space complexity of an algorithm is O(m), where m is the maximum element in the instructions array. This is because the BIT has a size equal to the maximum element in the instructions array.

Approach 3: Segment Tree

Segment Trees are data structures that allow for efficient querying and updating of intervals. We can use a Segment Tree to keep track of the count of elements smaller than the current element and larger than the current element.

Algorithm

Step 1: Create a segment tree to store counts of smaller elements.

Step 2: Iterate through each element in the instructions array.

Step 3: Query the segment tree to find the count of smaller elements.

Step 4: Calculate the count of elements larger than the current element.

Step 5: Calculate the cost of insertion as the minimum of smaller and larger counts.

Step 6: Add the current element to the segment tree.

Step 7: Repeat steps for each element in the instructions array.

Step 8: Return the total cost.

Filename: SortedArrayCostUsingSegmentTree.java

import java.util.Arrays;

public class SortedArrayCostUsingSegmentTree {

    // Nested class representing the Segment Tree data structure

    static class SegmentTree {

        int[] smaller; // Array to store counts of smaller elements

        int size; // Size of the segment tree

        // Constructor to initialize the segment tree with size n

        public SegmentTree(int n) {

            size = 1;

            // Find the nearest power of 2 greater than or equal to n

            while (size < n) size *= 2;

            // Initialize arrays to store counts of smaller elements

            smaller = new int[2 * size];

        }

        // Method to add an element to the segment tree

        public void add(int index) {

            add(index, 1, 0, 0, size);

        }

        // Recursive helper method to update counts in the segment tree

        private void add(int index, int val, int node, int left, int right) {

            if (right - left == 1) {

                smaller[node]++; // Increment count of smaller elements

                return;

            }

            int mid = left + (right - left) / 2;

            if (index < mid)

                add(index, val, 2 * node + 1, left, mid);

            else

                add(index, val, 2 * node + 2, mid, right);

            // Update the count of smaller elements in the current node

            smaller[node] = smaller[2 * node + 1] + smaller[2 * node + 2];

        }

        // Method to query the segment tree for counts in a range

        public int query(int left, int right, int node, int nodeLeft, int nodeRight) {

            if (nodeRight <= left || nodeLeft >= right) // No overlap

                return 0;

            if (nodeLeft >= left && nodeRight <= right) // Fully covered

                return smaller[node];

            int mid = nodeLeft + (nodeRight - nodeLeft) / 2;

            // Recursively query the left and right children

            return query(left, right, 2 * node + 1, nodeLeft, mid) +

                   query(left, right, 2 * node + 2, mid, nodeRight);

        }

    }

    // Method to calculate the total cost of insertion into a sorted array

    public static int createSortedArray(int[] instructions) {

        int mod = (int)1e9 + 7;

        int cost = 0; // Initialize the total cost

        // Find the maximum element in the instructions array

        int maxValue = Arrays.stream(instructions).max().getAsInt();

        // Initialize a segment tree with size equal to the maximum element + 1

        SegmentTree tree = new SegmentTree(maxValue + 1);

        // Iterate through each element in the instructions array

        for (int i = 0; i < instructions.length; i++) {

            // Query the segment tree to find the count of smaller elements

            int smaller = tree.query(0, instructions[i], 0, 0, tree.size);

            // Calculate the count of elements larger than the current element

            int larger = i - smaller;

            // Calculate the cost of insertion as the minimum of smaller and larger counts

            cost = (cost + Math.min(smaller, larger)) % mod;

            // Add the current element to the segment tree

            tree.add(instructions[i]);

        }

        return cost; // Return the total cost

    }

    // Main method to test the createSortedArray method

    public static void main(String[] args) {

        int[] instructions = {1, 5, 6, 2};

        System.out.println("Total cost: " + createSortedArray(instructions));

    }

}

Output

Total cost: 1

Time Complexity: The time complexity of an algorithm is O(n * log(m)), where n is the number of elements in the instructions array and m is the maximum element in the instructions array. This is because each query and update operation on the segment tree takes O(log(m)) time, and there are n such operations.

Space Complexity: The space complexity of an algorithm is O(m), where m is the maximum element in the instructions array. This is because the segment tree has a size equal to the maximum element in the instructions array.