Skip to content

[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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions longest-substring-without-repeating-characters/soobing.ts
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++;
Copy link
Contributor

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씩 증가시키면서 확인하는 방식으로 풀었는데요, 솔루션 탭에서 더 최적화 할 수 있는 풀이법을 알게 되어 공유드려요! 😆

}

seen.add(s[right]);
maxLength = Math.max(maxLength, right - left + 1);
}

return maxLength;
}
91 changes: 91 additions & 0 deletions number-of-islands/soobing.ts
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;
}

*/
38 changes: 38 additions & 0 deletions reverse-linked-list/soobing.ts
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
}
95 changes: 95 additions & 0 deletions set-matrix-zeroes/soobing.ts
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으로 바꿔아한다면 처리한다.
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
}
}
}
59 changes: 59 additions & 0 deletions unique-paths/soobing.ts
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];
Copy link
Contributor

@seungriyou seungriyou May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2D DP에서 dp[r][c]를 구할 때 dp[r - 1][c]dp[r][c - 1]만 사용하기 때문에, rolling array 기법(참고 링크)을 이용해 1D DP로 공간 복잡도를 최적화 할 수 있을 것 같아요~!

이 기법은 row 만큼 순회하면서 1D 리스트인 dp[c]에 이전 값을 더해가며 업데이트하는 방식인데요, DP 문제에서 최적화 용도로 자주 사용되는 기법이라고 합니다! 한 번 찾아보셔도 재밌으실 것 같아요 ㅎㅎㅎ

}
}

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);
}
*/