Repository navigation
[novacat25] WEEK 01 Solutions #2913
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
base: main
Are you sure you want to change the base?
Changes from all commits
aabda78
947a3d5
524bd76
a3fdece
ea34956
9bba1b7
8bc8abd
17ff8c2
6cbe332
3ee1310
c026b76
c4cd0de
da8b11c
57efd43
87c9e87
030e522
25efbc6
bacba45
7374068
8670adf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| } |
| 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) | ||
| secondLast = last | ||
| last = result | ||
| } | ||
|
|
||
| return result | ||
| } | ||
| 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 | ||
| } |
| 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>() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파이썬의 dic에 대응되는 TS의 Map코드군요!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 안녕하세요 @novacat25 님 :) 저도 Python에서 처음엔 DFS(O(n²))로 풀었다가 dict로 O(n)까지 개선했는데, 비슷한 고민의 흔적이 담겨 있어서 반가웠어요. Map을 Python dict와 비교해 보았습니다.
has()로 key가 있는지 확인했더라도 TS는 get()의 반환값을 여전히 number | undefined로 판단하는데, 이를 !로 확정한 이유를 옆에 주석으로 설명해 주셔서 이해하기 쉬웠습니다. 궁금한 점이 하나 있습니다 AI 시대일수록 언어를 학습하고 코드로 소통하는 힘이 중요하다고 느끼는데, 서로 다른 언어로 같은 접근을 찾아 나가는 과정이 굉장히 의미 있어서 스터디에 참여하길 정말 잘했다는 생각이 드네요.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 안녕하세요, 리뷰 감사합니다! 곰곰이 생각해보니 지적대로네요. else를 쓴 이유는 돌이켜보니 별 건 없고, 실무에서는 확실히 if문만 써서 가독성을 신경쓰긴 했지만, 물론, 조금 더 가독성을 챙긴다면 저 역시 지우는 것에는 찬성합니다.. 아무튼, 이번 주도 물론이고 쭉 파이팅 해봐요!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
| } |
There was a problem hiding this comment.
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을 볼 필요가 없어진 듯하네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇군요..!
역시 사소한 디테일이 코드 짜는 사람마다 미세하게 달라지는 것 같긴 합니다.
당장 contains-duplicate 문제만 해도 Set과 length를 비교해서 원큐에 푸시는 분도 계셨던가 하면,
저처럼 명시적으로 로직을 쓰시는 분도 계셨던 것 같네요.