Start learning free

LESSON 280 OF 410 · DIVIDE AND CONQUER

Z Pattern in JavaScript with Time Complexity Analysis

Practice O(n2) Read the full explanation

Given an integer n, which is a power of 2, return a square of length n filled with the numbers {1, 2, 3, ... n2} following the "Z Pattern".

This consists of recursively traversing the 4 quadrants in the order: upper-left, upper-right, lower-left, lower-right.

Example 1:

Input: n = 2
Output: [
    [1, 2], 
    [3, 4]
]

Example 2:

Input: n = 4
Output:[
    [1,   2,  5,  6],
    [3,   4,  7,  8],
    [9,  10, 13, 14],
    [11, 12, 15, 16]
]

STEP 1 OF 11
    Ctrl + Enter to check

    JAVASCRIPT · DIVIDE AND CONQUER

    Z Pattern in JavaScript, explained

    Understanding the Problem

    The core challenge of this problem is to fill a 2D array in a specific "Z Pattern" order. This pattern involves recursively dividing the array into four quadrants and filling them in a specific sequence: upper-left, upper-right, lower-left, and lower-right. This problem is significant in understanding recursive patterns and matrix manipulations, which have applications in computer graphics, image processing, and more.

    Approach

    To solve this problem, we need to think recursively. The naive approach would be to manually fill the array, but this is not scalable for larger values of N. Instead, we can use a recursive function to handle the filling process.

    Naive Solution

    The naive solution involves iterating through the array and filling it manually. This approach is not optimal because it does not leverage the recursive nature of the problem and can be cumbersome for larger arrays.

    Optimized Solution

    The optimized solution involves using a recursive function to fill the array. The function will take the current bounds of the sub-array it needs to fill and the starting number. It will then divide the sub-array into four quadrants and recursively fill each quadrant in the specified order.

    Algorithm

    Here is a step-by-step breakdown of the algorithm:

    1. Initialize a 2D array of size N x N.
    2. Create a recursive function that takes the current bounds of the sub-array and the starting number.
    3. Base Case: If the sub-array size is 1x1, fill it with the starting number.
    4. Recursive Case: Divide the sub-array into four quadrants and recursively fill each quadrant in the order: upper-left, upper-right, lower-left, lower-right.

    Code Implementation

    // Function to fill the matrix in Z pattern
    function fillZPattern(matrix, startRow, startCol, size, startNum) {
        // Base case: if the size is 1, fill the single cell
        if (size === 1) {
            matrix[startRow][startCol] = startNum;
            return startNum + 1;
        }
    
        // Calculate the size of the sub-quadrants
        const halfSize = size / 2;
    
        // Recursively fill the four quadrants
        startNum = fillZPattern(matrix, startRow, startCol, halfSize, startNum); // Upper-left
        startNum = fillZPattern(matrix, startRow, startCol + halfSize, halfSize, startNum); // Upper-right
        startNum = fillZPattern(matrix, startRow + halfSize, startCol, halfSize, startNum); // Lower-left
        startNum = fillZPattern(matrix, startRow + halfSize, startCol + halfSize, halfSize, startNum); // Lower-right
    
        return startNum;
    }
    
    // Main function to generate the Z pattern matrix
    function generateZPattern(N) {
        // Initialize an N x N matrix
        const matrix = Array.from({ length: N }, () => Array(N).fill(0));
    
        // Start filling the matrix from the top-left corner
        fillZPattern(matrix, 0, 0, N, 1);
    
        return matrix;
    }
    
    // Example usage:
    console.log(generateZPattern(2));
    console.log(generateZPattern(4));
    

    Complexity Analysis

    The time complexity of this approach is O(N^2) because we are filling each cell of the N x N matrix exactly once. The space complexity is O(N^2) as well, due to the storage required for the matrix.

    Edge Cases

    Since N is always a power of 2, we do not need to handle cases where N is not a power of 2. However, we should consider the smallest possible value of N (which is 1) and ensure our algorithm handles it correctly.

    Testing

    To test the solution comprehensively, we should include a variety of test cases:

    • Smallest possible value of N (N = 1).
    • Small values of N (N = 2, N = 4).
    • Larger values of N (N = 8, N = 16).

    We can use console logs or a testing framework like Jest to automate the testing process.

    Thinking and Problem-Solving Tips

    When approaching such problems, it is essential to break down the problem into smaller sub-problems and think recursively. Visualizing the problem with diagrams can also help in understanding the recursive pattern. Practicing similar problems and studying recursive algorithms can improve problem-solving skills.

    Conclusion

    In this blog post, we discussed how to solve the Z Pattern problem using a recursive approach. We covered the problem definition, approach, algorithm, code implementation, complexity analysis, edge cases, and testing. Understanding and solving such problems is crucial for developing strong problem-solving skills and a deep understanding of algorithms.

    Additional Resources

    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free