-
-
Notifications
You must be signed in to change notification settings - Fork 361
[JinuCheon] WEEK 02 solutions #2685
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
5768268
45f13c4
be11c1c
b174a0f
b606831
794ad7d
37dc7f9
33a3efe
69451cb
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,87 @@ | ||
| # brute force 로 풀어본 답안. O(n3) 다. 역시 timeout. | ||
| # dic, set 자료형 익히기 겸 해봤다. 파이썬 편하다 최고. | ||
| class Solution: | ||
| def threeSum(self, nums: list[int]) -> list[list[int]]: | ||
| n = len(nums) | ||
| result = set() | ||
|
|
||
| for i in range(n): | ||
| for j in range(n): | ||
| for k in range(n): | ||
| if (i == j or j == k or i == k): | ||
| continue | ||
| if (nums[i] + nums[j] + nums[k] == 0): | ||
| result.add(tuple(sorted([nums[i], nums[j], nums[k]]))) | ||
|
|
||
| return list(result) | ||
|
|
||
| # LLM 에게 힌트 얻어서 진행. | ||
| # i != j, i != k, j != k 조건 때문에 정렬을 하면 안된다고 착각하고 있었다. 사실 고정된 순서는 중요하지 않음. | ||
| # 정렬을 진행하고, two pointer 로 진행. | ||
| # 통과! | ||
| class Solution2: | ||
| def threeSum(self, nums: list[int]) -> list[list[int]]: | ||
| # 정렬 | ||
| nums.sort() | ||
| n = len(nums) | ||
| result = set() | ||
|
|
||
| for i in range(n): | ||
| # i 자리 중복 skip (이전 원소와 같으면 건너뜀) | ||
| if i > 0 and nums[i] == nums[i - 1]: | ||
| continue | ||
|
|
||
| # i + left + right === 0 이 되어야 함. | ||
| left = i + 1 | ||
| right = n - 1 | ||
|
|
||
| while left < right: | ||
| if nums[left] + nums[right] == -nums[i]: | ||
| result.add(tuple([nums[i], nums[left], nums[right]])) | ||
| left += 1 | ||
| right -= 1 | ||
|
|
||
| elif nums[left] + nums[right] < -nums[i]: | ||
| left += 1 | ||
| else: | ||
| right -= 1 | ||
|
|
||
| return list(result) | ||
|
|
||
| # LLM의 피드백. | ||
| # 중복을 스킵하는 효율적인 방법이 있었음. | ||
| class Solution: | ||
| def threeSum(self, nums: list[int]) -> list[list[int]]: | ||
| nums.sort() | ||
| n = len(nums) | ||
| result = [] | ||
|
|
||
| for i in range(n): | ||
| if i > 0 and nums[i] == nums[i - 1]: | ||
| continue | ||
|
|
||
| if nums[i] > 0: | ||
| break | ||
|
|
||
| left, right = i + 1, n - 1 | ||
| target = -nums[i] | ||
|
|
||
| while left < right: | ||
| s = nums[left] + nums[right] | ||
| if s == target: | ||
| result.append([nums[i], nums[left], nums[right]]) | ||
| left += 1 | ||
| right -= 1 | ||
|
|
||
| # left, right 자리도 중복 skip | ||
| while left < right and nums[left] == nums[left - 1]: | ||
| left += 1 | ||
| while left < right and nums[right] == nums[right + 1]: | ||
| right -= 1 | ||
|
|
||
| elif s < target: | ||
| left += 1 | ||
| else: | ||
| right -= 1 | ||
|
|
||
| return result |
|
Contributor
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. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 1:
|
| 복잡도 | |
|---|---|
| Time | O(n) |
| Space | O(n) |
피드백: 메모이제이션/DP를 사용해 피보나치 계열의 계단 수를 선형 시간에 계산합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2: Solution2.climbStairs — Time: ❌ O(1) → O(n) / Space: O(n)
| 유저 분석 | 실제 분석 | 결과 | |
|---|---|---|---|
| Time | O(1) | O(n) | ❌ |
| Space | - | O(n) | - |
피드백: 해당 접근은 이미 계산된 값 재사용으로 중복 계산을 피합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 3: Solution.climbStairs — Time: O(2^n) / Space: O(n)
| 복잡도 | |
|---|---|
| Time | O(2^n) |
| Space | O(n) |
피드백: 입력 크기에 따라 지수 시간 복잡도와 깊은 재귀 스택이 발생합니다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # 재귀를 만드는 것이 잘 생각나지 않아서, 이렇게 저렇게 시도해보다 결국 LLM 도움. | ||
| # 그렇지만 제출 결과 Time Out. | ||
| # 2^N 복잡도임. max 45 의 경우 35,184,372,088,832. | ||
| class Solution: | ||
| def climbStairs(self, n: int) -> int: | ||
| # 1 -> 한칸만 가능 | ||
| # 2 -> (1,1) or (2) | ||
| if n <= 2: | ||
| return n; | ||
|
|
||
| # 1칸 진행한 케이스 + 2칸 진행한 케이스 | ||
| return self.climbStairs(n-1) + self.climbStairs(n-2); | ||
|
|
||
| # memonization 적용. 실무를 하고나서 이걸 보니, 캐싱이라고 부르고 싶다. | ||
| # 1~N 숫자 하나당 한번의 계산을 하게 되니, 시간복잡도는 O(1) 이다. | ||
| # 이게 왜 easy 난이도지? 잊어먹었다가 주말에 자력으로 풀어보자. | ||
| class Solution: | ||
| def climbStairs(self, n: int) -> int: | ||
| self.memo = {} | ||
| return self.dfs(n) | ||
|
|
||
| def dfs(self, n: int) -> int: | ||
| if n <= 2: | ||
| return n | ||
|
|
||
| # 이미 해당 수를 계산한 적이 있다면 early return. | ||
| if n in self.memo: | ||
| return self.memo[n] | ||
|
|
||
| # 새로운 결과가 있으면 무조건 저장. | ||
| self.memo[n] = self.dfs(n - 1) + self.dfs(n - 2) | ||
|
Contributor
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. 이 부분이 핵심인데요.
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. @yuseok89 유석님 감사합니다! 따뜻한 리뷰 감사합니다 :) # yuseok89 님 피드백 반영.
# 점화식(recursive relation): 수열의 항을 그 이전 항들을 이용해서 정의하는 식.
# 함수 호출 오버헤드 제거 & 안정성
class Solution3:
def climbStairs(self, n: int) -> int:
if n < 2:
return n
dp = [0] * (n+1)
# set init values
dp[1] = 1
dp[2] = 2
# i 번째 계단 오르는 경우의 수: 1칸 + 2칸 경우의 수의 합
for i in range(3, n+1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]; |
||
| return self.memo[n] | ||
|
|
||
|
|
||
| # yuseok89 님 피드백 반영. | ||
| # 점화식(recursive relation): 수열의 항을 그 이전 항들을 이용해서 정의하는 식. | ||
| # 함수 호출 오버헤드 제거 & 안정성 | ||
| class Solution3: | ||
| def climbStairs(self, n: int) -> int: | ||
| if n < 2: | ||
| return n | ||
|
|
||
| dp = [0] * (n+1) | ||
|
|
||
| # set init values | ||
| dp[1] = 1 | ||
| dp[2] = 2 | ||
|
|
||
| # i 번째 계단 오르는 경우의 수: 1칸 + 2칸 경우의 수의 합 | ||
| for i in range(3, n+1): | ||
| dp[i] = dp[i - 1] + dp[i - 2] | ||
|
|
||
| return dp[n]; | ||
|
Contributor
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. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 좌우 누적곱을 이용해 2-pass로 제조합을 구하는 최적화된 풀이를 제시합니다. 개선 제안: 현재 구현이 적절해 보입니다.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # 처음에 직관적으로 떠오른 O(n2) 풀이. | ||
| class Solution: | ||
| def productExceptSelf(self, nums: List[int]) -> List[int]: | ||
| n = len(nums) | ||
| result = [1] * n | ||
| for i in range(n): | ||
| for j in range(n): | ||
| if i == j: | ||
| continue | ||
| result[j] *= nums[i] | ||
|
|
||
| return result | ||
|
|
||
| # 고민하다가 LLM 에게 힌트 달라고 질문. | ||
| # 좌 / 우 누적곱으로 처리하는 방식. | ||
| class Solution: | ||
| def productExceptSelf(self, nums: List[int]) -> List[int]: | ||
| n = len(num) | ||
| result = [1] * n | ||
|
|
||
| # 왼쪽 누적곱을 result에 미리 채워둠 | ||
| cumulativeLeftSum = 1 | ||
| for i in range(n): | ||
| result[i] = cumulativeLeftSum | ||
| cumulativeLeftSum *= nums[i] | ||
|
|
||
| # 오른쪽 누적곱을 곱해나가면서 완성 | ||
| cumulativeRightSum = 1 | ||
| for i in range(n - 1, -1, -1): | ||
| result[i] *= cumulativeRightSum | ||
| cumulativeRightSum *= nums[i] | ||
|
|
||
| return result |
|
Contributor
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. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 1:
|
| 복잡도 | |
|---|---|
| Time | O(n) |
| Space | O(Alphabet) |
피드백: 두 문자열의 문자 빈도수를 카운트하는 간단하고 직관적인 방법입니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2: Solution2.isAnagram — Time: O(n) / Space: O(k)
| 복잡도 | |
|---|---|
| Time | O(n) |
| Space | O(k) |
피드백: s에서 각 문자를 증가시키고 t에서 감소시키며 최종 값이 모두 0인지 확인합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 3: Solution.isAnagram — Time: O(n log n) / Space: O(1)
| 복잡도 | |
|---|---|
| Time | O(n log n) |
| Space | O(1) |
피드백: 정렬 시간을 포함하면 일반적으로 느린 편이지만 직관적입니다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # my own solution | ||
| # O(n) | ||
| class Solution: | ||
| def isAnagram(self, s: str, t: str) -> bool: | ||
| count1 = {} | ||
| for ch in s: | ||
| count1[ch] = count1.get(ch, 0) + 1 | ||
|
|
||
| count2 = {} | ||
| for ch in t: | ||
| count2[ch] = count2.get(ch, 0) + 1 | ||
|
|
||
| return count1 == count2 | ||
|
|
||
| # gpt suggestion | ||
| ### half space | ||
| #### O(n) | ||
| class Solution2: | ||
| def isAnagram(self, s: str, t: str) -> bool: | ||
| count = {} | ||
| for ch in s: | ||
| count[ch] = count.get(ch, 0) + 1 | ||
| for ch in t: | ||
| count[ch] = count.get(ch, 0) - 1 | ||
| return all(v == 0 for v in count.values()) | ||
|
|
||
| ### simple solution (What??) | ||
| #### O(n) | ||
| #### Counter works like my own solution internally. But I'm ganna forgot this, becuase I'm learning now. | ||
| from collections import Counter | ||
| class Solution3: | ||
| def isAnagram(self, s: str, t: str) -> bool: | ||
| return Counter(s) == Counter(t) | ||
|
|
||
| ### using sort | ||
| #### O(n log n) | ||
| class Solution: | ||
| def isAnagram(self, s: str, t: str) -> bool: | ||
| return sorted(s) == sorted(t) |
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.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 정렬 후 각 i에 대해 왼쪽/오른쪽 포인터를 이동시키며 중복을 건너뛰는 방식으로 시간복잡도를 O(n^2)로 줄였고, 중복 조합을 제거하기 위한 로직이 포함되어 있습니다.
개선 제안: 현재 구현이 적절해 보입니다.