[Java Algorithm] 2차원 배열

2025. 10. 2. 16:55·JAVA

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
'JAVA' 카테고리의 다른 글
  • [JAVA] 예외처리
  • [JAVA Algorithm] 컬렉션 프레임워크
  • [JAVA Algorithm] 배열 개념 정리
  • [JAVA] 완전탐색 Vs 그리디 알고리즘 이해하기
stark77
stark77
하마의 IT 자기개발 이모저모, 백엔드 개발자로 거듭나기
  • stark77
    하마의 개발자 성장일기
    stark77
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 컴퓨터구조와 운영체제
        • 컴퓨터구조
        • 운영체제
      • SQL 기초
      • Spring
        • 백엔드 기초
        • Spring 실습
      • JAVA
        • Java 실습
      • HTML&CSS
        • HTML&CSS 실습
      • Git&GitHub
        • Git&GitHub 실습
      • 내배캠 끄적끄적
        • Today I Learned
      • 유용한 툴 및 사이트 정리
      • 취미
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    WebSocket
    String.format
    jsp
    BEAN
    객체지향프로그래밍
    객체지향
    Github
    Stomp
    RestTemplate
    Til
    git
    JPA
    MVC
    java
    경합조건과 교착상태
    thymleaf
    Java 문법기초
    Spring
    for문
    웹소켓
    HTML&CSS
    백엔드 기초
    다형성
    Spriingboot
    algorithm
    네트워크 기초
    SpringSecurity
    백엔드 기초다지기
    프로세스와 쓰레드
    실시간 데이터 처리
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
stark77
[Java Algorithm] 2차원 배열
상단으로

티스토리툴바