-
-
Notifications
You must be signed in to change notification settings - Fork 195
[soobing] WEEK07 Solutions #1465
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
base: main
Are you sure you want to change the base?
Changes from all commits
ece6e47
ef1c977
fde5f42
009270c
4337a1e
d3862c9
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,28 @@ | ||
/** | ||
* 문제 설명 | ||
* - 중복되지 않는 가장 긴 부분 문자열의 길이 구하기 | ||
* | ||
* 아이디어 | ||
* 1) Brute Force - O(n^2) | ||
* 2) Sliding Window + Hash Set - O(n) | ||
* - 투 포인터를 활용하여 시작점과, 끝을 가르키는 두개의 포인터를 사용. | ||
* - 끝 포인터가 이미 존재하는 문자열에 포함하는 경우 시작 포인터를 끝 포인터가 존재하지 않는 곳까지 이동시킨다. | ||
* | ||
*/ | ||
function lengthOfLongestSubstring(s: string): number { | ||
const seen = new Set<string>(); | ||
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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
|
||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) { | ||
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. 반복문으로 푸는 코드가 정말 깔끔하네요 🤩 문제 하단에 follow up 조건으로 재귀 방식으로도 풀어보는 걸 권하고 있어서, 재귀로도 한 번 더 풀어보셔도 좋을 것 같습니다! |
||
const next = head.next; | ||
head.next = prev; | ||
prev = head; | ||
head = next; | ||
} | ||
|
||
return prev; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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으로 바꿔아한다면 처리한다. | ||
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. 아이디어를 코멘트로 정리/비교해주셔서 리뷰에 도움이 되는 것 같습니다! 실제로 코딩 인터뷰를 볼 때에도 좋은 인상을 남길 수 있는 습관인 것 같아요!! |
||
*/ | ||
|
||
/** | ||
1번 방법 | ||
* | ||
function setZeroes(matrix: number[][]): void { | ||
const rows = matrix.length; | ||
const cols = matrix[0].length; | ||
const zeroRows = new Set<number>(); | ||
const zeroCols = new Set<number>(); | ||
|
||
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; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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]; | ||
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. 2D DP에서 이 기법은 row 만큼 순회하면서 1D 리스트인 |
||
} | ||
} | ||
|
||
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<string, number>(); | ||
|
||
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); | ||
} | ||
*/ |
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.
seen
을 set이 아닌 hash map으로 관리하며 가장 최근에 등장했던 인덱스까지 함께 저장한다면,left
를 1씩 증가시키며 확인하는 것이 아닌 곧바로 가장 최근에 등장했던 인덱스의 다음 위치(=seen[s[right]] + 1
)로 옮길 수 있어 최적화가 가능하다고 합니다. (단, 이때seen[s[right]] >= left
인지 추가로 확인해야 합니다!)저도 처음에 soobing님과 동일하게 투 포인터와 set을 이용해
left
를 1씩 증가시키면서 확인하는 방식으로 풀었는데요, 솔루션 탭에서 더 최적화 할 수 있는 풀이법을 알게 되어 공유드려요! 😆