Day #2 - Problem-Solving

Day #2 - Problem-Solving

I tackled two data structures and algorithms (DSA) problems related to arrays today. I worked on these questions directly within my LeetCode profile.

1. Build Array from Permutation

Problem Statement: Given a zero-based permutation nums (0-indexed), build an array ans of the same length where ans[i] = nums[nums[i]] for each 0 <= i < nums.length and return it.

A zero-based permutation nums is an array of distinct integers from 0 to nums.length - 1 (inclusive).

The approach I used to solve it:

Runtime: 1ms

Time complexity: O(n), where n is length of the input array.

Space Complexity: O(n), where n is the length of ans which is occupying the additional space.

class Solution {
    public int[] buildArray(int[] nums) {

        int[] ans = new int[nums.length];
        for(int i = 0; i < nums.length; i++) {
            ans[i] = nums[nums[i]];
        }
        return ans;
    }
}

Explanation of the above program:

The straightforward approach to solving this problem involves creating a new array, ans[], with the same size as the input array nums[]. Each element in ans[] is determined by placing the element at nums[nums[i]] into its respective position in the ans[] array.

For instance, considering the array nums[] = {5, 0, 1, 2, 3, 4} with a size of 5, the process unfolds as follows:

  • At index 0, ans[0] is set to nums[nums[0]], resulting in ans[0] = nums[5] = 4.

  • Moving to index 1, ans[1] is determined as nums[nums[1]], yielding ans[1] = nums[0] = 5.

  • Similarly, at index 2, ans[2] is computed as nums[nums[2]], leading to ans[2] = nums[1] = 0.

  • This process continues until the final answer is obtained: ans[] = {4, 5, 0, 1, 2, 3}.

This approach efficiently rearranges the elements of the array based on their corresponding values, ultimately achieving the desired outcome.

Optimized approach:

Runtime: 0ms

class Solution {
    public int[] buildArray(int[] nums) {

        // Using O(1) space complexity
        func(nums, 0);
        return nums;
    }

    public void func(int[] nums, int i) {
        if(i < nums.length) {
            int key = nums[i];
            int res = nums[key];
            func(nums, i + 1);
            nums[i] = res;
        }
    }
}

The presented approach employs recursion to iteratively identify a key and a result (res) during each iteration. The key represents the element at the ith index, while the result (res) stores the final value, which corresponds to the element at nums[key].

The subsequent step involves invoking a recursive function, func(), which takes the nums array. With each function call, the index (i) is incremented to refer to the next element in the nums array. As long as the condition (i < nums.length) holds true, the control returns to the previous function call, eventually updating the nums array with each result.

Illustrating this with the example nums[] = {5, 0, 1, 2, 3, 4}:

  • At i = 0, key = nums[0] = 5, res = nums[5] = 4 -> func(nums, i+1)

  • At i = 1, key = nums[1] = 0, res = nums[0] = 5 -> func(nums, i + 1)

  • At i = 2, key = nums[2] = 1, res = nums[1] = 0 -> func(nums, i+1)

  • At i = 3, key = nums[3] = 2, res = nums[2] = 1 -> func(nums, i + 1)

  • At i = 4, key = nums[4] = 3, res = nums[3] = 2 -> func(nums, i+1)

  • At i = 5, key = nums[5] = 4, res = nums[4] = 3 -> func(nums, i + 1)

Once i = 6, violating the condition, control returns to i = 5, and the value of nums at that ith index is updated with res. This process continues until it reaches the main function where it was initially called.

Time complexity: O(n), since func() is a recursive function that processes each element of the nums array at least one.

Space complexity: O(n), the space complexity of the code is determined by the maximum depth of the recursive call stack. In this case, the maximum depth corresponds to the length of the nums array which is n.

Concatenation of Array

Problem statement: Given an integer array nums of length n, you want to create an array ans of length 2n where ans[i] == nums[i] and ans[i + n] == nums[i] for 0 <= i < n (0-indexed).

Specifically, ans is the concatenation of two nums arrays.

Return the array ans.

The approach I used:

Runtime: 1 ms

Time complexity: O(n), as the for loop iterates through each element in the nums array once, n is the size of nums array.

Space complexity: O(n), its determined by the additional space required by ans array, hence the complexity O(n).

class Solution {
    public int[] getConcatenation(int[] nums) {

        // new array
        int ans[] = new int[nums.length * 2];
        for(int i = 0; i < nums.length; i++) {
            ans[i] = ans[i + nums.length] = nums[i];
        }
        return ans;
    }
}

Explanation:

In this approach, a new array ans[] is initialized with a size double that of the nums[] array. For each element at index i in the nums[] array, its value is added at two distinct positions in the ans[] array: at ans[i] and at ans[i + nums.length].

Expressed as ans[i] = ans[i + nums.length] = nums[i], this operation effectively duplicates the value of nums[i] at two different locations in the array. After this process is completed for each element, the resulting ans[] array is returned.

This strategy ensures that the final array contains the elements of the original nums[] array followed by the same elements again in the same order.

Another approach that I found worth mentioning:

class Solution {
    public int[] getConcatenation(int[] nums) {

        int len = nums.length;
        int ans[] = new int[2 * len];

        System.arraycopy(nums, 0, ans, 0, len);
        System.arraycopy(nums, 0, ans, len, len);

        return ans;
    }
}

Here a built-in method called System.arraycopy() is used.

The System.arraycopy() method in Java is used to copy data from one array to another. It provides a fast and efficient way to copy elements from the source array to the destination array.

Syntax of System.arraycopy() :

System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

// src: source array from which elements will be copied
// srcPos: starting position in the source array (src) from where elements will be copied.
// dest: destination array where elements will be copied
// destPos: This is the starting position in the destination array where elements will be copied.
// length: number of elements to be copied

In the described method, the source array is nums[], and initially, we copy a segment of nums[] starting from the 0th position. The number of elements copied equals nums.length, forming the ans[] array. Subsequently, the entire nums[] array is copied into the ans[] array again, but this time, the copying process initiates from the len position.

Time complexity: The time complexity is primarily determined by the two System.arraycopy operations, each of which takes O(len) time to copy the elements.

Space complexity: The space complexity is determined by the additional array ans of size 2 * len. Therefore, the space complexity is O(len).

Until the next update in the journey... #bytebybyte

~ Future Forward :)