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]]
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]]
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
mrows andncolumns. -
mandnare 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:
-
startingRow: The top row index of the current layer. -
endingRow: The bottom row index of the current layer. -
startingCol: The left column index of the current layer. -
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:
-
Initialization:
- Create an empty
vector<int> ansto store our spiral order. - Get the dimensions:
m = matrix.size()(number of rows) andn = matrix[0].size()(number of columns). - Calculate
total_elements = m * nto know when we've collected everything. - Initialize
count = 0to track how many elements we've added toans. - Set our boundary pointers:
-
startingRow = 0 -
endingRow = m - 1 -
startingCol = 0 -
endingCol = n - 1
-
- Create an empty
The Spiral Loop: We'll use a
whileloop that continues as long ascount < total_elements. This ensures we process every element exactly once.Inside the Loop (One Full Layer Traversal): Each iteration of the
whileloop 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`.
- Return: Once the
whileloop finishes (meaningcountequalstotal_elements), returnans.
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;
}
};
⏱️ Time & Space Complexity Analysis
-
Time Complexity: O(m * n)
- We visit each element in the
m x nmatrix exactly once. Each visit involves a constant number of operations (pushing toans, incrementingcount). Therefore, the time complexity scales linearly with the total number of elements.
- We visit each element in the
-
Space Complexity: O(m * n)
- We are storing all
m * nelements in ouransvector. 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 toreturnall elements, the O(m * n) space for the result is standard.
- We are storing all
🎯 Key Takeaways
- 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.
- Shrink and Conquer: The idea of "peeling off" layers by shrinking boundaries is key to solving many spiral-like problems.
- Edge Case Checks: Always remember to include
count < totalchecks before each traversal phase. This prevents out-of-bounds errors and ensures correctness, especially for matrices withm=1orn=1or 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)