본문 바로가기

Algorithms

[LeetCode] 54. Spiral Matrix 풀이 (Java)

728x90
반응형

✔️ 오늘의 리트코드 공부 기록 – Spiral Matrix


 

문제 요약 (Spiral Matrix)

MxN 행렬이 주어질 때,
시계 방향 나선형(spiral order) 으로 모든 원소를 순서대로 반환하는 문제.

 

문제링크: https://leetcode.com/problems/spiral-matrix/


🤔 처음 떠올린 아이디어

 

처음 문제를 봤을 때는 이런 생각이 들었다.

 “방향 배열(dr, dc) 만들어서 계속 이동하다가 막히면 방향 바꾸면 되지 않을까?”

 

그래서

  • visited[][] 배열을 두고
  • 다음 칸이 범위를 벗어나거나, 이미 방문했다면 방향을 전환

이 방식이 가장 직관적으로 떠올랐다.

 

하지만 코드를 생각해보니

👉 visited 배열이 추가로 필요해서 공간 효율이 아쉽다는 느낌이 들었다.


 

💡 핵심 깨달음: 이 문제는 “이동”이 아니라 “경계” 문제다

이 문제를 계속 보다 보니 이런 구조라는 걸 깨달았다.

 

  • 바깥 테두리를 한 바퀴 돈다
  • 그 다음, 안쪽에 남은 작은 사각형을 다시 돈다
  • 이 과정을 반복한다

 

즉, 이 문제는

 한 칸씩 이동하는 문제가 아니라, 행렬을 레이어(layer) 단위로 깎아가는 문제

 


🧠 Boundary(경계) 방식으로 사고 전환

 

그래서 나는 행렬을 이렇게 바라보기로 했다.

 

rStart -------------- cEnd

|                                      |

|                                      |

|                                      |

cStart ------------- rEnd

 

필요한 것은 딱 4개의 경계 변수다.

 

int rStart = 0;

int rEnd = matrix.length - 1;

int cStart = 0;

int cEnd = matrix[0].length - 1;


 

🔄 Boundary 방식의 고정된 순서

Boundary 방식에서는 순서가 곧 공식/로직이다.

  1. top row (왼 → 오)
  2. right column (위 → 아래)
  3. bottom row (오 → 왼)
  4. left column (아래 → 위)

한 바퀴를 돌면, 경계를 하나씩 줄인다.

 

⚠️ 반드시 필요한 조건 체크

아래 두 단계에서는 조건 체크가 필수다.

  • bottom row
  • left column

왜냐하면,
1×N, N×1 행렬처럼 중앙에 한 줄만 남은 경우 중복 방문이 발생할 수 있기 때문이다.

  • if (rStart <= rEnd) { ... }
  • if (cStart <= cEnd) { ... } 

 

코드 (Java) | Spiral Matrix 풀이

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> result = new ArrayList<>();

        int rStart = 0;
        int rEnd = matrix.length - 1;
        int cStart = 0;
        int cEnd = matrix[0].length - 1;

        while (rStart <= rEnd && cStart <= cEnd) {

            // 1. top
            for (int c = cStart; c <= cEnd; c++) {
                result.add(matrix[rStart][c]);
            }
            rStart++;

            // 2. right
            for (int r = rStart; r <= rEnd; r++) {
                result.add(matrix[r][cEnd]);
            }
            cEnd--;

            // 3. bottom
            if (rStart <= rEnd) {
                for (int c = cEnd; c >= cStart; c--) {
                    result.add(matrix[rEnd][c]);
                }
                rEnd--;
            }

            // 4. left
            if (cStart <= cEnd) {
                for (int r = rEnd; r >= rStart; r--) {
                    result.add(matrix[r][cStart]);
                }
                cStart++;
            }
        }

        return result;
    }
}

 

 

📈 Time / Space Complexity

  •  시간 복잡도: 모든 원소 1번 방문 → O(M×N)
  • 공간 복잡도: 추가 배열 없음 → O(1)

❌ Boundary 방식 실수 TOP 5

 

1️⃣ rEnd / cEnd를 length로 두는 실수

  • int rEnd = matrix.length; ❌ → 반드시 length - 1

 

2️⃣ bottom / left에서 조건 체크 누락

  • 1×N, N×1 행렬에서 중복 추가 발생

 

3️⃣ 경계를 먼저 줄이는 실수

  • 줄이고 → 읽기 ❌
  • 읽고 → 줄이기 ⭕

 

4️⃣ while 조건을 OR로 쓰는 실수

  • while (rStart <= rEnd || cStart <= cEnd) ❌ → 반드시 AND

 

5️⃣ 순서 변경

  • Boundary 방식은 순서 자체가 로직이다.
  • top → right → bottom → left (고정)

🧩 Boundary 일반화 공식

Spiral Matrix뿐 아니라 행렬 테두리 문제 전반에 그대로 적용 가능한 공식이다.

while (rStart <= rEnd && cStart <= cEnd) {
	top
	right
	bottom (if needed)
	left (if needed)
}

 

📝 이번 문제에서 얻은 깨달음

  • Spiral 문제는 이동 문제가 아니라 경계 문제
  • visited 방식은 직관적이지만, Boundary가 더 깔끔하다
  • Boundary는 “기술”이 아니라 행렬을 바라보는 관점
  • 👉 결국 중요한 건 “어떻게 움직이느냐”가 아니라 “어떻게 깎아나가느냐”였다.

 

728x90
반응형