Skip to content

Commit ae891a0

Browse files
authored
Merge pull request #1735 from jongwanra/main
[jongwanra] WEEK 02 solutions
2 parents a68ca36 + affba32 commit ae891a0

File tree

5 files changed

+373
-0
lines changed

5 files changed

+373
-0
lines changed

3sum/jongwanra.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
[Problem]
3+
https://leetcode.com/problems/3sum/description/
4+
5+
[Brainstorm]
6+
3 <= nums.length <= 3,000
7+
Brute Force: O(N^3) => 27,000,000,000 => time limited
8+
O(N^2)의 풀이는 시간 제한에 걸리지 않을 것으로 보인다. 3,000 * 3,000 => 9,000,000
9+
10+
[Plan]
11+
1. map을 설정한다. key = elements value: index list
12+
2. nested-loop을 순회한다.
13+
2-1. i == j인 경우 제외
14+
2-2. map에서 동일한 인덱스인 경우 제외하고 구한다.
15+
16+
"""
17+
18+
from typing import List
19+
20+
21+
class Solution:
22+
"""
23+
Attempt-1 My solution (incorrect)
24+
time limited
25+
"""
26+
27+
def threeSum1(self, nums: List[int]) -> List[List[int]]:
28+
map = {}
29+
for index in range(len(nums)):
30+
values = map.get(nums[index], [])
31+
values.append(index)
32+
map[nums[index]] = values
33+
34+
triplets = set()
35+
for i in range(len(nums)):
36+
for j in range(len(nums)):
37+
if i == j:
38+
continue
39+
complement = -(nums[i] + nums[j])
40+
# print(f"nums[{i}]={nums[i]}, nums[{j}]={nums[j]} , complement={complement}")
41+
if complement in map:
42+
values = map[complement]
43+
for k in values:
44+
if k == i or k == j:
45+
continue
46+
triplets.add(tuple(sorted([nums[i], nums[j], nums[k]])))
47+
return list(triplets)
48+
49+
"""
50+
Attempt-2 Another solution
51+
ref: https://www.algodale.com/problems/3sum/
52+
53+
"""
54+
55+
def threeSum2(self, nums: List[int]) -> List[List[int]]:
56+
triplets = set()
57+
58+
for index in range(len(nums) - 2):
59+
seen = set()
60+
for j in range(index + 1, len(nums)):
61+
complement = -(nums[index] + nums[j])
62+
if complement in seen:
63+
triplet = [nums[index], nums[j], complement]
64+
triplets.add(tuple(sorted(triplet)))
65+
seen.add(nums[j])
66+
67+
return list(triplets)
68+
69+
"""
70+
Attempt-3 Another solution
71+
ref: https://www.algodale.com/problems/3sum/
72+
73+
[Plan]
74+
two-pointer로 접근한다.
75+
76+
[Complexity]
77+
N: nums.length
78+
Time: O(N^2)
79+
Space: O(1)
80+
"""
81+
82+
def threeSum3(self, nums: List[int]) -> List[List[int]]:
83+
triplets = set()
84+
nums.sort()
85+
86+
for index in range(len(nums) - 2):
87+
left = index + 1
88+
right = len(nums) - 1
89+
90+
while left < right:
91+
three_sum = nums[index] + nums[left] + nums[right]
92+
if three_sum < 0:
93+
left += 1
94+
continue
95+
if three_sum > 0:
96+
right -= 1
97+
continue
98+
triplets.add((nums[index], nums[left], nums[right]))
99+
left += 1
100+
right -= 1
101+
return list(triplets)
102+
103+
104+
sol = Solution()
105+
print(sol.threeSum3([-1, 0, 1, 2, -1, 4]))
106+

climbing-stairs/jongwanra.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""
2+
[Problem]
3+
https://leetcode.com/problems/climbing-stairs/
4+
5+
[Brainstorming]
6+
한 번에 갈 수 있는 계단: 1 or 2
7+
갈 수 있는 경우의 수를 구해야 하는 문제
8+
constraints: 1 <= n <= 45
9+
10+
n = 1, 1
11+
n = 2, 2
12+
n = 3, 3
13+
n = 4, 5
14+
...
15+
f(n) = f(n - 1) + f(n - 2)
16+
17+
[Plan]
18+
1. n + 1 크기의 list를 만든다.
19+
2. list[1] = 1, list[2] = 2를 대입한다.
20+
3. for-loop를 3부터 n까지 순회한다.
21+
3-1. Bottom-Top 방식으로 n까지 값을 채워간다.
22+
4. n값을 반환한다.
23+
24+
[Complexity]
25+
Time: O(n)
26+
Space: O(n)
27+
"""
28+
29+
30+
class Solution:
31+
def climbStairs(self, n: int) -> int:
32+
answer = [0, 1, 2]
33+
for index in range(3, n + 1):
34+
answer.append(answer[index - 1] + answer[index - 2])
35+
36+
return answer[n]
37+
38+
"""
39+
another solution
40+
ref: https://www.algodale.com/problems/climbing-stairs/
41+
[Summary]
42+
Bottom Top 방식인데 공간을 최적화하여 접근
43+
[Complexity]
44+
Time: O(n)
45+
Space: O(1) - Space Optimization
46+
47+
[Plan]
48+
1. prev = 1, cur = 2를 저장한다.
49+
2. for-loop를 순회한다. 3 to n + 1
50+
51+
"""
52+
53+
def climbStairs2(self, n: int) -> int:
54+
if n <= 3:
55+
return n
56+
57+
prev, cur = [2, 3]
58+
for index in range(4, n + 1):
59+
tmp = cur
60+
cur = cur + prev
61+
prev = tmp
62+
return cur
63+
64+
"""
65+
another solution
66+
ref: https://www.algodale.com/problems/climbing-stairs/
67+
[Summary]
68+
Top-Bottom으로 재귀적으로 접근
69+
[Complexity]
70+
Time: O(n)
71+
Space: O(n)
72+
"""
73+
74+
def climbStairs3(self, n: int) -> int:
75+
cache = {}
76+
77+
def dfs(num: int) -> int:
78+
nonlocal cache
79+
if num <= 3:
80+
return num
81+
if num in cache:
82+
return cache[num]
83+
cache[num] = dfs(num - 1) + dfs(num - 2)
84+
return cache[num]
85+
86+
return dfs(n)
87+
88+
89+
sol = Solution()
90+
91+
# Normal Case
92+
print(sol.climbStairs3(2) == 2)
93+
print(sol.climbStairs3(3) == 3)
94+
print(sol.climbStairs3(4) == 5)
95+
print(sol.climbStairs3(5) == 8)
96+
print(sol.climbStairs3(38))
97+
# Edge Case
98+
print(sol.climbStairs3(1) == 1)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
[Problem]
3+
https://leetcode.com/problems/product-of-array-except-self/
4+
5+
배열 nums가 주어졌을 때, 배열 answer를 반환해라.
6+
answer[i]는 nums[i]를 제외한 모든 요소들의 곱이어야 한다.
7+
8+
[Brainstorming]
9+
전체 요소들의 곱을 구한다. 해당 요소만 나눗셈으로 제외한다.
10+
-> 이 방식은 다루기 어렵다. 0이 들어갈 수 있음.
11+
12+
Brute Force 방식으로 밖에 떠오르지 않음..
13+
O(n)으로 어떻게 풀 수 있지?
14+
15+
"""
16+
from typing import List
17+
class Solution:
18+
"""
19+
another solution
20+
ref: https://www.algodale.com/problems/product-of-array-except-self/
21+
22+
[Brainstorming]
23+
각 인덱스 요소를 제외한 요소들의 곱은 아래와 같다.
24+
nums[0] * nums[1] * ... * nums[index - 1] * nums[index + 1] * ... nums[len(nums) - 1]
25+
즉, nums[index] 전의 곱을 누적한 배열과, nums[index] 이후의 곱을 누적한 배열을 구하면 답을 구할 수 있다.
26+
27+
[Complexity]
28+
N: nums.length
29+
Time: O(N)
30+
Space: O(N)
31+
"""
32+
def productExceptSelf(self, nums: List[int]) -> List[int]:
33+
before_products = [1] * len(nums)
34+
for index in range(1, len(nums)):
35+
before_products[index] = before_products[index - 1] * nums[index- 1]
36+
37+
after_products = [1] * len(nums)
38+
for index in range(len(nums) - 2, -1, -1):
39+
after_products[index] = after_products[index + 1] * nums[index + 1]
40+
41+
answer = []
42+
for index in range(len(nums)):
43+
answer.append(before_products[index] * after_products[index])
44+
45+
return answer
46+
47+
sol = Solution()
48+
print(sol.productExceptSelf([1,2,3,4]))
49+
print(sol.productExceptSelf([-1,1,0,-3,3]))
50+
print(sol.productExceptSelf([2,3,4,5]))
51+
52+

valid-anagram/jongwanra.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""
2+
[Problem]
3+
https://leetcode.com/problems/valid-anagram/
4+
5+
[Brain Storm]
6+
아나그램인 경우, true, 아니면 false 반환
7+
동일한 알파벳이 중복되어 사용될 수 있다.
8+
9+
[Plan]
10+
1. s에 대해 for-loop을 순회하며 alphabet-count 형태로 map을 만든다.
11+
2. t에 대해 for-loop을 순회한다.
12+
2-1. t의 alphabet이 alphabet-count > 0 인 경우, count -= 1을 한다.
13+
2-2. 없는 경우, false로 return 한다.
14+
3. alphabet-count 가 빈 경우 true 그렇지 않으면 false를 반환한다.
15+
16+
[Complexity]
17+
t.length = N
18+
s.length = M
19+
20+
Time: O(N + M)
21+
Space: O(M)
22+
"""
23+
24+
25+
class Solution:
26+
def isAnagram(self, s: str, t: str) -> bool:
27+
alphabet_to_count = {}
28+
for alphabet in s:
29+
alphabet_to_count[alphabet] = alphabet_to_count.get(alphabet, 0) + 1
30+
31+
for alphabet in t:
32+
count = alphabet_to_count.get(alphabet, -1)
33+
if count == -1:
34+
return False
35+
36+
count -= 1
37+
if count == 0:
38+
alphabet_to_count.pop(alphabet)
39+
else:
40+
alphabet_to_count[alphabet] = count
41+
42+
return len(alphabet_to_count) == 0
43+
44+
45+
46+
sol = Solution()
47+
# Normal Case
48+
print(sol.isAnagram("anagram", "nagaram"))
49+
print(sol.isAnagram("rat", "car"))
50+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
[Problem]
3+
https://leetcode.com/problems/validate-binary-search-tree/
4+
5+
[Brainstorming]
6+
BST가 유효한지를 검증하는 문제.
7+
1 <= number of nodes <= 10^4(100,000)
8+
9+
[Plan]
10+
1. Tree를 DFS를 이용하여 만든다.
11+
2. 실제로 입력을 넣어보며, 존재하지 않는 경우 False를 반환한다.
12+
"""
13+
from typing import Optional
14+
class TreeNode:
15+
def __init__(self, val=0, left=None, right=None):
16+
self.val = val
17+
self.left = left
18+
self.right = right
19+
20+
class Solution:
21+
"""
22+
Attempt 1 - My Solution (incorrect)
23+
이 풀이의 경우, 자기 자신과 자기 자식 노드들 까지만으로 한정한다.
24+
따라서, BST를 제대로 검증할 수 없다.
25+
root = [5,4,6,null,null,3,7] 인 경우에는 검증할 수 없다. (Edge Case)
26+
"""
27+
def isValidBST1(self, root: Optional[TreeNode]) -> bool:
28+
invalid_flag = False
29+
def dfs(node:Optional[TreeNode])->None:
30+
nonlocal invalid_flag
31+
if invalid_flag:
32+
return
33+
34+
if not node:
35+
return
36+
37+
if node.left and node.left.val > node.val:
38+
invalid_flag = True
39+
return
40+
if node.right and node.right.val < node.val:
41+
invalid_flag = True
42+
return
43+
44+
dfs(node.left)
45+
dfs(node.right)
46+
47+
dfs(root)
48+
return not invalid_flag
49+
"""
50+
Attempt 2 - Another Solution
51+
ref: https://www.algodale.com/problems/validate-binary-search-tree
52+
[Complexity]
53+
N: number of nodes in trees
54+
Time: O(N) => Traverse all nodes in the tree.
55+
Space: worst case: O(N), best case: O(log N)
56+
"""
57+
58+
def isValidBST(self, root:Optional[TreeNode])->bool:
59+
def dfs(node:Optional[TreeNode], min_limit:int, max_limit:int)->bool:
60+
if not node:
61+
return True
62+
if not (min_limit < node.val < max_limit):
63+
return False
64+
65+
return dfs(node.left, min_limit, node.val) and dfs(node.right, node.val, max_limit)
66+
67+

0 commit comments

Comments
 (0)