diff --git a/longest-substring-without-repeating-characters/ICE0208.java b/longest-substring-without-repeating-characters/ICE0208.java new file mode 100644 index 0000000000..587b243b21 --- /dev/null +++ b/longest-substring-without-repeating-characters/ICE0208.java @@ -0,0 +1,25 @@ +import java.util.Arrays; + +class Solution { + public int lengthOfLongestSubstring(String s) { + int [] lastSeen = new int[128]; + Arrays.fill(lastSeen, -1); + + int left = 0; + int maxLength = 0; + + for (int right = 0; right < s.length(); right++) { + char current = s.charAt(right); + + // 현재 문자가 이미 등장했다면 + // 이전 등장 위치 다음으로 left를 이동한다. + // left는 right 보다 작거나 같다는 것이 보장. + left = Math.max(left, lastSeen[current] + 1); + + lastSeen[current] = right; + maxLength = Math.max(maxLength, right - left + 1); + } + + return maxLength; + } +} diff --git a/number-of-islands/ICE0208.java b/number-of-islands/ICE0208.java new file mode 100644 index 0000000000..b383e82992 --- /dev/null +++ b/number-of-islands/ICE0208.java @@ -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 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; + } +} diff --git a/reverse-linked-list/ICE0208.java b/reverse-linked-list/ICE0208.java new file mode 100644 index 0000000000..d2f634db7b --- /dev/null +++ b/reverse-linked-list/ICE0208.java @@ -0,0 +1,17 @@ +class Solution { + public ListNode reverseList(ListNode head) { + ListNode prev = null; + ListNode current = head; + + while (current != null) { + ListNode next = current.next; + + current.next = prev; + + prev = current; + current = next; + } + + return prev; + } +} diff --git a/set-matrix-zeroes/ICE0208.java b/set-matrix-zeroes/ICE0208.java new file mode 100644 index 0000000000..92aa9d1951 --- /dev/null +++ b/set-matrix-zeroes/ICE0208.java @@ -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; + } + } + } +} diff --git a/unique-paths/ICE0208.java b/unique-paths/ICE0208.java new file mode 100644 index 0000000000..5bc6369d13 --- /dev/null +++ b/unique-paths/ICE0208.java @@ -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]; + } +}