Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions number-of-islands/ICE0208.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

number-of-islands/ICE0208.java
import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
    private static final int[][] DIRECTIONS = {
            {0, 1},
            {0, -1},
            {1, 0},
            {-1, 0}
    };

    private static final char WATER = '0';
    private static final char LAND = '1';

    private record Position(int row, int column) {
    }

    /**
     * 격자를 순회하며 아직 방문하지 않은 육지를 발견할 때마다
     * 연결된 하나의 섬을 반복형 DFS로 모두 방문 처리한다.
     *
     * 시간 복잡도: O(m * n)
     * 공간 복잡도: O(m * n)
     */
    public int numIslands(char[][] grid) {
        int islandCount = 0;

        for (int row = 0; row < grid.length; row++) {
            for (int column = 0; column < grid[row].length; column++) {
                if (grid[row][column] != LAND) {
                    continue;
                }

                // 아직 방문하지 않은 육지는 새로운 섬의 시작점이다.
                islandCount++;
                markIslandAsVisited(grid, row, column);
            }
        }

        return islandCount;
    }

    /**
     * 시작 위치와 상하좌우로 연결된 모든 육지를 방문 처리한다.
     * 재귀 호출로 인한 스택 오버플로를 피하기 위해 별도의 스택을 사용한다.
     */
    private static void markIslandAsVisited(
            char[][] grid,
            int startRow,
            int startColumn
    ) {
        Deque<Position> stack = new ArrayDeque<>();
        stack.push(new Position(startRow, startColumn));

        // 스택에 넣는 시점에 방문 처리하여 같은 위치가 중복으로 들어가는 것을 방지한다.
        grid[startRow][startColumn] = WATER;

        while (!stack.isEmpty()) {
            Position current = stack.pop();

            for (int[] direction : DIRECTIONS) {
                int nextRow = current.row() + direction[0];
                int nextColumn = current.column() + direction[1];

                if (!isInBounds(grid, nextRow, nextColumn)) {
                    continue;
                }

                if (grid[nextRow][nextColumn] != LAND) {
                    continue;
                }

                grid[nextRow][nextColumn] = WATER;
                stack.push(new Position(nextRow, nextColumn));
            }
        }
    }

    private static boolean isInBounds(
            char[][] grid,
            int row,
            int column
    ) {
        return row >= 0
                && row < grid.length
                && column >= 0
                && column < grid[0].length;
    }
}
  • 패턴: Depth-First Search, Stack/Explicit Stack (as part of DFS)
  • 설명: 그리드를 순회하며 섬(연결된 육지)을 DFS로 방문 처리한다. 재귀 대신 스택을 사용해 DFS를 구현하고, 인접한 육지를 탐색하며 방문 표식을 남긴다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(m * n) O(n * m)
Space O(m * n) O(n * m)

피드백: 두 방향으로의 인접만 확장하는 DFS로 전체 격자를 한 번씩 방문하고, 방문 시 육지를 WATER로 표시해 중복 방문을 막는다.

개선 제안: 현재 구현이 적절해 보입니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
private static final int[][] DIRECTIONS = {
{0, 1},
{0, -1},
{1, 0},
{-1, 0}
};

private static final char WATER = '0';
private static final char LAND = '1';

private record Position(int row, int column) {
}

/**
* 격자를 순회하며 아직 방문하지 않은 육지를 발견할 때마다
* 연결된 하나의 섬을 반복형 DFS로 모두 방문 처리한다.
*
* 시간 복잡도: O(m * n)
* 공간 복잡도: O(m * n)
*/
public int numIslands(char[][] grid) {
int islandCount = 0;

for (int row = 0; row < grid.length; row++) {
for (int column = 0; column < grid[row].length; column++) {
if (grid[row][column] != LAND) {
continue;
}

// 아직 방문하지 않은 육지는 새로운 섬의 시작점이다.
islandCount++;
markIslandAsVisited(grid, row, column);
}
}

return islandCount;
}

/**
* 시작 위치와 상하좌우로 연결된 모든 육지를 방문 처리한다.
* 재귀 호출로 인한 스택 오버플로를 피하기 위해 별도의 스택을 사용한다.
*/
private static void markIslandAsVisited(
char[][] grid,
int startRow,
int startColumn
) {
Deque<Position> stack = new ArrayDeque<>();
stack.push(new Position(startRow, startColumn));

// 스택에 넣는 시점에 방문 처리하여 같은 위치가 중복으로 들어가는 것을 방지한다.
grid[startRow][startColumn] = WATER;

while (!stack.isEmpty()) {
Position current = stack.pop();

for (int[] direction : DIRECTIONS) {
int nextRow = current.row() + direction[0];
int nextColumn = current.column() + direction[1];

if (!isInBounds(grid, nextRow, nextColumn)) {
continue;
}

if (grid[nextRow][nextColumn] != LAND) {
continue;
}

grid[nextRow][nextColumn] = WATER;
stack.push(new Position(nextRow, nextColumn));
}
}
}

private static boolean isInBounds(
char[][] grid,
int row,
int column
) {
return row >= 0
&& row < grid.length
&& column >= 0
&& column < grid[0].length;
}
}
54 changes: 54 additions & 0 deletions set-matrix-zeroes/ICE0208.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

set-matrix-zeroes/ICE0208.java
import java.util.Arrays;

class Solution {
    public void setZeroes(int[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;

        // 첫 번째 행과 열은 marker로 사용하므로, 원래 0이 있었는지 별도로 저장한다.
        boolean firstRowHasZero = false;
        for (int col = 0; col < cols; ++col) {
            if (matrix[0][col] == 0) {
                firstRowHasZero = true;
                break;
            }
        }

        boolean firstColumnHasZero = false;
        for (int row = 0; row < rows; ++row) {
            if (matrix[row][0] == 0) {
                firstColumnHasZero = true;
                break;
            }
        }

        // 첫 번째 행과 열을 각 행과 열의 zero marker로 사용한다.
        for (int row = 1; row < rows; ++row) {
            for (int col = 1; col < cols; ++col) {
                if (matrix[row][col] == 0) {
                    matrix[row][0] = 0;
                    matrix[0][col] = 0;
                }
            }
        }

        // marker를 기준으로 내부 원소를 0으로 변경한다.
        for (int row = 1; row < rows; ++row) {
            for (int col = 1; col < cols; ++col) {
                if (matrix[row][0] == 0 || matrix[0][col] == 0) {
                    matrix[row][col] = 0;
                }
            }
        }

        if (firstRowHasZero) {
            Arrays.fill(matrix[0], 0);
        }

        if (firstColumnHasZero) {
            for (int row = 0; row < rows; ++row) {
                matrix[row][0] = 0;
            }
        }
    }
}
  • 패턴: Greedy, Dynamic Programming, Divide and Conquer, Two Pointers, Hash Map / Hash Set, Binary Search, Monotonic Stack, Heap / Priority Queue, DFS, BFS, Backtracking, Union Find, Trie, Bit Manipulation, Sliding Window
  • 설명: 이 코드는 2D 매트릭스에서 특정 행/열을 마커로 이용해 제로를 확산시키는 문제로, 공간을 추가로 사용하지 않고 기존 행/열을 마커로 재활용하는 방식이 핵심이다. 제한된 공간에서 상태를 저장하고 조건에 따라 원소를 업데이트하는 아이디어는 다수의 공간 최적화 패턴과 연관된다.

📊 시간/공간 복잡도 분석

복잡도
Time O(m * n)
Space O(1)

피드백: 입력 행렬에 추가적인 공간을 사용하지 않고, 첫 행/열을 마커로 활용하여 전체 원소를 한 번씩 스캔한다.

개선 제안: 현재 구현이 적절해 보입니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import java.util.Arrays;

class Solution {
public void setZeroes(int[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;

// 첫 번째 행과 열은 marker로 사용하므로, 원래 0이 있었는지 별도로 저장한다.
boolean firstRowHasZero = false;
for (int col = 0; col < cols; ++col) {
if (matrix[0][col] == 0) {
firstRowHasZero = true;
break;
}
}

boolean firstColumnHasZero = false;
for (int row = 0; row < rows; ++row) {
if (matrix[row][0] == 0) {
firstColumnHasZero = true;
break;
}
}

// 첫 번째 행과 열을 각 행과 열의 zero marker로 사용한다.
for (int row = 1; row < rows; ++row) {
for (int col = 1; col < cols; ++col) {
if (matrix[row][col] == 0) {
matrix[row][0] = 0;
matrix[0][col] = 0;
}
}
}

// marker를 기준으로 내부 원소를 0으로 변경한다.
for (int row = 1; row < rows; ++row) {
for (int col = 1; col < cols; ++col) {
if (matrix[row][0] == 0 || matrix[0][col] == 0) {
matrix[row][col] = 0;
}
}
}

if (firstRowHasZero) {
Arrays.fill(matrix[0], 0);
}

if (firstColumnHasZero) {
for (int row = 0; row < rows; ++row) {
matrix[row][0] = 0;
}
}
}
}
48 changes: 48 additions & 0 deletions unique-paths/ICE0208.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

unique-paths/ICE0208.java
import java.util.Arrays;

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        // 첫 번째 행과 열은 한 방향으로만 이동해 도달할 수 있으므로 1로 초기화합니다.
        initializeBaseCases(dp);

        for (int row = 1; row < m; ++row) {
            for (int col = 1; col < n; ++col) {
                dp[row][col] = dp[row - 1][col] + dp[row][col - 1];
            }
        }

        return dp[m - 1][n - 1];
    }

    /**
        * DP 배열의 첫 번째 행과 첫 번째 열을 1로 초기화합니다.
        * @param dp 초기화할 DP 배열
        */
    private static void initializeBaseCases(int[][] dp) {
        int rows = dp.length;
        int cols = dp[0].length;

        for (int row = 0; row < rows; row++) {
            dp[row][0] = 1;
        }
        for (int col = 0; col < cols; col++) {
            dp[0][col] = 1;
        }
    }
}

class Solution2 {
    public int uniquePaths(int m, int n) {
        int[] dp = new int[n];
        Arrays.fill(dp, 1);

        for (int row = 1; row < m; ++row) {
            for (int col = 1; col < n; ++col) {
                dp[col] += dp[col - 1];
            }
        }

        return dp[n - 1];
    }
}
  • 패턴: Dynamic Programming, Greedy
  • 설명: 코드는 두 가지 방식으로 경로의 수를 DP로 계산합니다. 2D 배열과 1D 배열 모두에서 현재 위치의 경로 수를 위/좌의 합으로 갱신하므로 DP 패턴에 속합니다. 주어진 문제의 해를 작은 부분 문제로 나누어 해결하는 특징이 명확합니다.

📊 시간/공간 복잡도 분석

ℹ️ 이 파일에는 2가지 풀이가 포함되어 있어 각각 분석합니다.

풀이 1: Solution.uniquePaths — Time: O(m*n) / Space: O(m*n)
복잡도
Time O(m*n)
Space O(m*n)

피드백: 2차원 DP 배열을 사용해 이웃 셀의 합으로 현재 셀의 경로 수를 계산합니다.

개선 제안: 현재 구현은 명확하지만 메모리 사용을 줄이려면 공간 최적화 버전(1차원 DP)을 사용할 수 있습니다.

풀이 2: Solution2.uniquePaths — Time: O(m*n) / Space: O(n)
복잡도
Time O(m*n)
Space O(n)

피드백: 1차원 배열 dp를 통해 메모리 사용량을 줄였고, 각 행마다 이전 열의 값을 이용해 현재 값을 업데이트한다.

개선 제안: 추가적으로 커스텀 최적화나 초기화 로직을 간소화해도 좋다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

unique-paths/ICE0208.java
import java.util.Arrays;

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        // 첫 번째 행과 열은 한 방향으로만 이동해 도달할 수 있으므로 1로 초기화합니다.
        initializeBaseCases(dp);

        for (int row = 1; row < m; ++row) {
            for (int col = 1; col < n; ++col) {
                dp[row][col] = dp[row - 1][col] + dp[row][col - 1];
            }
        }

        return dp[m - 1][n - 1];
    }

    /**
        * DP 배열의 첫 번째 행과 첫 번째 열을 1로 초기화합니다.
        * @param dp 초기화할 DP 배열
        */
    private static void initializeBaseCases(int[][] dp) {
        int rows = dp.length;
        int cols = dp[0].length;

        for (int row = 0; row < rows; row++) {
            dp[row][0] = 1;
        }
        for (int col = 0; col < cols; col++) {
            dp[0][col] = 1;
        }
    }
}

class Solution2 {
    public int uniquePaths(int m, int n) {
        int[] dp = new int[n];
        Arrays.fill(dp, 1);

        for (int row = 1; row < m; ++row) {
            for (int col = 1; col < n; ++col) {
                dp[col] += dp[col - 1];
            }
        }

        return dp[n - 1];
    }
}
  • 패턴: Dynamic Programming, Monotonic Stack
  • 설명: 두 가지 풀이 모두 DP를 이용한 경로 수 계산 패턴으로 각 셀의 경로 수를 합산합니다. 또한 두 번째 풀이에서 1차원 DP 배열로 공간 최적화하는 일반적 DP 패턴이 보입니다.

📊 시간/공간 복잡도 분석

복잡도
Time O(m*n)
Space O(m*n)

피드백: 2D 배열 버전은 직관적이고 이해하기 쉽고, 1D 배열 버전은 공간을 절약합니다.

개선 제안: 현재 구현이 적절해 보입니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import java.util.Arrays;

class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
// 첫 번째 행과 열은 한 방향으로만 이동해 도달할 수 있으므로 1로 초기화합니다.
initializeBaseCases(dp);

for (int row = 1; row < m; ++row) {
for (int col = 1; col < n; ++col) {
dp[row][col] = dp[row - 1][col] + dp[row][col - 1];
}
}

return dp[m - 1][n - 1];
}

/**
* DP 배열의 첫 번째 행과 첫 번째 열을 1로 초기화합니다.
* @param dp 초기화할 DP 배열
*/
private static void initializeBaseCases(int[][] dp) {
int rows = dp.length;
int cols = dp[0].length;

for (int row = 0; row < rows; row++) {
dp[row][0] = 1;
}
for (int col = 0; col < cols; col++) {
dp[0][col] = 1;
}
}
}

class Solution2 {
public int uniquePaths(int m, int n) {
int[] dp = new int[n];
Arrays.fill(dp, 1);

for (int row = 1; row < m; ++row) {
for (int col = 1; col < n; ++col) {
dp[col] += dp[col - 1];
}
}

return dp[n - 1];
}
}
Loading