Skip to content
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

[gomgom22] Week4 #829

Merged
merged 6 commits into from
Jan 4, 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
46 changes: 46 additions & 0 deletions coin-change/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* 가지고 있는 동전을 최대한 활용하여 최소의 조합으로 amount를 만드는 최소 동전 개수 구하는 함수
*
* @param {number[]} coins - 사용 가능한 동전 배열
* @param {number} amount - 만들어야 하는 총합
* @returns {number}
*
* 시간 복잡도 O(n * m)
* - n은 동전 배열의 크기
* - m은 amount
*
* 공간 복잡도 (n);
* - 큐에 최대 n개의 요소가 들어갈 수 있음
*/
function coinChange(coins: number[], amount: number): number {
// 총합이 0인 경우 0 반환
if (amount === 0) return 0;

// 너비 우선 탐색을 활용한 풀이

const queue: [number, number] [] = [[0, 0]]; // [현재 총합, 깊이]
const visited = new Set<number>();
Copy link
Contributor

Choose a reason for hiding this comment

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

여기서도, 만약 동전에 1원이 있다면 O(m) 만큼의 공간 복잡도가 필요할것 같아요


while (queue.length > 0) {
const [currentSum, depth] = queue.shift()!;

// 동전을 하나씩 더해서 다음 깊이을 탐색
for (const coin of coins) {
const nextSum = currentSum + coin;

// 목표 금액에 도달하면 현재 깊이를 반환
if (nextSum === amount) return depth + 1;

// 아직 총합에 도달하지 않았고, 중복되지 않아 탐색 가능한 경우
if (nextSum < amount && !visited.has(nextSum)) {
queue.push([nextSum, depth + 1]);
visited.add(nextSum)
}

}
}

// 탐색 조건을 완료 해도 경우의 수를 찾지 못한 경우
return -1;
}

49 changes: 49 additions & 0 deletions merge-two-sorted-lists/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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 | null} list1 - 첫 번째 정렬된 연결 리스트
* @param {ListNode | null} list2 - 두 번째 정렬된 연결 리스트
* @returns {ListNode | null} - 병합된 정렬된 연결 리스트
*
* 시간 복잡도: O(n + m)
* - n: list1의 노드 개수
* - m: list2의 노드 개수
*
* 공간 복잡도: O(1)
* - 새로운 노드를 생성하지 않고 기존 노드의 포인터를 조정하여 병합하므로 추가 공간 사용 없음
*/

function mergeTwoLists(list1: ListNode | null, list2: ListNode | null): ListNode | null {
// null 예외 처리
if (!list1 || !list2) return list1 || list2;

// 시작점을 위한 더미 헤드 노드 생성
const start = new ListNode(-101);
Copy link
Contributor

Choose a reason for hiding this comment

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

-101에 담고싶으셨던 의미가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

큰 의미는 없고 val 제약 조건에 포함되지 않는 수를 지정하고 싶었어요!

let current = start;

// 두 리스트를 순차적으로 비교하며 병합
while (list1 !== null && list2 !== null) {
if (list1.val <= list2.val) {
current.next = list1;
list1 = list1.next;
} else {
current.next = list2;
list2 = list2.next;
}
current = current.next;
}

// 남은 노드를 연결 (list1 또는 list2 중 하나는 null 상태)
current.next = list1 !== null ? list1 : list2;

// 더미 헤드의 다음 노드가 실제 병합된 리스트의 시작
return start.next;
}
29 changes: 29 additions & 0 deletions missing-number/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* 0부터 n까지의 숫자 중 배열에서 누락된 숫자를 찾는 함수
* @param {number[]} nums - 0부터 n까지의 숫자가 포함된 배열 (순서는 무작위이며 일부 숫자가 누락될 수 있음)
* @returns {number} - 배열에서 누락된 숫자
*
* 시간 복잡도: O(n)
* - Set은 Hash Table로 구현되어 has 메서드가 Array.includes 메서드보다 유리
* - Set을 생성하는 데 O(n) 시간이 소요
* - 배열 길이만큼 반복문을 돌면서 Set의 존재 여부를 확인하는 데 O(1) * n = O(n)
* - 결과적으로 O(n) + O(n) = O(n)
*
* 공간 복잡도: O(n)
* - Set 자료구조를 사용하여 입력 배열의 모든 요소를 저장하므로 O(n)의 추가 메모리 사용
*/
function missingNumber(nums: number[]): number {
// 배열의 숫자를 모두 Set에 추가하여 중복 제거
const distinctSet = new Set([...nums]);
Yjason-K marked this conversation as resolved.
Show resolved Hide resolved

// 0부터 n까지의 숫자 중에서 누락된 숫자를 탐색
for (let i = 0; i < nums.length; i++) {
// Set에 i가 존재하지 않으면 i가 누락된 숫자
if (!distinctSet.has(i)) {
return i;
}
}

// 모든 숫자가 Set에 존재하면 n이 누락된 숫자
return nums.length;
}
48 changes: 48 additions & 0 deletions palindromic-substrings/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 문자열에서 substring 중 panlindrome인 경우의 수를 구하는 함수
* @param {string} s - 입력 문자열
* @returns {number} - s문자열에서 찾을수 있는 panlindrome substring의 개수
*
* 시간 복잡도: O(n^2) (n: 문자열 길이)
* - 한 번의 외부 루프: 부분 문자열 길이 (\(subLen\)) - O(n)
* - 내부 루프: 시작 인덱스 (\(start\)) - O(n)
* - 따라서, 총 복잡도는 O(n^2)
*
* 공간 복잡도: O(n^2)
* - DP 배열 dp[i][j]는 \(n^2\) 크기를 가지며 문자열의 모든 시작과 끝 조합에 대한 정보를 저장합니다.
*/
function countSubstrings(s: string): number {
const n = s.length; // 문자열 길이
let result = 0;

// DP 배열 생성 (dp[i][j] 는 s[i] ~ s[j] 까지가 회문인지 여부를 저장)
const dp: boolean[][] = Array.from({ length: n }, () => Array(n).fill(false));

// 1글자 경우
for (let i = 0; i < n; i++) {
dp[i][i] = true;
result++;
}

// 2글자 경우
for (let i = 0; i < n - 1; i++) {
if (s[i] === s[i + 1]) {
dp[i][i + 1] = true;
result++;
}
}

// 3글자 이상인 경우
for (let subLen = 3; subLen <= n; subLen++) {
for (let start = 0; start <= n - subLen; start++) {
const end = start + subLen - 1;
// 양 끝 문자가 같고, 내부 부분 문자열이 회문이면 true
if (s[start] === s[end] && dp[start + 1][end - 1]) {
dp[start][end] = true;
result++;
}
}
}

return result;
}
70 changes: 70 additions & 0 deletions word-search/Yjason-K.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* board 에서 주어진 단어를 찾을 수 있는지 여부 확인 (boolean)
* @param {string[][]} board - 단어를 탐색할 2D board
* @param {string} word - 찾고자 하는 단어
* @returns {boolean} - 단어가 격자에서 존재하면 true, 그렇지 않으면 false
*
* 시간 복잡도: O(N * M * 4^L)
* - N: board의 행 개수
* - M: board의 열 개수
* - L: word의 길이
*
* 공간 복잡도: O(L) (재귀 호출 스택)
*/
function exist(board: string[][], word: string): boolean {
const rows = board.length;
const cols = board[0].length;

// 방향 배열 (상, 하, 좌, 우)
const directions = [
[0, -1], // 상
[0, 1], // 하
[-1, 0], // 좌
[1, 0], // 우
];

/**
* DFS 탐색(깊이 우선 탐색)을 통해 단어를 찾는 함수
* @param {number} x - 현재 x 좌표 (열)
* @param {number} y - 현재 y 좌표 (행)
* @param {number} index - 현재 탐색 중인 word의 문자 인덱스
* @returns {boolean} - 현재 경로가 유효하면 true, 유효하지 않으면 false
*/
const dfs = (x: number, y: number, index: number): boolean => {
// 단어를 모두 찾았을 경우
if (index === word.length) return true;

// 범위를 벗어나거나 문자가 일치하지 않는 경우
if (x < 0 || y < 0 || x >= cols || y >= rows || board[y][x] !== word[index]) {
return false;
}

// 현재 위치 방문 처리 (임시 수정)
const temp = board[y][x];
board[y][x] = "#";

// 상하좌우 탐색
for (const [dx, dy] of directions) {
if (dfs(x + dx, y + dy, index + 1)) {
return true;
}
}

// 백트래킹: 셀 값 복구
board[y][x] = temp;

return false;
};

// board에서 word 첫글자가 일치하는 경우 탐색 시작
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
if (board[y][x] === word[0] && dfs(x, y, 0)) {
return true;
}
}
}

return false;
}

Loading