Skip to content

[clara-shin] WEEK 05 solutions #1392

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 7 commits into from
May 2, 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
24 changes: 24 additions & 0 deletions best-time-to-buy-and-sell-stock/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* 시간 복잡도 O(n)
* 공간 복잡도 O(1)
*
* 그리디 알고리즘
* 현재까지의 최저 가격을 기억하고, 그 가격에 샀을 때의 이익을 계속 계산하여 최대 이익을 구함
*/

/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function (prices) {
let minPrice = prices[0]; // 최저 가격 초기화 (첫 날 가격)
let maxProfit = 0; // 최대 이익 초기화 (아직 이익 없음)

// 두 번째 날부터
for (let i = 1; i < prices.length; i++) {
minPrice = Math.min(minPrice, prices[i]); // 최저 가격 갱신
maxProfit = Math.max(maxProfit, prices[i] - minPrice); // 최대 이익 갱신
}

return maxProfit;
};
38 changes: 38 additions & 0 deletions encode-and-decode-strings/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* 문자열 리스트를 하나의 문자열로 인코딩하고, 다시 디코딩 하기
*
* 접근 방법:
* 길이 기반 인코딩 => 각 문자열마다 "길이#문자열" 형식으로 인코딩
* "#"을 기준으로 문자열을 나누고, 길이를 이용해 원래 문자열을 복원하여 디코딩
*/

/**
* @param {string[]} strs - 인코딩할 문자열 리스트
* @return {string} - 인코딩된 문자열
*/
var encode = function (strs) {
if (!strs || strs.length === 0) return '';
return strs.map((str) => str.length + '#' + str).join('');
};

/**
* @param {string} s - 디코딩할 인코딩된 문자열
* @return {string[]} - 원래의 문자열 리스트
*/
var decode = function (s) {
if (!s || s.length === 0) return [];

const result = [];
let i = 0;

while (i < s.length) {
const delimiterIndex = s.indexOf('#', i); // '#'의 위치 찾기
const length = parseInt(s.slice(i, delimiterIndex)); // 길이 추출

const start = delimiterIndex + 1; // 문자열 시작 위치
result.push(s.slice(start, start + length)); // 알아낸 길이만큼 문자열 추출 후 결과에 추가
i = start + length; // 다음 문자열 시작 위치로 포인터 이동
}

return result;
};
35 changes: 35 additions & 0 deletions group-anagrams/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* 문자열 배열 strs가 주어졌을 때, 애너그램끼리 그룹화하는 문제
* 애너그램: 같은 문자를 재배열하여 만들 수 있는 단어들
*
* 접근 방법: 각 문자 출현빈도를 카운팅하는 방식
* 1. 각 문자열을 알파벳 개수로 변환하여 키를 생성
* 2. Map을 사용하여 키를 기준으로 문자열을 그룹화
* 3. Map의 값들을 배열로 변환하여 반환
*
* 시간복잡도: O(n * k) (n: 문자열 개수, k: 문자열 길이) -> 단순 카운팅
* 공간복잡도: O(n * k) -> 모든 문자열을 저장해야 하므로
*/

/**
* @param {string[]} strs
* @return {string[][]}
*/
var groupAnagrams = function (strs) {
const map = new Map();

for (let str of strs) {
const count = new Array(26).fill(0); // 알파벳 개수 초기화
Copy link
Contributor

Choose a reason for hiding this comment

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

alphabet index 를 활용하여 key 값을 만든 부분이 흥미롭습니다.
저는 sort 를 사용해서 Time Complexity 가 O(nklogk) 가 되었는데, 이 방식을 이용하면 O(nk) 로 줄일 수 있겠네요. 👍

for (let i = 0; i < str.length; i++) {
const index = str.charCodeAt(i) - 'a'.charCodeAt(0); // 알파벳 인덱스 계산
count[index]++; // 해당 알파벳 개수 증가
}
const key = count.join('#'); // 알파벳 개수를 문자열로 변환하여 키 생성
if (!map.has(key)) {
map.set(key, []); // 키가 없으면 새로운 배열 생성
}
map.get(key).push(str); // 해당 키에 문자열 추가
}

return Array.from(map.values()); // Map의 값들을 배열로 변환하여 반환
};
79 changes: 79 additions & 0 deletions implement-trie-prefix-tree/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// TrieNode 클래스 정의: 트라이의 각 노드를 표현
var TrieNode = function () {
this.children = new Map(); // 자식 노드를 Map으로 저장
this.isEnd = false; // 단어의 끝을 표시하는 플래그
};

// Trie 클래스 정의: 트라이 자료구조를 표현
var Trie = function () {
this.root = new TrieNode(); // 루트 노드
};

/**
* @param {string} word
* @return {void}
*/
Trie.prototype.insert = function (word) {
let node = this.root; // 현재 노드를 루트로 초기화

for (let i = 0; i < word.length; i++) {
const char = word[i]; // 현재 문자

// 현재 문자에 대한 자식 노드가 없으면 새로 생성
if (!node.children.has(char)) {
node.children.set(char, new TrieNode());
}

// 다음 자식 노드로 이동
node = node.children.get(char);
}
node.isEnd = true; // 단어의 끝을 표시
};

/**
* @param {string} word
* @return {boolean}
*/
Trie.prototype.search = function (word) {
const node = this._searchPrefix(word); // 주어진 단어의 노드를 찾음
return node !== null && node.isEnd === true; // 경로가 존재하고, 마지막 노드가 단어의 끝인지 확인
};

/**
* @param {string} prefix
* @return {boolean}
*/
Trie.prototype.startsWith = function (prefix) {
return this._searchPrefix(prefix) !== null; // 경로가 존재하는지만 확인
};

/**
* 헬퍼 메서드: 주어진 접두사에 대한 경로를 찾는 메서드
* @param {string} str
* @return {TrieNode|null} 경로의 마지막 노드 또는 null
* @private
*/
Trie.prototype._searchPrefix = function (str) {
let node = this.root; // 현재 노드를 루트로 초기화

// 주어진 문자열의 각 문자에 대해 노드 탐색
for (let i = 0; i < str.length; i++) {
const char = str[i]; // 현재 문자

// 현재 문자에 대한 자식 노드가 없으면 null 리턴
if (!node.children.has(char)) {
return null;
}

// 다음 자식 노드로 이동
node = node.children.get(char);
}
return node; // 경로의 마지막 노드 반환
};
/**
* Your Trie object will be instantiated and called as such:
* var obj = new Trie()
* obj.insert(word)
* var param_2 = obj.search(word)
* var param_3 = obj.startsWith(prefix)
*/
34 changes: 34 additions & 0 deletions word-break/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* 문자열 s가 주어진 단어 사전 wordDict의 단어들로 분리될 수 있는지 확인하는 문제
* 다이나믹 프로그래밍(DP)
* 시간 복잡도: O(n * k) (k: 최대 단어 길이)
* 공간 복잡도: O(n) (DP 배열 + 단어 집합)
*/

/**
* @param {string} s
* @param {string[]} wordDict
* @return {boolean}
*/
var wordBreak = function (s, wordDict) {
const n = s.length;
const dp = new Array(n + 1).fill(false); // DP 배열 초기화
dp[0] = true; // 빈 문자열은 항상 분리 가능

const wordSet = new Set(wordDict); // 단어 사전을 Set으로 변환: 검색시간 O(1)

const maxWordLength = Math.max(...wordDict.map((word) => word.length)); // 단어의 최대 길이 찾기

for (let i = 0; i <= n; i++) {
// 가능한 단어 길이만 검사
for (let j = Math.max(0, i - maxWordLength); j < i; j++) {
if (dp[j] && wordSet.has(s.substring(j, i))) {
// dp[j]가 true이고, j부터 i까지의 부분 문자열이 단어 사전에 존재하는 경우
dp[i] = true;
break;
}
}
}

return dp[n];
};
68 changes: 68 additions & 0 deletions word-search/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* 주어진 격자(board)에서 특정 단어(word)가 존재하는지 확인하는 함수
*
* 접근 방식:DFS(깊이 우선 탐색) + 백트래킹
* 시간 복잡도: O(m * n * 4^k) (m: 행, n: 열, k: 단어 길이)
* 공간 복잡도: O(m * n) (최대 깊이 m*n)
*
*/

/**
* @param {character[][]} board
* @param {string} word
* @return {boolean}
*/
var exist = function (board, word) {
const m = board.length;
const n = board[0].length;

const directions = [
[-1, 0],
[1, 0],
[0, -1],
[0, 1],
];

function dfs(row, col, index) {
if (index === word.length) {
return true;
}

if (row < 0 || row >= m || col < 0 || col >= n || board[row][col] !== word[index]) {
return false;
}

// 현재 셀 방문 표시 (임시로 변경)
const temp = board[row][col];
board[row][col] = '#'; // 방문한 셀을 특수 문자로 표시

// 네 방향 탐색
for (const [dx, dy] of directions) {
const newRow = row + dx;
const newCol = col + dy;

if (dfs(newRow, newCol, index + 1)) {
// 단어를 찾았으면 원래 값 롤백 후 true 반환
board[row][col] = temp;
return true;
}
}

// 백트래킹(현재 셀의 원래 값 롤백)
board[row][col] = temp;

return false;
}

// 격자의 모든 셀에서 시작점으로 시도
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (board[i][j] === word[0] && dfs(i, j, 0)) {
return true;
}
}
}

// 모든 시작점에서 실패하면
return false;
};