-
-
Notifications
You must be signed in to change notification settings - Fork 304
[dev_qorh] WEEK 02 solutions #2044
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
Changes from all commits
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,146 @@ | ||
| /** | ||
| 합산이 0이 되는 서로소 조합을 찾아야 한다.. 좌표가 다른 같은 값은 허용하되, | ||
| 같은 수로 구성된 조합은 중복을 불허한다. | ||
|
|
||
| 기본적으로 3가지 수를 중복없이 조합해서 뽑은 다음, 정렬을 사용하여 set 으로 구분할 수 있지 않을까? | ||
|
|
||
|
|
||
| function threeSum(nums: number[]): number[][] { | ||
| const arraySet = new Set<string>(); | ||
|
|
||
| for (let i = 0; i <= nums.length - 3; i++) { | ||
| for (let j = i + 1; j <= nums.length - 2; j++) { | ||
| for (let k = j + 1; k <= nums.length - 1; k++) { | ||
| if (nums[i] + nums[j] + nums[k] === 0) { | ||
| const sorted = [nums[i], nums[j], nums[k]]; | ||
| sorted.sort((a, b) => a - b); | ||
| const string = sorted.join("#"); | ||
| arraySet.add(string); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return Array.from(arraySet).map((el) => | ||
| el.split("#").map((el) => Number(el)) | ||
| ); | ||
| } | ||
| */ | ||
|
|
||
| /** | ||
| 시간 초과해버렸다. 어떻게 효율적으로 풀 수 있을까? | ||
| two sum 을 보니 해시테이블을 쓴다고 한다. nums 를 해시테이블로 변환하고, | ||
| 이중 반복문에서 이를 활용해보자. | ||
|
|
||
| function threeSum(nums: number[]): number[][] { | ||
| const arraySet = new Set<string>(); | ||
| const hash = new Map<number, number[]>(); | ||
| nums.forEach((val, idx) => { | ||
| const idxes = hash.get(val); | ||
| if (idxes && idxes.length) { | ||
| hash.set(val, [...idxes, idx]); | ||
| } else { | ||
| hash.set(val, [idx]); | ||
| } | ||
| }); | ||
|
|
||
| for (let i = 0; i < nums.length - 2; i++) { | ||
| for (let j = i + 1; j < nums.length - 1; j++) { | ||
| const idxes = hash.get(0 - (nums[i] + nums[j])); | ||
|
|
||
| idxes?.forEach((idx) => { | ||
| if (idx != i && idx != j) { | ||
| const sorted = [nums[i], nums[j], nums[idx]]; | ||
| sorted.sort((a, b) => a - b); | ||
| const string = sorted.join("#"); | ||
| arraySet.add(string); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| return Array.from(arraySet).map((el) => | ||
| el.split("#").map((el) => Number(el)) | ||
| ); | ||
| } | ||
|
|
||
| */ | ||
|
|
||
| /** | ||
| ... 0으로만 가득찬 배열에서 time limit 이 발생했다. | ||
| 풀이를 보고오니, 위 로직에서 0으로 가득찬 arraySet 을 순회하는 곳에서 결국 O(n^3) 이 되어버린다는 점을 확인할 수 있었다. | ||
| 확실한 것은, 정렬을 추가했을 경우 위 로직도 통과 가능한 정도로 만들어볼 수 있지 않을까 싶었다. | ||
|
|
||
| function threeSum(nums: number[]): number[][] { | ||
| nums.sort((a, b) => a - b); | ||
| const arraySet = new Set<string>(); | ||
| const hash = new Map<number, number[]>(); | ||
|
|
||
| nums.forEach((val, idx) => { | ||
| const idxes = hash.get(val); | ||
| if (idxes && idxes.length) { | ||
| hash.set(val, [...idxes, idx]); | ||
| } else { | ||
| hash.set(val, [idx]); | ||
| } | ||
| }); | ||
|
|
||
| for (let i = 0; i < nums.length - 2; i++) { | ||
| for (let j = i + 1; j < nums.length - 1; j++) { | ||
| const idxes = hash.get(0 - (nums[i] + nums[j])) ?? []; | ||
|
|
||
| for (let k = 0; k < idxes.length; k++) { | ||
| const idx = idxes[k]; | ||
| if (idx != i && idx != j) { | ||
| const sorted = [nums[i], nums[j], nums[idx]]; | ||
| sorted.sort((a, b) => a - b); | ||
| const string = sorted.join("#"); | ||
| arraySet.add(string); | ||
|
|
||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return Array.from(arraySet).map((el) => | ||
| el.split("#").map((el) => Number(el)) | ||
| ); | ||
| } | ||
|
|
||
| 근데 어떻게든 못만듦. | ||
| */ | ||
|
|
||
| /** | ||
| 어제 해답을 보았으니, 다시한번 풀어보자. | ||
| 핵심은 투 포인터를 사용하는 것, 자료구조에 얽메이지 않는 것 | ||
| */ | ||
|
|
||
| function threeSum(nums: number[]): number[][] { | ||
| nums.sort((a, b) => a - b); | ||
| const results = []; | ||
|
|
||
| // [-4, -1, -1, 0, 1, 2] | ||
| for (let i = 0; i < nums.length; i++) { | ||
| while (nums[i - 1] === nums[i]) i++; | ||
|
|
||
| let low = i + 1; | ||
| let high = nums.length - 1; | ||
| while (low < high) { | ||
| const sum = nums[i] + nums[low] + nums[high]; | ||
| if (sum < 0) { | ||
| low++; | ||
| } else if (sum > 0) { | ||
| high--; | ||
| } else { | ||
| while (nums[high] === nums[high - 1]) high--; | ||
| while (nums[low] === nums[low + 1]) low++; | ||
| results.push([nums[i], nums[low], nums[high]]); | ||
| low++; | ||
| high--; | ||
|
Comment on lines
+139
to
+140
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. 저는 처음에 여기서 한번 답을 찾았으면 break 로 끝내려고 했었는데 그렇게 되면 동일한 i 에 대해서 더 찾을 수 가 없는 예외케이스가 생기더라구요. 이 부분도 저는 문제풀면서 알게되었습니다 ㅎㅎ |
||
| } | ||
| } | ||
| } | ||
|
|
||
| return results; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /** | ||
| 4와 5를 각각 구하면 | ||
|
|
||
| 1+1+1+1 | ||
| 1+1+2 | ||
| 1+2+1 | ||
| 2+1+1 | ||
| 2+2 | ||
| => 5 | ||
|
|
||
| 1+1+1+1+1 | ||
| 1+1+1+2 | ||
| 1+1+2+1 | ||
| 1+2+1+1 | ||
| 2+1+1+1 | ||
| 1+2+2 | ||
| 2+1+2 | ||
| 2+2+1 | ||
| => 8 | ||
|
|
||
| 왜 피보나치 수열 같을까? 일단 그렇게 값을 계산해보자. | ||
|
|
||
| 시간 복잡도: O(n) | ||
| 공간 복잡도: O(n) | ||
|
|
||
| 근데 왜 피보나치인지는 생각해보고 업데이트 하기.. | ||
|
Comment on lines
+21
to
+26
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. 오 저도 똑같은 생각으로 접근했는데 제 결론은 이런 문제가 나왔을 때 유사한 패턴이 보이면 점화식 세우고 풀어보자! 였습니다. ㅎㅎ |
||
| */ | ||
|
|
||
| function climbStairs(n: number): number { | ||
| if (n === 1) { | ||
| return 1; | ||
| } | ||
|
|
||
| if (n === 2) { | ||
| return 2; | ||
| } | ||
|
|
||
| const results = [1, 2]; | ||
| for (let i = 2; i < n; i++) { | ||
| results.push(results[i - 2] + results[i - 1]); | ||
| } | ||
|
|
||
| return results.pop() as number; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| /** | ||
| [첫번째 시도] | ||
| O(n)의 시간 복잡도로, i 번째 수를 제외한 제곱 수 합을 구하는 문제 | ||
| nums 를 좌우에서 순회하며, result += nums[i]^2 or nums[n-i]^2 를 이어 나가되, | ||
| 두 배열을 더하는 시점에서 nums 의 배열 값을 뺴주면? | ||
|
|
||
| ... 문제를 잘못 이해해서 합인 줄 알았다.. 곱이구나 나눗셈 없이 이를 어떻게 구한담 | ||
|
|
||
| 일단 nums 에 0이 두개 있으면 정답 배열의 모든 수는 0이다. | ||
| 그 외에는 곱셈을 유지해야 하는데.. | ||
|
|
||
| 두 배열을 만들어서, 전체 수 곱 행렬을 만들어 보자 => 실패 | ||
|
|
||
| 수식으로 나타내보면? | ||
|
|
||
| f(n) = n번째 수 제외 전체 곱 | ||
| f(0) = f(1) * ... * f(n) | ||
| f(1) = f(0) * f(2) * ... * f(n) | ||
| f(n-1) = f(0) * ... * f(n-2) * f(n) | ||
|
|
||
| 모르겠다.. 오늘은 해설 보고 내일 기억해서 풀어보자 | ||
| function productExceptSelf(nums: number[]): number[] { | ||
| const forward = [nums[0]]; | ||
| const backward = [nums[nums.length - 1]]; | ||
|
|
||
| for (let i = 1; i < nums.length; i++) { | ||
| forward.push(forward[i - 1] * nums[i]); | ||
| backward.push(backward[i - 1] * nums[nums.length - 1 - i]); | ||
| } | ||
|
|
||
| const result = []; | ||
| for (let i = 0; i < nums.length - 1; i++) { | ||
| result.push(forward[i] * backward[i]); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| */ | ||
| /** | ||
| [두번째 시도] | ||
| 접근은 맞았다. 결국 제대로 수식을 작성해야 하는 듯 | ||
| 수식을 작성해보면, | ||
| forward 는 [1, 2, 2*3, 2*3*4] | ||
| backward 는 [3*4*5, 4*5, 5, 1] 가 나와야 한다. | ||
|
|
||
| 시간 복잡도: O(2n) | ||
| 공간 복잡도: O(3n) | ||
| */ | ||
|
|
||
| function productExceptSelf(nums: number[]): number[] { | ||
| const forward = [1] | ||
| const backward = [1] | ||
| for (let i=0; i<nums.length-1; i++) { | ||
| forward.push(nums[i] * forward[i]) | ||
| backward.push(nums[nums.length-1-i] * backward[i]) | ||
|
|
||
| } | ||
|
|
||
| const result = [] | ||
| for (let i=0; i<nums.length; i++){ | ||
| result.push(forward[i] * backward[nums.length-1-i]) | ||
| } | ||
|
|
||
| return result | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /** | ||
| 각 문자열을 순회하며, 글자별로 등장한 횟수를 비교할 수 있을 듯 하다. | ||
| s 를 먼저 순회하여 생성한 map 에 대해 t 를 순회하며 map 을 소거시켜보자 | ||
|
|
||
| 시간 복잡도: O(n) | ||
| 공간 복잡도: O(log n) (문자열 수를 기준) | ||
| */ | ||
|
|
||
| function isAnagram(s: string, t: string): boolean { | ||
| const wordMap = new Map<string, number>(); | ||
| for (let i = 0; i < s.length; i++) { | ||
| const value = wordMap.get(s[i]) | ||
| if (value) { | ||
| wordMap.set(s[i], value + 1); | ||
| } else { | ||
| wordMap.set(s[i], 1); | ||
| } | ||
| } | ||
|
|
||
| for (let i = 0; i < t.length; i++) { | ||
| const remainWord = wordMap.get(t[i]); | ||
| if (remainWord) { | ||
|
|
||
| if (remainWord > 1) { | ||
| wordMap.set(t[i], remainWord - 1); | ||
| } else if (remainWord === 1) { | ||
| wordMap.delete(t[i]); | ||
| } | ||
| } else { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return wordMap.size === 0 ? true : false; | ||
| } |
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.
최대한 답변을 보시지 않고 끝까지 풀어보신다는게 아주 중요한 것 같습니다!