-
-
Notifications
You must be signed in to change notification settings - Fork 305
[Blossssom] WEEK 07 solutions #2224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9df1915
1fcaf81
10ff17e
a524525
067953e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /** | ||
| * @param s - 문자열 입력값 | ||
| * @returns - 부분 문자열 중복없이 가장 긴 문자열의 길이 | ||
| * @description | ||
| * - 풀이 1: 앞단계를 Map으로 정리 후 같은 값이 있을 경우 해당 값을 삭제할 때 까지 반복 | ||
| * - 삭제 전 길이를 누적해 가장 큰 값을 반환 | ||
| * | ||
| * - 풀이 2: 반복을 돌며 찾는게 아닌 해당 idx로 점프하는 과정으로 변경 | ||
| * - 최악의 경우를 이전 풀이 1보다 개선 | ||
| */ | ||
|
|
||
| // function lengthOfLongestSubstring(s: string): number { | ||
| // const saveMap: Map<string, boolean> = 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<string, number>(); | ||
|
|
||
| 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); | ||
|
|
||
|
|
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 얼음의 갯수 등의 로컬라이징도 많이 되었던 문제죠! 저도 dfs로 접근했습니다만, nested function을 싫어하는 취향에 더해 dfs를 위해 반환값이 있는 함수를 짜는 것이 너무 불편하더라고요. 그래서 Swift의 inout 기능을 사용해서 풀었습니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
|
|
||
|
|
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 참조를 즉석에서 바꿔 주신 것도 좋네요! LeetCode의 리스트 문제는 항상 참조를 컨트롤하는 게 고역인 듯 합니다. console.log를 지우셨다면 시간 효율성은 더 잘 나오셨을 것 같네요!! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
|
|
||
|
|
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 추가 공간을 쓰지 않게끔 풀이를 진행하는 것이 어려웠던 문제였습니다. 전반적으로 첫 번째 요소와 탐색 측면에서 힘을 주신 부분이 보이네요. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
|
|
||
|
|
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DP 문제 측면에서 이 문제가 파스칼의 삼각형과 동치라고 하네요! 처음에 저는 배열을 초기화하고 모든 경로를 큐에 넣어서 bfs로 풀고자 했던 기억이 납니다. 한편 수학 공식 단 한 줄로 끝낼 수도 있어서 이건 조금 허탈했어요. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저와 똑같이 dvdf에서 한 번 쓴 맛을 보신 것 같아 웃음이 났습니다ㅋㅋㅋ 풀이 1에서 map으로 정리해주신 것에서, 풀이 2의 해당 인덱스로 점프하는 과정으로 수정해 주셨는데, 훨씬 더 효율적이겠죠. 숫자 방면의 직관이 잘 드러난 케이스로 생각합니다.