diff --git a/longest-substring-without-repeating-characters/soobing.ts b/longest-substring-without-repeating-characters/soobing.ts new file mode 100644 index 000000000..5e77a63b6 --- /dev/null +++ b/longest-substring-without-repeating-characters/soobing.ts @@ -0,0 +1,28 @@ +/** + * 문제 설명 + * - 중복되지 않는 가장 긴 부분 문자열의 길이 구하기 + * + * 아이디어 + * 1) Brute Force - O(n^2) + * 2) Sliding Window + Hash Set - O(n) + * - 투 포인터를 활용하여 시작점과, 끝을 가르키는 두개의 포인터를 사용. + * - 끝 포인터가 이미 존재하는 문자열에 포함하는 경우 시작 포인터를 끝 포인터가 존재하지 않는 곳까지 이동시킨다. + * + */ +function lengthOfLongestSubstring(s: string): number { + const seen = new Set(); + let left = 0; + let maxLength = 0; + + for (let right = 0; right < s.length; right++) { + while (seen.has(s[right])) { + seen.delete(s[left]); + left++; + } + + seen.add(s[right]); + maxLength = Math.max(maxLength, right - left + 1); + } + + return maxLength; +} diff --git a/number-of-islands/soobing.ts b/number-of-islands/soobing.ts new file mode 100644 index 000000000..8fdc56d4a --- /dev/null +++ b/number-of-islands/soobing.ts @@ -0,0 +1,91 @@ +/** + * 문제 설명 + * - 2차원 그리드에서 섬의 갯수를 구하는 문제 + * + * 조건 + * - 가로, 세로가 1로 인접해있는 경우 같은 섬으로 간주 + * + * 아이디어 + * - 경로 탐색, 네트워크, 조합 유형이 나오면 '그래프 탐색 알고리즘'을 떠울린다. + * 1) DFS (재귀) + * 2) BFS (링크드리스트 or 큐) + * + */ + +function numIslands(grid: string[][]): number { + const rows = grid.length; + const cols = grid[0].length; + let count = 0; + function dfs(r: number, c: number) { + if (r < 0 || c < 0 || r >= rows || c >= cols || grid[r][c] === "0") return; + + grid[r][c] = "0"; + dfs(r - 1, c); + dfs(r + 1, c); + dfs(r, c - 1); + dfs(r, c + 1); + } + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (grid[r][c] === "1") { + count++; + dfs(r, c); + } + } + } + return count; +} + +/** + * + * BFS version + +function numIslands(grid: string[][]): number { + const rows = grid.length; + const cols = grid[0].length; + let count = 0; + + const directions = [ + [0, 1], + [1, 0], + [0, -1], + [-1, 0], + ]; + function bfs(r: number, c: number) { + const queue = [[r, c]]; + grid[r][c] = "0"; + + while (queue.length) { + const [row, col] = queue.shift()!; + + for (const [dr, dc] of directions) { + const newRow = row + dr; + const newCol = col + dc; + + if ( + newRow >= 0 && + newRow < rows && + newCol >= 0 && + newCol < cols && + grid[newRow][newCol] === "1" + ) { + queue.push([newRow, newCol]); + grid[newRow][newCol] = "0"; + } + } + } + } + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (grid[r][c] === "1") { + count++; + bfs(r, c); + } + } + } + return count; +} + + */ diff --git a/reverse-linked-list/soobing.ts b/reverse-linked-list/soobing.ts new file mode 100644 index 000000000..fbe1ae913 --- /dev/null +++ b/reverse-linked-list/soobing.ts @@ -0,0 +1,38 @@ +/** + * 문제 설명 + * - 링크드 리스트를 뒤집는 문제 + * + * 아이디어 + * - 노드를 순회하면서 다음 노드를 미리 기억해두고, 화살표를 반대로 돌린 후 한칸씩 이동한다. + * - prev, head(current) 두 가지 포인터가 필요 + */ + +/** + * Definition for singly-linked list. + * 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) + * } + * } + */ + +interface ListNode { + val: number; + next: ListNode | null; +} + +function reverseList(head: ListNode | null): ListNode | null { + let prev: ListNode | null = null; + + while (head) { + const next = head.next; + head.next = prev; + prev = head; + head = next; + } + + return prev; +} diff --git a/set-matrix-zeroes/soobing.ts b/set-matrix-zeroes/soobing.ts new file mode 100644 index 000000000..74b547307 --- /dev/null +++ b/set-matrix-zeroes/soobing.ts @@ -0,0 +1,95 @@ +/** + * 문제 설명 + * - 0으로 표시된 좌표의 행과 열을 모두 0으로 만드는 문제 + * - 다른 메모리를 만들지 말고, 핵심은 in place!! 입력데이터를 수정해서 푸는것을 원함 + * - "in-place"로 풀라고 하면 보통은 O(1) 공간복잡도를 기대 + * + * 아이디어 + * 1) 행렬을 전체 순회하면서 0의 위치를 set에 기억해놨다가 다시 순회하면서 변경 -> 공간 복잡도 O(m + n) + * + * 2) 첫번째 행과 열을 메모리로 사용 -> 공간 복잡도 O(1) + * - 첫번째 행과 열에 0이 존재하는지 확인 후 변수로 따로 뺀다 + * - 나머지 셀 중에서 0이 존재하는지 순회하고 있으면 첫번째 행/열에 표시한다 + * - 나머지 셀을 다시 순회하면서 첫번째 열을 참고하여 0으로 치환한다 + * - 첫번째 행과 열을 0으로 바꿔아한다면 처리한다. + */ + +/** + 1번 방법 + * +function setZeroes(matrix: number[][]): void { + const rows = matrix.length; + const cols = matrix[0].length; + const zeroRows = new Set(); + const zeroCols = new Set(); + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (matrix[r][c] === 0) { + zeroRows.add(r); + zeroCols.add(c); + } + } + } + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (zeroRows.has(r) || zeroCols.has(c)) { + matrix[r][c] = 0; + } + } + } +} +*/ + +function setZeroes(matrix: number[][]): void { + const rows = matrix.length; + const cols = matrix[0].length; + let firstRowHasZero = false; + let firstColHasZero = false; + + // 첫번째 행과 열에 0이 존재하는지 확인 + for (let r = 0; r < rows; r++) { + if (matrix[r][0] === 0) { + firstColHasZero = true; + break; + } + } + for (let c = 0; c < cols; c++) { + if (matrix[0][c] === 0) { + firstRowHasZero = true; + break; + } + } + + // 나머지 셀중에 0이 존재하는지 확인 후 첫번쨰 행과 열에 표시 + for (let r = 1; r < rows; r++) { + for (let c = 1; c < cols; c++) { + if (matrix[r][c] === 0) { + matrix[r][0] = 0; + matrix[0][c] = 0; + } + } + } + + // 나머지 셀을 다시 순회하면서 첫번째 행/열이 0인 경우 같은 행/열도 0으로 변경 + for (let r = 1; r < rows; r++) { + for (let c = 1; c < cols; c++) { + if (matrix[r][0] === 0 || matrix[0][c] === 0) { + matrix[r][c] = 0; + } + } + } + + // 첫번째 행/열이 0인경우 해당 행/열도 0으로 변경 + if (firstRowHasZero) { + for (let c = 0; c < cols; c++) { + matrix[0][c] = 0; + } + } + if (firstColHasZero) { + for (let r = 0; r < rows; r++) { + matrix[r][0] = 0; + } + } +} diff --git a/unique-paths/soobing.ts b/unique-paths/soobing.ts new file mode 100644 index 000000000..45c1657a2 --- /dev/null +++ b/unique-paths/soobing.ts @@ -0,0 +1,59 @@ +/** + * 문제 설명 + * - m행과 n열의 그래프에서 오른쪽 아래에 도달할 수 있는 방법의 가짓수 + * - 아래, 오른쪽으로만 이동 가능 + * + * 아이디어 + * 1) DP + * - 첫번째 행, 첫번째 열을 제외하고나서는 (m - 1)번 아래 + (n - 1)번 오른쪽을 더한 값이 현재 위치에 올 수 있는 방법임. + * - 전부 거친다음 가장 오른쪽 아래의 값을 반환하면 정답 + * 2) 조합 -> factorial의 경우 Maximum call stack size exceed 발생 + * - 아래로 이동 가능한 수(m-1), 오른쪽으로 이동 가능한 수 (n-1)의 조합 + * - (m + n - 2)! / (m - 1)! * (n - 1)! + */ +function uniquePaths(m: number, n: number): number { + const dp = Array(m).fill(Array(n).fill(1)); + + for (let r = 1; r < m; r++) { + for (let c = 1; c < n; c++) { + dp[r][c] = dp[r - 1][c] + dp[r][c - 1]; + } + } + + return dp[m - 1][n - 1]; +} + +/** + * factorial 풀이 -> 에러발생 + * +function uniquePaths(m: number, n: number): number { + function factorial(n: number) { + if (n === 1) return 1; + + return n * factorial(n - 1); + } + + return factorial(m + n - 2)! / (factorial(m - 1) * factorial(n - 1)); + +} +*/ + +/** + * 코드 리뷰 답안 기록 + function uniquePaths(m: number, n: number): number { + const memo = new Map(); + + const traverse = (row: number, col: number) => { + if (row >= m || col >= n) return 0; + if (row === m - 1 && col === n - 1) return 1; + const key = `${row}-${col}`; + if (memo.has(key)) return memo.get(key); + + const result = traverse(row + 1, col) + traverse(row, col + 1); + memo.set(key, result); + return result; + }; + + return traverse(0, 0); + } + */