Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
31 changes: 31 additions & 0 deletions contains-duplicate/novacat25.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
시간 복잡도(Time Complexity): 평균 O(n)
공간 복잡도(Space Complexity): O(n)
풀이 알고리즘: JavaScript Set 활용

풀이 History
1트: Array.find()를 이용해 중복 여부를 탐색했으나,
반복적인 배열 탐색으로 인해 시간 초과(TLE) 발생.

2트: 배열 정렬 후 Binary Search를 활용하는 방법을 고려했으나,
정렬에 필요한 추가적인 시간 비용으로 비효율적이라고 판단.

3트: Set 자료구조를 이용한 풀이로 변경.
Set.has()를 통해 기존 데이터의 존재 여부를 평균 O(1)에 확인하고,
Set.add()를 통해 방문한 숫자를 기록.
*/

const containsDuplicate = (nums: number[]): boolean => {
const numSet = new Set()

for (let i = 0; i < nums.length; i++) {
const currentNum = nums[i]
if (numSet.has(currentNum)) {
return true
}

numSet.add(currentNum)
}

return false
}
29 changes: 29 additions & 0 deletions house-robber/novacat25.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
시간 복잡도: O(n)
공간 복잡도: O(1)
풀이 알고리즘: DP(Dynamic Programming) 이용 (Space-Optimised DP)

해설: dp[i] = max(dp[i-2] + nums[i], dp[i-1]) 로 점화식을 세울 수 있다.
dp[i-2]와 dp[i-1]만 있으면 dp[i]를 구할 수 있으므로, dp 배열을 만들지 않고 두 개의 변수(secondLast, last)만으로도 구현 가능하다.

점화식: dp[i] = max(dp[i−2] + nums[i], dp[i−1])
Dynamic Programming 기법에 의해, secondLast와 last를 다음 Iteration에서 재사용할 수 있도록 저장하여 공간 최적화(Space-Optimisation)를 활용할 수 있다.
*/
const rob = (nums: number[]): number => {
const n = nums.length

if (n === 1) return nums[0]

let secondLast = 0
let last = nums[0]

let result = -1

for (let i = 1; i < n; i++) {
result = Math.max(nums[i] + secondLast, last)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

저는 dp[i]를 i번째 집을 반드시 방문해서 훔치는 식으로 정의해서 nums[i] + max(dp[i-2], dp[i-3])로 풀었는데,
i번째 집까지의 최대값으로 정의하셨네요..
저도 dp로 풀어서 풀이가 굉장히 비슷하다고 생각했는데 정의에서 차이가 나네요
이렇게 정의하니 i-3을 볼 필요가 없어진 듯하네요

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

그렇군요..!
역시 사소한 디테일이 코드 짜는 사람마다 미세하게 달라지는 것 같긴 합니다.
당장 contains-duplicate 문제만 해도 Set과 length를 비교해서 원큐에 푸시는 분도 계셨던가 하면,
저처럼 명시적으로 로직을 쓰시는 분도 계셨던 것 같네요.

secondLast = last
last = result
}

return result
}
36 changes: 36 additions & 0 deletions longest-consecutive-sequence/novacat25.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
시간 복잡도(Time Complexity): 평균 O(n)
공간 복잡도(Space Complexity): 평균 O(n)
풀이 알고리즘: JavaScript Set 이용

풀이 History
1트: JavaScript Set을 떠올렸으나, for(const val of st) 부분에서 불필요한 부분을 탐색하여 TLE가 터졌다. (X)
2트: [...st]를 이용해 Set을 Array로 변환 후, for(const val of setArr)로 탐색하여 시간 복잡도를 개선했다. (O(n) 시간 복잡도)

기타: Duplicate identifier 'longestConsecutive' 에러가 발생하는 듯하여 novacatLongestConsecutive라는 이름으로 함수를 변경함.
*/

const novacatLongestConsecutive = (nums: number[]): number => {
const numberSet = new Set<number>()
let result = 0

for (const val of nums) {
numberSet.add(val)
}

const setArr = [...numberSet]
for (const val of setArr) {
if (!numberSet.has(val - 1)) {
let cur = val
let cnt = 0
while (numberSet.has(cur)) {
cur++
cnt++
}

result = Math.max(result, cnt)
}
}

return result
}
43 changes: 43 additions & 0 deletions top-k-frequent-elements/novacat25.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
공간 복잡도: O(n)
시간 복잡도: O(n)
풀이: nums의 length만큼 버킷을 생성하여 Bucket Sort 적용 후 빈도가 높은 순서대로 k개의 숫자를 선택한다.
풀이 History
1차: JavaScript Map + Max Heap을 이용해 k개의 숫자를 선택하는 방식으로 구현했다.(O(nlogk) 시간 복잡도) 문제 조건 상 시간 복잡도가 O(nlogn) 이하로 나와야 하므로 2차에서 Bucket Sort를 적용하여 O(n) 시간 복잡도로 구현하였다.
2차: 아래의 코드대로 Bucket Sort를 적용하여 k개의 숫자를 선택하는 방식으로 수정했다.(O(n) 시간 복잡도)
개선점: for 문을 그대로 쓰지 않고, 함수를 만들어서 재사용성을 높이는 방안도 좋을 것으로 생각됨.
*/
const getBucketSort = (freqArr: Map<number, number>, n: number): number[][] => {
// freq 값을 그대로 index로 활용하고 싶기 때문에 n+1개의 버킷을 생성한다.(index 0은 사용하지 않음)
const buckets: number[][] = Array.from({ length: n + 1 }, () => [])

for (const [num, freq] of freqArr) {
buckets[freq].push(num)
}

return buckets
}

const topKFrequent = (nums: number[], k: number): number[] => {
const answerArr: number[] = []
const freqMap = new Map<number, number>()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

파이썬의 dic에 대응되는 TS의 Map코드군요!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

네, Python에서는 dict/set, Java에서는 또 구현 자료구조 별로 HashMap/HashSet/TreeMap 등으로 세분화되어 있는 것 같고...
비슷한 개념이지만 언어별로 이런 차이가 있는 게 재미있는 것 같습니다.


for (const val of nums) {
const count = (freqMap.get(val) || 0) + 1
freqMap.set(val, count)
}

const bucketArr = getBucketSort(freqMap, nums.length)

for (let i = bucketArr.length - 1; i >= 0; i--) {
for (const num of bucketArr[i]) {
answerArr.push(num)

if (answerArr.length === k) {
return answerArr
}
}
}

return answerArr
}
34 changes: 34 additions & 0 deletions two-sum/novacat25.ts

@Chaewon-Park-STUDY Chaewon-Park-STUDY Nov 10, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

안녕하세요 @novacat25 님 :)
지난 온보딩 때도 리뷰를 맡았었는데, 이번에도 맡게 되어 반갑습니다. TS와 점점 가까워지는 중입니다...ㅎㅎ

저도 Python에서 처음엔 DFS(O(n²))로 풀었다가 dict로 O(n)까지 개선했는데, 비슷한 고민의 흔적이 담겨 있어서 반가웠어요.

Map을 Python dict와 비교해 보았습니다.

  • map.set(key, value) ↔ d[key] = value
  • map.get(key) ↔ d.get(key)
  • map.has(key) ↔ key in d

has()로 key가 있는지 확인했더라도 TS는 get()의 반환값을 여전히 number | undefined로 판단하는데, 이를 !로 확정한 이유를 옆에 주석으로 설명해 주셔서 이해하기 쉬웠습니다.

궁금한 점이 하나 있습니다
Q) if 안에서 break로 빠져나가니 별도의 else 없이 그 아래에 map.set(currentNum, i)를 두어도 될 것 같은데 else를 쓰신 이유가 있을까요?

AI 시대일수록 언어를 학습하고 코드로 소통하는 힘이 중요하다고 느끼는데, 서로 다른 언어로 같은 접근을 찾아 나가는 과정이 굉장히 의미 있어서 스터디에 참여하길 정말 잘했다는 생각이 드네요.
이번 주 과제를 정말 빠르게 수행하셨는데, 수고 많으셨습니다 💪

@novacat25 novacat25 Nov 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

안녕하세요, 리뷰 감사합니다!
저도 오히려 Python을 다룰 기회가 없어서 Python 메소드로 보니까,
저야말로 관점의 자극이 되어서 기쁩니다!

곰곰이 생각해보니 지적대로네요.
break나 return으로 강제로 빠져나가는 구문이 있으니, else 없어도 되긴 했네요..

else를 쓴 이유는 돌이켜보니 별 건 없고,
약간 조금 더 확실한 안전빵 조건으로 두고 싶지 않았나 하는 마음으로 썼습니다!

실무에서는 확실히 if문만 써서 가독성을 신경쓰긴 했지만,
코딩 테스트만 보면 조금 보수적으로 써서 그럤던 것 같네요.

물론, 조금 더 가독성을 챙긴다면 저 역시 지우는 것에는 찬성합니다..

아무튼, 이번 주도 물론이고 쭉 파이팅 해봐요!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

빠른 피드백 감사합니다🙂
저도 다른 문제 파이썬으로 풀어보고
나머지 문제들도 리뷰 남기겠습니다!🫡

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
시간 복잡도(Time Complexity): 평균 O(n)
공간 복잡도(Space Complexity): O(n)
풀이 알고리즘: JavaScript Map 활용

풀이 History
1트: 완전 탐색을 이용해 정답은 맞췄으나,
시간 복잡도가 O(n²)로 비효율적이었음.

2트: JavaScript Map을 활용하여
숫자와 인덱스를 Key-Value 형태로 관리.
보완해야 할 숫자의 존재 여부를 평균 O(1)에 탐색하여
전체 시간 복잡도를 O(n)으로 개선.
*/

const twoSum = (nums: number[], target: number): number[] => {
const numToIndexMap: Map<number, number> = new Map()

for (let i = 0; i < nums.length; i++) {
const currentNum = nums[i]
const complement = target - currentNum

const complementIndex = numToIndexMap.get(complement)

if (complementIndex !== undefined) {
return [complementIndex, i]
}

numToIndexMap.set(currentNum, i)
}

// Fallback 값으로 임의로 반환하지 않고, 예외를 발생시켜 문제 조건에 맞지 않는 경우를 명시적으로 처리함.
throw new Error("No valid pair found")
}
Loading