Skip to content

[gomgom22] Week10 #1026

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

Merged
merged 6 commits into from
Feb 16, 2025
Merged
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
61 changes: 61 additions & 0 deletions course-schedule/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

/**
* 선수 과목과 이수 과목 관계를 확인하여 모든 과목을 이수할 수 있는지 여부 확인.
* @param {number} numCourses - 전체 과목 수
* @param {number[][]} prerequisites - 선수 과목과 이수 과목 관계 정보
* @returns {boolean} - 모든 과목을 수강할 수 있는지 여부
*
* 시간 복잡도: O(V + E)
* - 그래프를 생성하는 과정에서 O(E),
* - bfs O(V + E),
*
* 공간 복잡도: O(V + E)
* - 각 과목에 대한 선수 과목 저장에 O(V + E),
* - prereqCount 저장에 O(V),
* - 큐(queue) 저장에 O(V),
*/
function canFinish(numCourses: number, prerequisites: number[][]): boolean {
// 각 과목의 선수 과목 정보를 저장하는 리스트
const graph: number[][] = Array.from({ length: numCourses }, () => []);
// 각 과목의 선수 과목 수 를 저장하는 배열
const prereqCount: number[] = new Array(numCourses).fill(0);

// 그래프 및 선수 과목 개수 초기화
for (const [course, prereq] of prerequisites) {
graph[prereq].push(course); // 선수 과목을 들어야 수강 가능한 과목 추가
prereqCount[course]++; // 해당 과목의 선수 과목 개수 증가
}

// 선수 과목이 없는 과목들을 저장할 큐
const queue: number[] = [];

// 선수 과목이 없는 과목들을 큐에 추가 (진입 차수가 0인 노드)
for (let i = 0; i < numCourses; i++) {
if (prereqCount[i] === 0) {
queue.push(i);
}
}

// 수강한 과목 수
let completedCourses = 0;

// 선수 과목이 없는 과목을 사용해서 다음 과목을 수강
while (queue.length > 0) {
const current = queue.shift()!;
completedCourses++; // 과목 수강 완료

// 현재 과목을 선수 과목으로 하는 다른 과목들의 선수 과목 개수 감소
for (const nextCourse of graph[current]) {
prereqCount[nextCourse]--;

// 선수 과목 개수가 0이 되면 큐에 추가 (더 이상 기다릴 필요 없음)
if (prereqCount[nextCourse] === 0) {
queue.push(nextCourse);
}
}
}

// 모든 과목을 수강 수 있는지 확인
return completedCourses === numCourses;
}

31 changes: 31 additions & 0 deletions find-minimum-in-rotated-sorted-array/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* 배열에서 가장 작은 수 찾기 ( 제약 : 시간 복잡도 O(log n) )
* @param {number[]} nums 회전된 수 배열
*
* 시간 복잡되: O(log n)
* - 이분 탐색을 사용하여 최소값을 탐색
*
* 공간 복잡도: O(1)
*/
function findMin(nums: number[]): number {
let left = 0, right = nums.length - 1;

while (left <= right) {
let mid = Math.floor((left + right) / 2);

// 정렬이 망가진 경우
if (nums[mid] < nums[mid-1]) return nums[mid];

// left, right 범위 줄여나가기
if (nums[0] < nums[mid]){
left = mid + 1;
} else {
right = mid - 1;
}
}

// 탐색 후에도 찾지 못한 경우 회전되지 않은 경우
return nums[0];

}

26 changes: 26 additions & 0 deletions invert-binary-tree/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 이진 트리를 반전시키는 함수
* @param {TreeNode | null} root - 반전할 이진 트리의 루트 노드
* @returns {TreeNode | null} - 반전된 이진 트리의 루트 노드
*
* 시간 복잡도: O(n)
* - 트리의 모든 노드를 한 번씩 방문해야 하므로 선형 시간 복잡도를 가짐
* 공간 복잡도: O(h)
* - 재귀 호출에 의해 최대 트리의 높이(h)만큼의 호출 스택이 필요
*/
function invertTree(root: TreeNode | null): TreeNode | null {
// 루트가 null이면 null 반환
if (!root) return null;

// 왼쪽과 오른쪽 서브트리를 재귀적으로 반전
const left = invertTree(root.left);
const right = invertTree(root.right);

// 현재 노드의 왼쪽과 오른쪽 서브트리를 교환
root.left = right;
root.right = left;

// 반전된 루트 노드를 반환
return root;
}

30 changes: 30 additions & 0 deletions jump-game/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* 배열의 각 인덱스에서 가능한 점프를 통해 마지막 인덱스에 도달할 수 있는지 확인하는 문제
* @param {number[]} nums - 각 인덱스에서 이동할 수 있는 최대 거리
* @returns {boolean} - 마지막 인덱스에 도달할 수 있으면 true, 아니면 false
*
* 시간 복잡도: O(2^n)
* - DFS 기반의 탐색이므로 최악의 경우 모든 경우 탐색
*
* 공간 복잡도: O(n) (재귀 호출 스택과 방문 배열 사용)
* - `visited` 배열이 nums.length 크기를 차지
*/
function canJump(nums: number[]): boolean {
const visited = new Array(nums.length).fill(false);

const dfs = (idx: number): boolean => {
if (idx >= nums.length - 1) return true; // 마지막 인덱스 이상 도달하면 성공
if (visited[idx]) return false; // 이미 방문한 경우 중복 방문 방지

visited[idx] = true; // 방문 처리

for (let i = 1; i <= nums[idx]; i++) { // 현재 위치에서 가능한 모든 점프 탐색
if (dfs(idx + i)) return true;
}

return false;
}

return dfs(0); // 0번 인덱스에서 시작
}

37 changes: 37 additions & 0 deletions linked-list-cycle/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* 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)
* }
* }
*/

/**
* 연결 리스트인가 순환하는지 여부 확인
* @param {ListNode} head - ListNode
* @returns {boolean} - 순환 여부
*
* 시간 복잡도: O(n)
*
* 공간 복잡도: O(n)
* - 노드의 개수만큼 Set에 저장
*/
function hasCycle(head: ListNode | null): boolean {
const set = new Set();

while (head) {
if (set.has(head)) {
return true;
}

set.add(head);
head = head.next;
}

return false;
}

32 changes: 32 additions & 0 deletions maximum-product-subarray/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* subArray 중 최대 곱을 구하는 함수
* @param {number[]} nums - 숫자 배열
* @returns {number} - subArray 중 최대 곱
*
* @description 음수의 곱이 존재 할 수 있기 때문에, 최대 값과 최소값을 갱신하며, 결과 값을 갱신.
*
* 시간 복잡도 : O(n)
* - nums 배열 1회 순회
*
* 공간 복잡도 : O(1)
*/
function maxProduct(nums: number[]): number {
let max = nums[0];
let min = nums[0];
let result = nums[0];

// 첫 번째 요소를 제외한 모든 요소를 탐색
for (let i = 1; i < nums.length; i++) {
let current = nums[i]

// 현재 값, 이전 최대 곱과의 곱, 이전 최소 곱과의 곱 중 최대/최소 갱신
const cases = [current * max, current * min, current];

max = Math.max(...cases);
min = Math.min(...cases);
result = Math.max(result, max);
}

return result;
}

76 changes: 76 additions & 0 deletions pacific-atlantic-water-flow/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* 깊이 우선 탐색(DFS)을 사용하여 특정 바다에서 올라갈 수 있는 좌표을 저장
* @param i 현재 위치의 행 (row)
* @param j 현재 위치의 열 (column)
* @param visited 방문한 좌표를 저장하는 Set (바다에서 도달할 수 있는 위치를 저장)
* @param heights 높이 정보가 담긴 2차원 배열
*
* 시간 복잡도: O(m × n)
* - 각 셀은 최대 한 번 방문하며, 총 m × n개의 셀을 탐색함
*
* 공간 복잡도: O(m × n)
* - `visited` Set에 최대 m × n개의 좌표를 저장 가능
* - 재귀 호출 스택의 깊이는 O(m + n) (최악의 경우 가장 긴 경로를 따라 탐색)
*/
function dfs(i: number, j: number, visited: Set<string>, heights: number[][]) {
if (visited.has(`${i},${j}`)) return;

visited.add(`${i},${j}`);

for (const [di, dj] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) {
const newI = i + di;
const newJ = j + dj;

if (
newI >= 0 && newI < heights.length &&
newJ >= 0 && newJ < heights[0].length &&
heights[newI][newJ] >= heights[i][j]
) {
dfs(newI, newJ, visited, heights);
}
}
};

/**
* 두 바다 모두 도달할 수 있는 좌표를 찾는 함수
*
* @param heights 2차원 배열로 이루어진 지형의 높이 정보
* @returns 두 바다 모두 도달할 수 있는 좌표 배열
*
* 시간 복잡도: O(m × n)
* - 태평양 및 대서양에서 각각 DFS 수행 → O(m × n)
* - 결과를 찾는 이중 루프 → O(m × n)
*
* 공간 복잡도: O(m × n)
* - `pacificSet`과 `atlanticSet`에 최대 O(m × n)개의 좌표 저장
*
*/
function pacificAtlantic(heights: number[][]): number[][] {
if (!heights || heights.length === 0 || heights[0].length === 0) return [];

const rows = heights.length;
const cols = heights[0].length;

const pacificSet = new Set<string>();
const atlanticSet = new Set<string>();

// 태평양(왼쪽, 위쪽)에서 출발하는 DFS
for (let i = 0; i < rows; i++) dfs(i, 0, pacificSet, heights);
for (let j = 0; j < cols; j++) dfs(0, j, pacificSet, heights);

// 대서양(오른쪽, 아래쪽)에서 출발하는 DFS
for (let i = 0; i < rows; i++) dfs(i, cols - 1, atlanticSet, heights);
for (let j = 0; j < cols; j++) dfs(rows - 1, j, atlanticSet, heights);

const result: number[][] = [];
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (pacificSet.has(`${i},${j}`) && atlanticSet.has(`${i},${j}`)) {
result.push([i, j]);
}
}
}

return result;
};

55 changes: 55 additions & 0 deletions search-in-rotated-sorted-array/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// similar problem to #245 Find Minimum In Rotated Sorted Array
/**
* 회정된 배열에서 target의 index를 찾는 문제
* @param {number[]} nums - 회전된 배열
* @param target - 찾는 수
* @returns {number} - target의 index
*
* 시간 복잡도: O(log n)
* - 이분 탐색을 사용하여 최적의 탐색을 수행
*
* 공간 복잡도: O(1)
* - 추가적인 공간 사용 없이 포인터만 이용하여 탐색
*/
function search(nums: number[], target: number): number {
let left = 0, right = nums.length - 1;

while (left <= right) {
let mid = Math.floor((left + right) / 2);

if (nums[mid] === target) return mid;

// mid를 기준으로 왼쪽 부분이 정렬된 경우
if (nums[left] <= nums[mid]) {
/**
* 왼쪽 부분이 오름차순 정렬되어 있다면:
* - nums[left] ~ nums[mid] 범위는 정렬되어 있음
* - target이 이 범위 내에 있다면, right를 줄여서 탐색
* - 아니라면 target은 오른쪽에 있으므로 left를 증가시켜 탐색
*/
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1; // 왼쪽 범위에서 탐색
} else {
left = mid + 1; // 오른쪽 범위에서 탐색
}
}
// mid를 기준으로 오른쪽 부분이 정렬된 경우
else {
/**
* 오른쪽 부분이 오름차순 정렬되어 있다면:
* - nums[mid] ~ nums[right] 범위는 정렬되어 있음
* - target이 이 범위 내에 있다면, left를 늘려서 탐색
* - 아니라면 target은 왼쪽에 있으므로 right를 감소시켜 탐색
*/
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1; // 오른쪽 범위에서 탐색
} else {
right = mid - 1; // 왼쪽 범위에서 탐색
}
}
}

// target을 찾지 못한 경우
return -1;
}