diff --git a/longest-substring-without-repeating-characters/Blossssom.ts b/longest-substring-without-repeating-characters/Blossssom.ts new file mode 100644 index 0000000000..f772fce689 --- /dev/null +++ b/longest-substring-without-repeating-characters/Blossssom.ts @@ -0,0 +1,59 @@ +/** + * @param s - 문자열 입력값 + * @returns - 부분 문자열 중복없이 가장 긴 문자열의 길이 + * @description + * - 풀이 1: 앞단계를 Map으로 정리 후 같은 값이 있을 경우 해당 값을 삭제할 때 까지 반복 + * - 삭제 전 길이를 누적해 가장 큰 값을 반환 + * + * - 풀이 2: 반복을 돌며 찾는게 아닌 해당 idx로 점프하는 과정으로 변경 + * - 최악의 경우를 이전 풀이 1보다 개선 + */ + +// function lengthOfLongestSubstring(s: string): number { +// const saveMap: Map = new Map(); +// const arr = []; +// for (let i = 0; i < s.length; i++) { +// if (saveMap.has(s[i])) { +// for (const key of saveMap.keys()) { +// arr.push(saveMap.size); +// saveMap.delete(key); +// if (key === s[i]) { +// break; +// } +// } +// } +// saveMap.set(s[i], true); +// } + +// arr.push(saveMap.size); + +// return Math.max(...arr); +// } + +function lengthOfLongestSubstring(s: string): number { + const map = new Map(); + + let maxLength = 0; + let start = 0; + + for (let i = 0; i < s.length; i++) { + const char = s[i]; + // 해당 문자열이 있고, 그 값이 start 보다 크거나 같을 경우 + // start를 중복 문자 바로 다음 칸으로 이동 + if (map.has(char) && map.get(char)! >= start) { + start = map.get(char)! + 1; + } + + map.set(char, i); + + // 현재 idx - 시작점 + 1 === 누적 문자열의 길이 + maxLength = Math.max(maxLength, i - start + 1); + } + + return maxLength; +} + +const s = "dvdf"; +lengthOfLongestSubstring(s); + + diff --git a/number-of-islands/Blossssom.ts b/number-of-islands/Blossssom.ts new file mode 100644 index 0000000000..491592835c --- /dev/null +++ b/number-of-islands/Blossssom.ts @@ -0,0 +1,59 @@ +/** + * @param grid - 섬(1), 물(0)로 이뤄진 m * n 2차원 배열 + * @returns - 섬의 갯수 (연결되지 않으면 개별 섬) + * @description + * - dfs 까진 알았는데 AI의 도움을 받았음 + * - 탐색 문제에서 디테일까지 접근하는 연습이 필요 + * - 시간 복잡도 O(M * N) + */ + +function numIslands(grid: string[][]): number { + const visit = Array.from({ length: grid.length }, () => + Array.from({ length: grid[0].length }, () => false) + ); + + const moveY = [0, 0, -1, 1]; + const moveX = [-1, 1, 0, 0]; + + let cnt = 0; + function dfs(y: number, x: number) { + visit[y][x] = true; + // 4 방향 index 확인 + for (let i = 0; i < 4; i++) { + const nextY = y + moveY[i]; + const nextX = x + moveX[i]; + + if ( + nextX >= 0 && + nextY >= 0 && + nextX < grid[0].length && + nextY < grid.length && + grid[nextY][nextX] === "1" && + !visit[nextY][nextX] + ) { + dfs(nextY, nextX); + } + } + } + + for (let i = 0; i < grid.length; i++) { + for (let j = 0; j < grid[0].length; j++) { + if (grid[i][j] === "1" && !visit[i][j]) { + cnt++; + dfs(i, j); + } + } + } + + return cnt; +} + +const grid = [ + ["1", "1", "0", "0", "0"], + ["1", "1", "0", "0", "0"], + ["0", "0", "1", "0", "0"], + ["0", "0", "0", "1", "1"], +]; +numIslands(grid); + + diff --git a/reverse-linked-list/Blossssom.ts b/reverse-linked-list/Blossssom.ts new file mode 100644 index 0000000000..9d67a2feec --- /dev/null +++ b/reverse-linked-list/Blossssom.ts @@ -0,0 +1,71 @@ +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; + } +} + +/** + * + * @param head - ListNode 구조체 + * @returns - 역순 head + * @description + * - 1차 시도: 단순 val 추출 후 역순으로 재 배치 + * - 2차 시도: head를 순회하며 스와핑, prev에 재 배치 하며 진행 + */ + +// function reverseList(head: ListNode | null): ListNode | null { +// if (!head) { +// return head; +// } + +// let currentNode: ListNode | null = head; +// let reversed: ListNode = new ListNode(); +// const saveArr = []; +// while (currentNode?.val !== undefined) { +// saveArr.push(currentNode.val); +// currentNode = currentNode.next; +// } + +// let check = reversed; +// for (let i = saveArr.length; i > 0; i--) { +// check.val = saveArr[i - 1]; +// if (i - 1) { +// check.next = new ListNode(); +// check = check.next; +// } +// } + +// return reversed; +// } + +function reverseList(head: ListNode | null): ListNode | null { + if (!head) { + return null; + } + + let prevNode: ListNode | null = null; + let nextNode: ListNode | null = null; + + console.log(head); + while (head) { + nextNode = head.next; + head.next = prevNode; + prevNode = head; + head = nextNode; + console.log(head, nextNode, prevNode); + } + + return prevNode; +} + +const head = new ListNode( + 0, + new ListNode(1, new ListNode(4, new ListNode(-2))) +); + +reverseList(head); + + diff --git a/set-matrix-zeroes/Blossssom.ts b/set-matrix-zeroes/Blossssom.ts new file mode 100644 index 0000000000..af7f9ad0ed --- /dev/null +++ b/set-matrix-zeroes/Blossssom.ts @@ -0,0 +1,86 @@ +/** + * @param matrix - 2중 배열 그리드 + * @description + * - 요소가 0일 경우 속한 행과 열 전체를 0으로 변경 + * + * - 풀이 1. 단순하게 0 목록을 만들어 해당 요소의 행과 열을 변경 + * - 풀이 2. 풀이 1은 매번 덮어씌우기를 진행하므로 행과 열 마다 첫번 째 요소를 메모장으로 사용. + * - 내부를 탐색하며 진행 + */ + +// function setZeroes(matrix: number[][]): void { +// const zeroes = []; + +// for (let i = 0; i < matrix.length; i++) { +// for (let j = 0; j < matrix[0].length; j++) { +// if (matrix[i][j] === 0) { +// zeroes.push([i, j]); +// } +// } +// } + +// for (let i = 0; i < zeroes.length; i++) { +// const [row, col] = zeroes[i]; +// console.log(row, col); + +// matrix[row] = Array.from({ length: matrix[row].length }, () => 0); +// console.log(matrix); + +// for (let j = 0; j < matrix.length; j++) { +// matrix[j][col] = 0; +// } +// } +// } + +function setZeroes(matrix: number[][]): void { + const m = matrix.length; + const n = matrix[0].length; + let isFirstRowZero = false; + let isFirstColZero = false; + + for (let i = 0; i < m; i++) { + if (!matrix[i][0]) { + isFirstColZero = true; + } + } + + for (let j = 0; j < n; j++) { + if (!matrix[0][j]) { + isFirstRowZero = true; + } + } + + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + if (!matrix[i][j]) { + matrix[i][0] = 0; + matrix[0][j] = 0; + } + } + } + + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + if (matrix[i][0] === 0 || matrix[0][j] === 0) { + matrix[i][j] = 0; + } + } + } + + if (isFirstColZero) { + for (let i = 0; i < m; i++) matrix[i][0] = 0; + } + if (isFirstRowZero) { + for (let j = 0; j < n; j++) matrix[0][j] = 0; + } +} + +const matrix = [ + [1, 1, 1], + [1, 0, 1], + [1, 1, 1], +]; + +setZeroes(matrix); + + diff --git a/unique-paths/Blossssom.ts b/unique-paths/Blossssom.ts new file mode 100644 index 0000000000..fb86630dee --- /dev/null +++ b/unique-paths/Blossssom.ts @@ -0,0 +1,46 @@ +/** + * @param m - grid row length + * @param n - grid col length + * @returns - 오른쪽 아래 모서리에 도착할 수 있는 경로의 수 + * @description + * - [0, 0] 시작 + * - 아래쪽 혹은 오른쪽으로만 이동 가능 + * - dfs인줄 알았는데 visit도 필요 없고 결국 dp. + * + * - 풀이 1. 각 지점에 도달할 수 있는 경우는 이전 idx의 경우의 수들 (x - 1, y - 1)의 합이다. + * - 따라서 이전 idx의 값들을 누적해가며 가장 마지막인 도착점의 누적 값이 결과. + * + * - 풀이 2. 풀이 1과 방식은 같지만 1차원 배열을 덮어 씌워가며 공간 복잡도를 줄임 + */ + +// function uniquePaths(m: number, n: number): number { +// const dp = Array.from({ length: m }, () => +// Array.from({ length: n }, () => 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]; +// console.log(dp); +// } +// } + +// return dp[m - 1][n - 1]; +// } + +function uniquePaths(m: number, n: number): number { + const dp = Array.from({ length: n }, () => 1); + + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + dp[j] += dp[j - 1]; + } + } + return dp[n - 1]; +} + +const m = 3; +const n = 7; +uniquePaths(m, n); + +