From 5e35f9cb571ecaa8e506cb103239983508f9ae25 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Wed, 22 Jan 2025 15:15:58 +0900 Subject: [PATCH 1/7] add solution : 62. Unique Paths --- unique-paths/mmyeon.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 unique-paths/mmyeon.ts diff --git a/unique-paths/mmyeon.ts b/unique-paths/mmyeon.ts new file mode 100644 index 000000000..44d9fdf41 --- /dev/null +++ b/unique-paths/mmyeon.ts @@ -0,0 +1,40 @@ +/** + * @link https://leetcode.com/problems/unique-paths/ + * + * 접근 방법 : + * - 시작부터 끝까지 누적된 경로의 수 찾아야 하니까 dp 사용 + * - 첫 번째 행과, 첫 번째 열은 1로 고정이라서 dp 배열을 1로 초기화함 + * - 점화식 : dp[x][y] = dp[x-1][y] + dp[x][y-1] + * + * 시간복잡도 : O(m * n) + * - m * n 행렬 크기만큼 순회하니까 O(m * n) + * + * 공간복잡도 : O(m * n) + * - 주어진 행렬 크기만큼 dp 배열에 값 저장하니까 O(m * n) + */ +function uniquePaths(m: number, n: number): number { + // m x n 배열 선언 + const dp = Array.from({ length: m }, () => Array(n).fill(1)); + + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + + return dp[m - 1][n - 1]; +} + +// 공간 복잡도를 O(n)으로 최적화하기 위해서 1차원 dp 배열 사용 +function uniquePaths(m: number, n: number): number { + // m x n 배열 선언 + const rows = Array(n).fill(1); + + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + rows[j] = rows[j] + rows[j - 1]; + } + } + + return rows[n - 1]; +} From e6e48af6d7b30363e76387f3141cffdfd8ad4a63 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Wed, 22 Jan 2025 16:50:35 +0900 Subject: [PATCH 2/7] add solution : 200. Number of Islands --- number-of-islands/mmyeon.ts | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 number-of-islands/mmyeon.ts diff --git a/number-of-islands/mmyeon.ts b/number-of-islands/mmyeon.ts new file mode 100644 index 000000000..0393063b1 --- /dev/null +++ b/number-of-islands/mmyeon.ts @@ -0,0 +1,60 @@ +/** + *@link https://leetcode.com/problems/number-of-islands/ + * + * 접근 방법 : + * - 섬의 시작점에서 끝까지 탐색해야 하므로 DFS 사용 + * - 방문한 섬은 중복 탐색 방지하기 위해서 방문 후 값 변경 처리 + * + * 시간복잡도 : O(m * n) + * - 2차원 배열 전체를 순회하므로 O(m * n) + * + * 공간복잡도 : O(m * n) + * - DFS 호출 스택 최대 깊이가 m * n 만큼 쌓일 수 있다. + */ + +function numIslands(grid: string[][]): number { + let count = 0; + const rows = grid.length; + const cols = grid[0].length; + + // 상하좌우 탐색 방향 배열 + const directions = [ + [0, -1], + [0, 1], + [-1, 0], + [1, 0], + ]; + + // 현재 위치에서 연결된 모든 섬 탐색 + const dfs = (row: number, col: number) => { + // 종료 조건 : 범위 벗어나거나, 이미 방문한 경우 + if ( + row < 0 || + row >= rows || + col < 0 || + col >= cols || + grid[row][col] === "0" + ) + return; + + // 현재 위치 방문 처리 + grid[row][col] = "0"; + + // 상하좌우 탐색 + for (const [x, y] of directions) { + dfs(row + x, col + y); + } + }; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + // 섬의 시작점인 경우 + if (grid[row][col] === "1") { + count++; + dfs(row, col); + } + } + } + + return count; +} From 8d93ac6dec93ba39505934715b5e3a5ff246062c Mon Sep 17 00:00:00 2001 From: mmyeon Date: Wed, 22 Jan 2025 18:37:12 +0900 Subject: [PATCH 3/7] add solution : 3. Longest Substring Without Repeating Characters --- .../mmyeon.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 longest-substring-without-repeating-characters/mmyeon.ts diff --git a/longest-substring-without-repeating-characters/mmyeon.ts b/longest-substring-without-repeating-characters/mmyeon.ts new file mode 100644 index 000000000..22e617041 --- /dev/null +++ b/longest-substring-without-repeating-characters/mmyeon.ts @@ -0,0 +1,36 @@ +/** + *@link https://leetcode.com/problems/longest-substring-without-repeating-characters/description/ + * + * 접근 방법 : + * - 슬라우딩 윈도우와 set 사용해서 중복없는 문자열 확인 + * - 중복 문자 발견하면 윈도우 축소 + * + * 시간복잡도 : O(n) + * - 각 문자 순회하니까 + * + * 공간복잡도 : O(n) + * - 중복 없는 경우 최대 n개의 문자 set에 저장 + */ +function lengthOfLongestSubstring(s: string): number { + let start = 0, + end = 0, + maxLength = 0; + const set = new Set(); + + while (end < s.length) { + const char = s[end]; + + // 중복이 있으면 윈도우 축소 + if (set.has(char)) { + set.delete(s[start]); + start++; + } else { + // 중복 없으면 set에 문자 추가, 윈도우 확장 + set.add(char); + maxLength = Math.max(maxLength, end - start + 1); + end++; + } + } + + return maxLength; +} From 3c111b6ad995a49dd421c646ff757f7b40e1c0d8 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Wed, 22 Jan 2025 18:42:34 +0900 Subject: [PATCH 4/7] remove comment --- unique-paths/mmyeon.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/unique-paths/mmyeon.ts b/unique-paths/mmyeon.ts index 44d9fdf41..4c386d1d1 100644 --- a/unique-paths/mmyeon.ts +++ b/unique-paths/mmyeon.ts @@ -27,7 +27,6 @@ function uniquePaths(m: number, n: number): number { // 공간 복잡도를 O(n)으로 최적화하기 위해서 1차원 dp 배열 사용 function uniquePaths(m: number, n: number): number { - // m x n 배열 선언 const rows = Array(n).fill(1); for (let i = 1; i < m; i++) { From 3cd4f1551d2e50e1d58c4d2e50a329f33da43c07 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Thu, 23 Jan 2025 11:20:00 +0900 Subject: [PATCH 5/7] add solution : 206. Reverse Linked List --- reverse-linked-list/mmyeon.ts | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 reverse-linked-list/mmyeon.ts diff --git a/reverse-linked-list/mmyeon.ts b/reverse-linked-list/mmyeon.ts new file mode 100644 index 000000000..40c410a50 --- /dev/null +++ b/reverse-linked-list/mmyeon.ts @@ -0,0 +1,37 @@ +class ListNode { + val: number; + next: ListNode | null; + constructor(val?: number, next?: ListNode | null) { + this.val = val === undefined ? 0 : val; + this.next = next === undefined ? null : next; + } +} + +/** + * + * @link https://leetcode.com/problems/reverse-linked-list/ + * + * 접근 방법 : + * - 리스트 순회하면서 새로운 노드를 생성하고 기존 reversed 리스트의 head를 연결 + * + * 시간복잡도 : O(n) + * - 리스트 노드 1회 순회하니까 + * + * 공간복잡도 : O(n) + * - reversed 된 링크드 리스트 새로 만드니까 + */ + +function reverseList(head: ListNode | null): ListNode | null { + if (head === null) return head; + + let headNode: ListNode | null = null; + let currentNode: ListNode | null = head; + + while (currentNode !== null) { + const newNode = new ListNode(currentNode.val, headNode); + headNode = newNode; + currentNode = currentNode.next; + } + + return headNode; +} From d642590aa5f56d51a0dc6e1e8b953237c69b6cde Mon Sep 17 00:00:00 2001 From: mmyeon Date: Thu, 23 Jan 2025 13:53:12 +0900 Subject: [PATCH 6/7] add solution : 73. Set Matrix Zeroes --- set-matrix-zeroes/mmyeon.ts | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 set-matrix-zeroes/mmyeon.ts diff --git a/set-matrix-zeroes/mmyeon.ts b/set-matrix-zeroes/mmyeon.ts new file mode 100644 index 000000000..e5b909c46 --- /dev/null +++ b/set-matrix-zeroes/mmyeon.ts @@ -0,0 +1,88 @@ +/** + Do not return anything, modify matrix in-place instead. + */ +/** + * @link https://leetcode.com/problems/set-matrix-zeroes/description/ + * + * 접근 방법 : + * - 행렬 순회하면서 0이 있는 행과 열을 rowsToZero과 colsToZero에 저장 + * - 다시 행렬 순회하면서 rowsToZero과 colsToZero와 포함된 경우 0으로 변경 + * + * 시간복잡도 : O(m * n) + * - m * n 행렬 크기만큼 순회 + * + * 공간복잡도 : O(m + n) + * - m 행과 n 열 정보 저장 + */ +function setZeroes(matrix: number[][]): void { + const rows = matrix.length; + const cols = matrix[0].length; + const rowsToZero = new Set(); + const colsToZero = new Set(); + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + if (matrix[row][col] === 0) { + rowsToZero.add(row); + colsToZero.add(col); + } + } + } + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + if (rowsToZero.has(row) || colsToZero.has(col)) { + matrix[row][col] = 0; + } + } + } +} + +// 공간 복잡도 O(m+n)을 O(1)로 개선하기 +// set에 행과 열 정보 저장하던 방식을 set없이 첫 번째 행과 열을 활용하는 방법으로 변경 +function setZeroes(matrix: number[][]): void { + const rows = matrix.length; + const cols = matrix[0].length; + let hasZeroInFirstRow = false; + let hasZeroInFirstCol = false; + + // 첫 번째 열에 0 있는지 여부를 플래그로 저장 + for (let row = 0; row < rows; row++) { + if (matrix[row][0] === 0) hasZeroInFirstCol = true; + } + + // 첫 번째 행에 0 있는지 여부를 플래그로 저장 + for (let col = 0; col < cols; col++) { + if (matrix[0][col] === 0) hasZeroInFirstRow = true; + } + + // 첫 번째 행과 열 제외하고 마커 설정하기 + for (let row = 1; row < rows; row++) { + for (let col = 1; col < cols; col++) { + if (matrix[row][col] === 0) { + matrix[0][col] = 0; + matrix[row][0] = 0; + } + } + } + + // 마커 기반으로 행렬에 반영 + for (let row = 1; row < rows; row++) { + for (let col = 1; col < cols; col++) { + if (matrix[row][0] === 0 || matrix[0][col] === 0) matrix[row][col] = 0; + } + } + + // 첫 번째 행 업데이트 + if (hasZeroInFirstRow) { + for (let col = 0; col < cols; col++) { + matrix[0][col] = 0; + } + } + + // 첫 번째 열 업데이트 + if (hasZeroInFirstCol) { + for (let row = 0; row < rows; row++) { + matrix[row][0] = 0; + } + } +} From 05279602801aca2d7cb7c7563788c40d48968849 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Fri, 24 Jan 2025 11:48:12 +0900 Subject: [PATCH 7/7] improve sliding window logic --- .../mmyeon.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/longest-substring-without-repeating-characters/mmyeon.ts b/longest-substring-without-repeating-characters/mmyeon.ts index 22e617041..d0bbbc6ae 100644 --- a/longest-substring-without-repeating-characters/mmyeon.ts +++ b/longest-substring-without-repeating-characters/mmyeon.ts @@ -34,3 +34,22 @@ function lengthOfLongestSubstring(s: string): number { return maxLength; } + +// Map 사용해서 중복 발생시 start 인덱스가 점프하도록 개선 +function lengthOfLongestSubstring(s: string): number { + let start = 0, + maxLength = 0; + const map = new Map(); + + for (let i = 0; i < s.length; i++) { + const char = s[i]; + // 중복 있는 경우, 중복문자의 다음 위치로 점프 + if (map.has(char)) start = Math.max(start, map.get(char)! + 1); + // 인덱스 갱신 + map.set(char, i); + + maxLength = Math.max(maxLength, i - start + 1); + } + + return maxLength; +}