2차원 배열이란?
2차원 배열은 행과 열 두 차원을 가진 배열을 말함,
다른 말로 표현하자면 2차원 배열은 1차원 배열을 요소로 가지는 배열이라고 할 수 있음
2차원 배열의 특징
- 행과 열을 통한 요소 접근
- 2차원 배열의 각 요소는 [행][열] 2개의 인덱스를 통해 접근
- 행과 열 모두 0부터 시작하는 인덱스를 가짐
- 두 개의 인덱스를 통해 원하는 위치의 데이터에 즉시 접근할 수 있음
- 고정된 행과 열 크기
- 2차원 배열은 생성 시점에 행과 열의 크기를 모두 지정해야 함
- 한번 생성된 2차원 배열의 크기는 변경할 수 없음
- 크기를 변경하려면 새로운 2차원 배열을 만들어야 함
- 연속된 메모리 공간
- 2차원 배열도 1차원 배열과 같이 메모리에 연속적으로 저장됨
- 행 우선(row-major) 또는 열 우선(column-major) 방식으로 저장될 수 있음
- Java의 경우 행 우선 방식을 사용
- 이는 첫 번째 행의 모든 요소를 먼저 저장한 뒤, 두 번째 행의 모든 요소를 저장하는 식으로 이어진다는 뜻
행렬 순회 방법
- 기본 순회 : 행 우선, 열 우선, 지그재그

행 우선 순회
2차원 배열을 행(가로) 기준으로 왼쪽에서 오른쪽으로 순회한 뒤, 다음 행으로 이동하여 반복하는 방식
▼ 단계별 시각화
- 원본 행렬:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
1단계: 첫 번째 행 순회
[1, 2, 3] 순서: 1 → 2 → 3
[4, 5, 6]
[7, 8, 9]
2단계: 두 번째 행 순회
[1, 2, 3]
[4, 5, 6] 순서: 4 → 5 → 6
[7, 8, 9]
3단계: 세 번째 행 순회
[1, 2, 3]
[4, 5, 6]
[7, 8, 9] 순서: 7 → 8 → 9
최종 순회 순서: 1→2→3→4→5→6→7→8→9
▼ 동작 원리
1. 행 반복문(바깥쪽)과 열 반복문(안쪽) 설정
1-1. 행 인덱스 i는 0부터 행의 크기-1까지 반복
1-2. 열 인덱스 j는 0부터 열의 크기-1까지 반복
2. 현재 위치(i,j)의 원소에 접근
2-1. arr[i][j] 형태로 각 원소에 접근
3. 다음 행으로 이동하여 반복
3-1. 현재 행의 순회가 끝나면 다음 행으로 이동
▼ 구현 코드
public class MatrixTraversal {
public static void rowMajorOrder(int[][] arr) {
int rows = arr.length;
int cols = arr[0].length;
// 1. 행 반복문(바깥쪽)과 열 반복문(안쪽) 설정
// 1-1. 행 인덱스 i는 0부터 행의 크기-1까지 반복
for (int i = 0; i < rows; i++) {
// 1-2. 열 인덱스 j는 0부터 열의 크기-1까지 반복
for (int j = 0; j < cols; j++) {
// 2. 현재 위치(i,j)의 원소에 접근
// 2-1. arr[i][j] 형태로 각 원소에 접근
System.out.print(arr[i][j] + " ");
}
// 3. 다음 행으로 이동하여 반복
// 3-1. 현재 행의 순회가 끝나면 다음 행으로 이동
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("행 우선 순회 결과:");
rowMajorOrder(matrix);
}
}
▶ 시간 복잡도 : O(NxM)
열 우선 순회
2차원 배열을 열(세로) 기준으로 위에서 아래로 순회한 뒤, 다음 열로 이동하여 반복하는 방식
▼ 단계별 시각화
- 원본 행렬:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
1단계: 첫 번째 열 순회
[1, 2, 3] 순서: 1
[4, 5, 6] ↓
[7, 8, 9] 4
↓
7
2단계: 두 번째 열 순회
[1, 2, 3] 순서: 2
[4, 5, 6] ↓
[7, 8, 9] 5
↓
8
3단계: 세 번째 열 순회
[1, 2, 3] 순서: 3
[4, 5, 6] ↓
[7, 8, 9] 6
↓
9
최종 순회 순서: 1→4→7→2→5→8→3→6→9
▼ 동작 원리
1. 열 반복문(바깥쪽)과 행 반복문(안쪽) 설정
1-1. 열 인덱스 j는 0부터 열의 크기-1까지 반복
1-2. 행 인덱스 i는 0부터 행의 크기-1까지 반복
2. 현재 위치(i,j)의 원소에 접근
2-1. arr[i][j] 형태로 각 원소에 접근
2-2. 각 열을 위에서 아래로 순회
3. 다음 열로 이동하여 반복
3-1. 현재 열의 순회가 끝나면 다음 열로 이동
▼ 구현 코드
public class MatrixTraversal {
public static void columnMajorOrder(int[][] arr) {
int rows = arr.length;
int cols = arr[0].length;
// 1. 열 반복문(바깥쪽)과 행 반복문(안쪽) 설정
// 1-1. 열 인덱스 j는 0부터 열의 크기-1까지 반복
for (int j = 0; j < cols; j++) {
// 1-2. 행 인덱스 i는 0부터 행의 크기-1까지 반복
for (int i = 0; i < rows; i++) {
// 2. 현재 위치(i,j)의 원소에 접근
// 2-1. arr[i][j] 형태로 각 원소에 접근
// 2-2. 각 열을 위에서 아래로 순회
System.out.print(arr[i][j] + " ");
}
// 3. 다음 열로 이동하여 반복
// 3-1. 현재 열의 순회가 끝나면 다음 열로 이동
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("열 우선 순회 결과:");
columnMajorOrder(matrix);
}
}
▶ 시간 복잡도 : O(NxM)
지그재그 순회
▼ 단계별 시각화
- 원본 배열:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
1단계: 첫 번째 행 순회 (왼쪽 → 오른쪽)
[1 → 2 → 3] (순회하는 행)
[4, 5, 6]
[7, 8, 9]
2단계: 두 번째 행 순회 (오른쪽 → 왼쪽)
[1, 2, 3]
[4 ← 5 ← 6] (순회하는 행)
[7, 8, 9]
3단계: 세 번째 행 순회 (왼쪽 → 오른쪽)
[1, 2, 3]
[4, 5, 6]
[7 → 8 → 9] (순회하는 행)
최종 순회 순서: 1→2→3→6→5→4→7→8→9
▼ 동작 원리
접근방법:
- 2차원 배열을 행 단위로 순회하되, 행의 인덱스에 따라 순회 방향을 달리함
- 행 인덱스의 짝/홀수 여부를 확인하여 순회 방향 결정
세부구현:
1. 행 인덱스에 따른 순회 방향 결정
1-1. 행 인덱스(i)가 짝수인 경우: 왼쪽에서 오른쪽으로 순회
1-2. 행 인덱스(i)가 홀수인 경우: 오른쪽에서 왼쪽으로 순회
2. 각 행의 원소 순회
2-1. 짝수 행: j를 0부터 열의 크기-1까지 증가
2-2. 홀수 행: j를 열의 크기-1부터 0까지 감소
3. 다음 행으로 이동하여 반복
3-1. 현재 행의 순회가 끝나면 다음 행으로 이동
▼ 구현 코드
public class MatrixTraversal {
public static void zigzagOrder(int[][] arr) {
int rows = arr.length;
int cols = arr[0].length;
// 1. 행 인덱스에 따른 순회 방향 결정
for (int i = 0; i < rows; i++) {
// 1-1. 행 인덱스(i)가 짝수인 경우: 왼쪽에서 오른쪽으로 순회
if (i % 2 == 0) {
// 2-1. 짝수 행: j를 0부터 열의 크기-1까지 증가
for (int j = 0; j < cols; j++) {
System.out.print(arr[i][j] + " ");
}
}
// 1-2. 행 인덱스(i)가 홀수인 경우: 오른쪽에서 왼쪽으로 순회
else {
// 2-2. 홀수 행: j를 열의 크기-1부터 0까지 감소
for (int j = cols - 1; j >= 0; j--) {
System.out.print(arr[i][j] + " ");
}
}
// 3. 다음 행으로 이동하여 반복
// 3-1. 현재 행의 순회가 끝나면 다음 행으로 이동
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("지그재그 순회 결과:");
zigzagOrder(matrix);
}
}
▶ 시간 복잡도 :O(NxM)
델타 탐색
2차원 배열에서 현재 위치를 기준으로 상하좌우나 대각선 등 특정 방향의 인접한 요소를 탐색하는 방법

핵심 아이디어
- 델타(Δ) 배열 정의
- 상하좌우 혹은 대각선 등, 우리가 이동하고 싶은 방향을 정의하고, 각 방향으로 이동할 때 행(row), 열(col)이 어떻게 변하는지를 수치로 표현
- 현재 위치에서 주변 탐색
- 현재 위치 (row, col) 기준으로 델타 배열을 이용해 주변 칸들의 좌표를 계산하고, 배열 범위 내에 있는지 확인한 뒤, 유효한 위치라면 해당 칸의 정보를 확인하거나 특정 작업(예: 방문 표시, 값 갱신 등)을 수행
예시를 통한 이해
아래와 같은 5x5 격자가 있다고 가정하겠습니다:
0 1 2 3 4
0[1] [2] [3] [4] [5]
1[6] [7] [8] [9] [10]
2[11][12][13][14][15]
3[16][17][18][19][20]
4[21][22][23][24][25]
현재 위치: (2,3) → 값 14가 있는 칸
상하좌우를 통해 확인할 칸:
- 상: (1,3) 값 9
- 하: (3,3) 값 19
- 좌: (2,2) 값 13
- 우: (2,4) 값 15
1. 델타 배열 정의하기
- 2차원 격자에서 상하좌우 인접 칸을 확인하고 싶다면, 이동 방향은 아래와 같습니다.
- 상(Up): 행 -1, 열 0
- 하(Down): 행 +1, 열 0
- 좌(Left): 행 0, 열 -1
- 우(Right): 행 0, 열 +1
- 이를 배열 형태로 표현해보면 다음과 같습니다.
dx = [0, 0, -1, 1] // 열 변화량: 상(0), 하(0), 좌(-1), 우(1)
dy = [-1, 1, 0, 0] // 행 변화량: 상(-1), 하(1), 좌(0), 우(0)
dx, dy의 인덱스 i에 따라 방향이 결정됩니다.
i=0일 때 상
i=1일 때 하
i=2일 때 좌
i=3일 때 우
2. 현재 위치에서 시작하기
- 현재 탐색하고 싶은 위치를 (row, col)이라 하겠습니다.
3. 델타를 이용한 인접 위치 탐색
- 상하좌우 각각에 대해 새로운 좌표를 계산합니다.
- i=0 (상) 방향일 때:
```
newRow = row + dy[0] = 2 + (-1) = 1
newCol = col + dx[0] = 3 + (0) = 3
```
이때 (1, 3)이 행렬 범위 내인지 확인합니다.
범위 내의 값이라면 필요한 작업을 수행할 수 있습니다.
- i=1 (하) 방향일 때:
```
newRow = 2 + dy[1] = 2 + 1 = 3
newCol = 3 + dx[1] = 3 + 0 = 3
```
새 좌표는 (3,3). 범위 내인지 확인 후 처리합니다.
- i=2 (좌) 방향일 때:
```
newRow = 2 + dy[2] = 2 + 0 = 2
newCol = 3 + dx[2] = 3 + (-1) = 2
```
새 좌표는 (2,2). 범위 내인지 확인 후 처리합니다.
- i=3 (우) 방향일 때:
```
newRow = 2 + dy[3] = 2 + 0 = 2
newCol = 3 + dx[3] = 3 + 1 = 4
```
새 좌표는 (2,4). 범위 내인지 확인 후 처리합니다.
응용 순회 : 나선형(달팽이)
2차원 배열을 바깥쪽에서 안쪽으로 시계방향으로 돌면서 순회하는 방식
▼ 순회 시각화

▼ 동작 원리
접근방법:
- 델타 배열 활용: 우 → 하 → 좌 → 상 순서로 이동 방향을 (dx, dy)로 정의
- 유효성 검사 후 이동: 현재 위치에서 다음 위치로 이동하기 전,
다음 위치가 배열 범위 내에 있고 방문하지 않은 칸인지 검사
- 이동 방향 전환: 다음 위치가 배열 범위 밖이거나 이미 방문한 칸일 경우,
시계 방향으로 방향을 변경한 뒤 다시 이동 시도
세부구현:
1. 방향 설정을 위한 델타 배열 정의
1-1. 우, 하, 좌, 상 순서로 이동 방향 배열 생성
1-2. dx = [1, 0, -1, 0], dy = [0, 1, 0, -1]
2. 현재 위치에서 이동 처리
2-1. 현재 위치 방문 및 방문 처리
2-2. 현재 방향(dir)에 따라 다음 위치(nextRow, nextCol) 계산
2-3. 다음 위치 유효성 확인 (배열 범위 내 & 미방문)
- 유효하다면 다음 위치로 이동
- 유효하지 않다면 방향 전환(dir = (dir + 1) % 4) 후 재시도
3. 모든 원소를 방문할 때까지 2단계를 반복
▼ 구현 코드
public class Solution {
public static void main(String[] args) {
// 예시 행렬 (3x4 행렬)
int[][] matrix = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printSnailOrder(matrix);
}
public static void printSnailOrder(int[][] matrix) {
if (matrix == null || matrix.length == 0) return;
int N = matrix.length; // 행의 크기
int M = matrix[0].length; // 열의 크기
boolean[][] visited = new boolean[N][M];
// 1. 방향 설정을 위한 델타 배열 정의
// 1-1. 우, 하, 좌, 상 순서로 이동 방향 배열 생성
// 1-2. dx = [1, 0, -1, 0], dy = [0, 1, 0, -1]
int[] dy = {0, 1, 0, -1}; // 행 이동
int[] dx = {1, 0, -1, 0}; // 열 이동
int x = 0, y = 0; // 현재 위치
int dir = 0; // 현재 방향 (0: 우, 1: 하, 2: 좌, 3: 상)
// 3. 모든 원소를 방문할 때까지 2단계를 반복
for (int i = 0; i < N * M; i++) {
// 2. 현재 위치에서 이동 처리
// 2-1. 현재 위치 방문 및 방문 처리
System.out.print(matrix[y][x] + " ");
visited[y][x] = true;
// 2-2. 현재 방향(dir)에 따라 다음 위치(nextRow, nextCol) 계산
int nextX = x + dx[dir];
int nextY = y + dy[dir];
// 2-3. 다음 위치 유효성 확인 (배열 범위 내 & 미방문)
if (nextX < 0 || nextX >= M || nextY < 0 || nextY >= N || visited[nextY][nextX]) {
// 유효하지 않다면 방향 전환(dir = (dir + 1) % 4) 후 재시도
dir = (dir + 1) % 4;
nextX = x + dx[dir];
nextY = y + dy[dir];
}
// 유효하다면 다음 위치로 이동
x = nextX;
y = nextY;
}
}
}
▶ 시간 복잡도 : O(NxM)
행렬 변환
: 행렬 변환은 데이터를 재구성하거나 변형하여 문제를 해결하는 데 사용됨
예를 들어, 겡미 개발에서는 3D 객체의 회전과 이동, 이미지 처리 시에는 사진의 회전과 뒤집기를 구현할 때 활용
전치 행렬
행렬의 행과 열을 서로 바꾼 행렬을 의미, 즉 원본 행렬의 (i,j) 위치의 원소가 전치 행렬에서는 (j,i) 위치로 이동 대각선을 기준으로 대칭되는 형태로 변환

3x2 행렬의 전치 예시:
원본(3x2): 전치(2x3):
1 2 1 3 5
3 4 → 2 4 6
5 6
▼ 구현 코드
public static int[][] transpose(int[][] matrix) {
int n = matrix.length; // 행의 개수
int m = matrix[0].length; // 열의 개수
// 행과 열의 크기를 바꿔서 새로운 배열 생성
int[][] result = new int[m][n];
// 모든 원소를 순회하며 위치 변환
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
result[j][i] = matrix[i][j];
}
}
return result;
}
▶︎ 시간복잡도 : O(NxM)
행렬의 회전
90도, 180도, 270도 회전
: 행렬 회전은 배열의 요소를 특정 규칙에 따라 재배치하는 작업. 주로 이미지 처리, 그래픽 렌더링, 퍼즐게임 등 다양한 응용 분야에서 사용됨
▼ 시각화
▶︎ 시계방향 90도 회전
정사각 행렬(3×3):
원본: 90도 회전:
1 2 3 7 4 1
4 5 6 → 8 5 2
7 8 9 9 6 3
직사각 행렬(2×3):
원본: 90도 회전:
1 2 3 4 1
4 5 6 → 5 2
6 3
▶︎ 시계방향 180도 회전
정사각 행렬(3×3):
원본: 180도 회전:
1 2 3 9 8 7
4 5 6 → 6 5 4
7 8 9 3 2 1
직사각 행렬(2×3):
원본: 180도 회전:
1 2 3 6 5 4
4 5 6 → 3 2 1
▶︎ 시계방향 270도 회전
정사각 행렬(3×3):
원본: 270도 회전:
1 2 3 3 6 9
4 5 6 → 2 5 8
7 8 9 1 4 7
직사각 행렬(2×3):
원본: 270도 회전:
1 2 3 3 6
4 5 6 → 2 5
1 4
▼ 구현 방법
90도 회전
- 행렬 인덱스 재배치: (i,j) → (j, N-1-i)
180도 회전
- 행렬 인덱스 재배치: (i,j) → (N-1-i, M-1-j)
270도 회전
- 행렬 인덱스 재배치: (i,j) → (M-1-j, i)
기억하기 위한 Tip!
- 90도씩 회전할때 i,j의 위치는 계속 바뀝니다.
- 첫번째 인덱스는 최대 인덱스에서 빼서 두번째 자리고 가고
- 두번째 인덱스는 그대로 첫번째 자리로 갑니다.
▼ 구현 코드
// 90도 회전 (직사각형 행렬)
public static int[][] rotate90(int[][] matrix) {
int N = matrix.length; // 행의 개수
int M = matrix[0].length; // 열의 개수
int[][] result = new int[M][N]; // 결과 배열의 크기가 바뀜
for(int i = 0; i < N; i++) {
for(int j = 0; j < M; j++) {
result[j][N-1-i] = matrix[i][j];
}
}
return result;
}
// 180도 회전 (직사각형 행렬)
public static int[][] rotate180(int[][] matrix) {
int N = matrix.length;
int M = matrix[0].length;
int[][] result = new int[N][M]; // 원래 크기 유지
for(int i = 0; i < N; i++) {
for(int j = 0; j < M; j++) {
result[N-1-i][M-1-j] = matrix[i][j];
}
}
return result;
}
// 270도 회전 (직사각형 행렬)
public static int[][] rotate270(int[][] matrix) {
int N = matrix.length;
int M = matrix[0].length;
int[][] result = new int[M][N]; // 결과 배열의 크기가 바뀜
for(int i = 0; i < N; i++) {
for(int j = 0; j < M; j++) {
result[M-1-j][i] = matrix[i][j];
}
}
return result;
}
▶︎ 시간 복잡도 : O(NxM)
'JAVA' 카테고리의 다른 글
| [JAVA] 예외처리 (1) | 2025.10.13 |
|---|---|
| [JAVA Algorithm] 컬렉션 프레임워크 (0) | 2025.10.02 |
| [JAVA Algorithm] 배열 개념 정리 (0) | 2025.10.01 |
| [JAVA] 완전탐색 Vs 그리디 알고리즘 이해하기 (0) | 2025.09.30 |
| [JAVA] 알고리즘 기본 정리 (0) | 2025.09.29 |