diff --git a/number-of-islands/smosco.js b/number-of-islands/smosco.js new file mode 100644 index 000000000..70cbdffd0 --- /dev/null +++ b/number-of-islands/smosco.js @@ -0,0 +1,75 @@ +/** + * Number of Islands - DFS 방식 + * + * 문제: m x n 그리드에서 섬의 개수 구하기 + * - '1': 땅, '0': 물 + * - 섬: 상하좌우로 연결된 땅의 집합 + * + * 접근 방법: + * 1. 그리드를 순회하면서 '1'을 만나면 섬 발견 (count++) + * 2. DFS로 연결된 모든 '1'을 '0'으로 바꿔서 방문 표시 + * 3. 다음 '1'을 찾으면 새로운 섬 + * + * 시간복잡도: O(m * n) - 모든 셀을 최대 한 번씩 방문 + * 공간복잡도: O(m * n) - 최악의 경우 DFS 재귀 스택 (모든 칸이 땅일 때) + * + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + if (!grid || grid.length === 0) return 0; + + const m = grid.length; + const n = grid[0].length; + let count = 0; + + // DFS로 연결된 모든 땅을 방문 처리 + const dfs = (i, j) => { + // 범위 벗어나거나 물이면 종료 + if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] === '0') { + return; + } + + // 방문 처리 (땅을 물로 바꿈) + grid[i][j] = '0'; + + // 상하좌우 탐색 + dfs(i - 1, j); // 위 + dfs(i + 1, j); // 아래 + dfs(i, j - 1); // 왼쪽 + dfs(i, j + 1); // 오른쪽 + }; + + // 그리드 전체 순회 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] === '1') { + count++; // 새로운 섬 발견 + dfs(i, j); // 연결된 모든 땅 방문 처리 + } + } + } + + return count; +}; + +// 테스트 +if (require.main === module) { + // Example 1 + const grid1 = [ + ['1', '1', '1', '1', '0'], + ['1', '1', '0', '1', '0'], + ['1', '1', '0', '0', '0'], + ['0', '0', '0', '0', '0'], + ]; + console.log(numIslands(grid1)); // 1 + + // Example 2 + const grid2 = [ + ['1', '1', '0', '0', '0'], + ['1', '1', '0', '0', '0'], + ['0', '0', '1', '0', '0'], + ['0', '0', '0', '1', '1'], + ]; + console.log(numIslands(grid2)); // 3 +} diff --git a/unique-paths/smosco.js b/unique-paths/smosco.js new file mode 100644 index 000000000..fb3620d33 --- /dev/null +++ b/unique-paths/smosco.js @@ -0,0 +1,45 @@ +/** + * Unique Paths - 조합론 방식 + * + * 문제: m x n 그리드에서 (0,0)에서 (m-1, n-1)까지 가는 경로의 수 + * - 오른쪽 또는 아래로만 이동 가능 + * + * 핵심 아이디어: + * - 총 이동 횟수: (m-1) + (n-1) = m+n-2번 + * - 오른쪽 n-1번, 아래 m-1번 이동 필요 + * - 순서만 다른 조합 문제: C(m+n-2, m-1) 또는 C(m+n-2, n-1) + * + * 예: m=3, n=7 + * - 총 8번 이동 (오른쪽 6번, 아래 2번) + * - C(8, 2) = 8!/(2!*6!) = 28 + * + * 시간복잡도: O(min(m, n)) - 조합 계산 + * 공간복잡도: O(1) + * + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function(m, n) { + // C(m+n-2, m-1) = (m+n-2)! / ((m-1)! * (n-1)!) + // = (m+n-2) * (m+n-3) * ... * n / (m-1)! + + let total = m + n - 2; // 총 이동 횟수 + let k = Math.min(m - 1, n - 1); // 작은 쪽 선택 (계산 효율) + + let result = 1; + + // C(total, k) 계산 + for (let i = 1; i <= k; i++) { + result = result * (total - k + i) / i; + } + + return result; +}; + +// 테스트 +if (require.main === module) { + console.log(uniquePaths(3, 7)); // 28 + console.log(uniquePaths(3, 2)); // 3 + console.log(uniquePaths(1, 1)); // 1 +}