DEV Community

Vansh Aggarwal
Vansh Aggarwal

Posted on

LeetCode Solution: 54. Spiral Matrix

Unraveling the Matrix: A Beginner's Guide to LeetCode 54 (Spiral Matrix)

Hey LeetCoders and aspiring competitive programmers! 👋 Vansh here, ready to tackle another classic problem that's super fun once you get the hang of it. Today, we're diving into LeetCode problem 54: Spiral Matrix. This one often pops up in interviews and is a fantastic way to sharpen your array traversal skills.

Don't let the "matrix" part scare you! We'll break it down step-by-step and make it feel like a breeze.


🧐 The Problem: Spinning Through a Grid

Imagine you have a grid of numbers (a matrix). Your goal is to "read" all the numbers by starting from the top-left corner and moving inwards in a spiral path. We need to collect these numbers and return them in the order we encounter them.

Example 1: A 3x3 Marvel

Input:

matrix = [[1,2,3],
          [4,5,6],
          [7,8,9]]
Enter fullscreen mode Exit fullscreen mode

Output: [1,2,3,6,9,8,7,4,5]

See how it goes 1 -> 2 -> 3 (top row), then 3 -> 6 -> 9 (right column down), then 9 -> 8 -> 7 (bottom row left), then 7 -> 4 (left column up), and finally 5 (the center)? That's the spiral!

Example 2: A 3x4 Journey

Input:

matrix = [[1,2,3,4],
          [5,6,7,8],
          [9,10,11,12]]
Enter fullscreen mode Exit fullscreen mode

Output: [1,2,3,4,8,12,11,10,9,5,6,7]

Notice that even with different dimensions, the spiral logic remains. We go around the outermost layer, then the next inner layer, and so on, until all elements are collected.

Constraints to keep in mind:

  • The matrix will have m rows and n columns.
  • m and n are between 1 and 10 (so, small matrices, which is great for learning!).
  • Numbers in the matrix are between -100 and 100.

💡 The "Aha!" Moment: Shrinking Boundaries

How do we simulate this spiral movement? If you think about it, a spiral essentially "peels off" layers of the matrix. We start with the outermost layer, collect its elements, and then move inwards to the next layer.

The core intuition is to maintain four pointers that define the current boundaries of our "unvisited" matrix:

  1. startingRow: The top row index of the current layer.
  2. endingRow: The bottom row index of the current layer.
  3. startingCol: The left column index of the current layer.
  4. endingCol: The right column index of the current layer.

After we traverse a side (e.g., the top row), we "shrink" that boundary inward (e.g., increment startingRow). We repeat this process, spiraling inwards, until our boundaries cross or meet, meaning all elements have been visited.


🚀 Our Step-by-Step Approach

Let's break down the logic with our four boundary pointers:

  1. Initialization:

    • Create an empty vector<int> ans to store our spiral order.
    • Get the dimensions: m = matrix.size() (number of rows) and n = matrix[0].size() (number of columns).
    • Calculate total_elements = m * n to know when we've collected everything.
    • Initialize count = 0 to track how many elements we've added to ans.
    • Set our boundary pointers:
      • startingRow = 0
      • endingRow = m - 1
      • startingCol = 0
      • endingCol = n - 1
  2. The Spiral Loop: We'll use a while loop that continues as long as count < total_elements. This ensures we process every element exactly once.

  3. Inside the Loop (One Full Layer Traversal): Each iteration of the while loop processes one "layer" of the spiral.

*   **Phase 1: Traverse Top Row (Left to Right)**
    *   Loop `index` from `startingCol` to `endingCol`.
    *   Add `matrix[startingRow][index]` to `ans`. Increment `count`.
    *   After the loop, increment `startingRow` because this row is now "peeled off".
    *   **Crucial check:** Always check `if (count < total_elements)` *before* starting the next phase, as the matrix might be exhausted (especially for single-row/column cases).

*   **Phase 2: Traverse Right Column (Top to Bottom)**
    *   Loop `index` from `startingRow` to `endingRow`.
    *   Add `matrix[index][endingCol]` to `ans`. Increment `count`.
    *   After the loop, decrement `endingCol`.

*   **Phase 3: Traverse Bottom Row (Right to Left)**
    *   Loop `index` from `endingCol` down to `startingCol`.
    *   Add `matrix[endingRow][index]` to `ans`. Increment `count`.
    *   After the loop, decrement `endingRow`.

*   **Phase 4: Traverse Left Column (Bottom to Top)**
    *   Loop `index` from `endingRow` down to `startingRow`.
    *   Add `matrix[index][startingCol]` to `ans`. Increment `count`.
    *   After the loop, increment `startingCol`.
Enter fullscreen mode Exit fullscreen mode
  1. Return: Once the while loop finishes (meaning count equals total_elements), return ans.

The count < total_elements check within each for loop is absolutely essential. Consider a 1x5 matrix [[1,2,3,4,5]]. After traversing the top row, startingRow increments, count becomes 5, and total_elements is 5. Without the checks, the subsequent loops for right, bottom, and left might run unnecessarily or access out-of-bounds indices.


💻 The Code

Here's the C++ implementation following our approach:

class Solution {
public:
    std::vector<int> spiralOrder(std::vector<std::vector<int>>& matrix) {
        std::vector<int> ans;

        int m = matrix.size();
        if (m == 0) { // Handle empty matrix case
            return ans;
        }
        int n = matrix[0].size();

        int total = m * n; // Total elements in the matrix
        int count = 0;     // Counter for elements added to 'ans'

        // Initialize boundary pointers
        int startingRow = 0;
        int endingRow = m - 1;
        int startingCol = 0;
        int endingCol = n - 1;

        // Loop until all elements are visited
        while (count < total) {

            // Phase 1: Traverse Top Row (Left to Right)
            // Iterate from startingCol to endingCol in the current startingRow
            for (int index = startingCol; count < total && index <= endingCol; index++) {
                ans.push_back(matrix[startingRow][index]);
                count++;
            }
            startingRow++; // Move startingRow inwards

            // Phase 2: Traverse Right Column (Top to Bottom)
            // Iterate from startingRow to endingRow in the current endingCol
            for (int index = startingRow; count < total && index <= endingRow; index++) {
                ans.push_back(matrix[index][endingCol]);
                count++;
            }
            endingCol--; // Move endingCol inwards

            // Phase 3: Traverse Bottom Row (Right to Left)
            // Iterate from endingCol down to startingCol in the current endingRow
            for (int index = endingCol; count < total && index >= startingCol; index--) {
                ans.push_back(matrix[endingRow][index]);
                count++;
            }
            endingRow--; // Move endingRow inwards

            // Phase 4: Traverse Left Column (Bottom to Top)
            // Iterate from endingRow down to startingRow in the current startingCol
            for (int index = endingRow; count < total && index >= startingRow; index--) {
                ans.push_back(matrix[index][startingCol]);
                count++;
            }
            startingCol++; // Move startingCol inwards
        }

        return ans;
    }
};
Enter fullscreen mode Exit fullscreen mode

⏱️ Time & Space Complexity Analysis

  • Time Complexity: O(m * n)

    • We visit each element in the m x n matrix exactly once. Each visit involves a constant number of operations (pushing to ans, incrementing count). Therefore, the time complexity scales linearly with the total number of elements.
  • Space Complexity: O(m * n)

    • We are storing all m * n elements in our ans vector. If we don't count the output array as auxiliary space (which some problems define), then the auxiliary space complexity would be O(1) as we only use a few pointers. However, since the problem asks to return all elements, the O(m * n) space for the result is standard.

🎯 Key Takeaways

  1. Boundary Pointers are Powerful: For matrix traversal problems, especially those involving layers or specific paths, using boundary pointers (top, bottom, left, right) to define your active processing area is a very common and effective technique.
  2. Shrink and Conquer: The idea of "peeling off" layers by shrinking boundaries is key to solving many spiral-like problems.
  3. Edge Case Checks: Always remember to include count < total checks before each traversal phase. This prevents out-of-bounds errors and ensures correctness, especially for matrices with m=1 or n=1 or when the number of elements is exhausted mid-layer.

This problem is a fantastic exercise in careful index management and loop conditions. Master it, and you'll be well-prepared for more complex matrix challenges!

Happy coding! ✨


Authored by Vansh2710 | Published: 2026-03-30 23:41:55

Top comments (0)