Skip to content

Commit 3a7dac9

Browse files
author
changmuk.im
committed
solve : week 3 with python
(cherry picked from commit 54f0612)
1 parent 76dbe5b commit 3a7dac9

File tree

5 files changed

+358
-0
lines changed

5 files changed

+358
-0
lines changed

climbing-stairs/EGON.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from unittest import TestCase, main
2+
3+
4+
class Solution:
5+
def climbStairs(self, n: int) -> int:
6+
return self.solveWithDP(n)
7+
8+
"""
9+
Runtime: 30 ms (Beats 83.62%)
10+
Time Complexity: O(n)
11+
> 3에서 n + 1 까지 range를 조회하였으므로 O((n + 1) - 3) ~= O(n)
12+
13+
Memory: 16.39 MB (Beats 90.15%)
14+
Space Complexity: O(n)
15+
> 크기가 n + 1인 dp를 선언하여 사용했으므로 O(n + 1) ~= O(n)
16+
"""
17+
def solveWithDP(self, n: int) -> int:
18+
if n <= 2:
19+
return n
20+
21+
dp = [0] * (n + 1)
22+
dp[0], dp[1], dp[2] = 0, 1, 2
23+
for stair in range(3, n + 1):
24+
dp[stair] = dp[stair - 1] + dp[stair - 2]
25+
26+
return dp[n]
27+
28+
29+
class _LeetCodeTestCases(TestCase):
30+
def test_1(self):
31+
n = 2
32+
output = 2
33+
self.assertEqual(Solution.climbStairs(Solution(), n), output)
34+
35+
def test_2(self):
36+
n = 3
37+
output = 3
38+
self.assertEqual(Solution.climbStairs(Solution(), n), output)
39+
40+
def test_3(self):
41+
n = 1
42+
output = 1
43+
self.assertEqual(Solution.climbStairs(Solution(), n), output)
44+
45+
46+
if __name__ == '__main__':
47+
main()

coin-change/EGON.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def coinChange(self, coins: List[int], amount: int) -> int:
7+
return self.solveWithDP(coins, amount)
8+
9+
# Unbounded Knapsack Problem
10+
"""
11+
Runtime: 801 ms (Beats 48.54%)
12+
Time Complexity: O(n)
13+
- coins 길이를 c, amount의 크기를 a라고 하면
14+
- coins를 정렬하는데 O(log c)
15+
- dp 배열 조회에 O((n + 1) * c)
16+
> c의 최대 크기는 12라서 무시가능하므로 O((n + 1) * c) ~= O(n * c) ~= O(n)
17+
18+
Memory: 16.94 MB (Beats 50.74%)
19+
Space Complexity: O(n)
20+
> 크기가 n + 1인 dp를 선언하여 사용했으므로 O(n + 1) ~= O(n)
21+
"""
22+
def solveWithDP(self, coins: List[int], amount: int) -> int:
23+
if amount == 0:
24+
return 0
25+
26+
coins.sort()
27+
28+
if amount < coins[0]:
29+
return -1
30+
31+
dp = [float('inf')] * (amount + 1)
32+
33+
for coin in coins:
34+
if coin <= amount:
35+
dp[coin] = 1
36+
37+
for curr_amount in range(amount + 1):
38+
for coin in coins:
39+
if 0 <= curr_amount - coin:
40+
dp[curr_amount] = min(
41+
dp[curr_amount],
42+
dp[curr_amount - coin] + 1
43+
)
44+
45+
return dp[-1] if dp[-1] != float('inf') else -1
46+
47+
48+
class _LeetCodeTestCases(TestCase):
49+
def test_1(self):
50+
coins = [1, 2, 5]
51+
amount = 11
52+
output = 3
53+
self.assertEqual(Solution.coinChange(Solution(), coins, amount), output)
54+
55+
def test_2(self):
56+
coins = [2]
57+
amount = 3
58+
output = -1
59+
self.assertEqual(Solution.coinChange(Solution(), coins, amount), output)
60+
61+
def test_3(self):
62+
coins = [1]
63+
amount = 0
64+
output = 0
65+
self.assertEqual(Solution.coinChange(Solution(), coins, amount), output)
66+
67+
def test_4(self):
68+
coins = [1, 2147483647]
69+
amount = 2
70+
output = 2
71+
self.assertEqual(Solution.coinChange(Solution(), coins, amount), output)
72+
73+
74+
if __name__ == '__main__':
75+
main()

combination-sum/EGON.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
from collections import defaultdict
4+
5+
6+
class Solution:
7+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
8+
return self.solveWithBackTracking(candidates, target)
9+
10+
"""
11+
Runtime: 2039 ms (Beats 5.01%)
12+
Time Complexity: O(c * c * log c)
13+
- 처음 stack의 크기는 c에 비례 O(c)
14+
- 중복 제거에 사용하는 변수인 curr_visited_checker 생성에 O(c' log c')
15+
- stack의 내부 로직에서 c에 비례한 for문을 순회하는데 O(c)
16+
> O(c) * O(c' log c') + O(c) * O(c) ~= O(c * c * log c)
17+
18+
Memory: 16.81 MB (Beats 11.09%)
19+
Space Complexity: O(c * c)
20+
- curr_combination의 크기가 c에 비례
21+
- stack의 크기는 curr_combination의 크기와 c에 비례
22+
> O(c * c)
23+
"""
24+
def solveWithDFS(self, candidates: List[int], target: int) -> List[List[int]]:
25+
result = []
26+
stack = []
27+
visited = defaultdict(bool)
28+
for candidate in candidates:
29+
stack.append([[candidate], candidate])
30+
31+
while stack:
32+
curr_combination, curr_sum = stack.pop()
33+
curr_visited_checker = tuple(sorted(curr_combination))
34+
35+
if curr_sum == target and visited[curr_visited_checker] is False:
36+
visited[curr_visited_checker] = True
37+
result.append(curr_combination)
38+
39+
if target < curr_sum:
40+
continue
41+
42+
for candidate in candidates:
43+
post_combination, post_sum = curr_combination + [candidate], curr_sum + candidate
44+
stack.append([post_combination, post_sum])
45+
46+
return result
47+
48+
"""
49+
Runtime: 58 ms (Beats 32.30%)
50+
Time Complexity: O(c * c)
51+
- candidates 정렬에 O(log c)
52+
- 첫 depte에서 dfs 함수 호출에 O(c)
53+
- 그 후 candidates의 길이에 비례해서 재귀적으로 dfs를 호출하는데 O(c)
54+
- lower_bound_idx에 따라 range가 감소하기는 하나 일단은 비례 O(c')
55+
> O(log c) + O(c * c') ~= O(c * c), 단 c' <= c 이므로 이 복잡도는 upper bound
56+
Memory: 16.59 MB (Beats 75.00%)
57+
Space Complexity: O(c)
58+
- result를 제외하고 모두 nonlocal 변수를 call by reference로 참조
59+
- dfs 함수 호출마다 메모리가 증가하는데, 호출횟수는 candidates의 길이에 비례 O(c)
60+
- lower_bound_idx에 따라 range가 감소하기는 하나 일단은 비례
61+
> O(c), 단 이 복잡도는 upper bound
62+
"""
63+
def solveWithBackTracking(self, candidates: List[int], target: int) -> List[List[int]]:
64+
def dfs(stack: List[int], sum: int, lower_bound_idx: int):
65+
nonlocal result, candidates, target
66+
67+
if target < sum:
68+
return
69+
elif sum < target:
70+
for idx in range(lower_bound_idx, len(candidates)):
71+
dfs(stack + [candidates[idx]], sum + candidates[idx], idx)
72+
else: # target == sum
73+
result.append(stack)
74+
return
75+
76+
result = []
77+
candidates.sort()
78+
dfs([], 0, 0)
79+
return result
80+
81+
82+
class _LeetCodeTestCases(TestCase):
83+
def test_1(self):
84+
candidates = [2, 3, 6, 7]
85+
target = 7
86+
output = [[2, 2, 3], [7]]
87+
self.assertEqual(Solution.combinationSum(Solution(), candidates, target), output)
88+
89+
def test_2(self):
90+
candidates = [2, 3, 5]
91+
target = 8
92+
output = [[2, 2, 2, 2], [2, 3, 3], [3, 5]]
93+
self.assertEqual(Solution.combinationSum(Solution(), candidates, target), output)
94+
95+
def test_3(self):
96+
candidates = [2]
97+
target = 1
98+
output = []
99+
self.assertEqual(Solution.combinationSum(Solution(), candidates, target), output)
100+
101+
102+
if __name__ == '__main__':
103+
main()

product-of-array-except-self/EGON.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def productExceptSelf(self, nums: List[int]) -> List[int]:
7+
return self.solve_with_no_extra_space(nums)
8+
9+
"""
10+
Runtime: 290 ms (Beats 12.23%)
11+
Time Complexity: O(n)
12+
- 1부터 n까지 순회했으므로 O(n - 1)
13+
- 1부터 n + 1 까지 순회했으므로 O(n)
14+
> O(n - 1) + O(n) ~= O(2n) ~= O(n)
15+
16+
Memory: 25.68 MB (Beats 83.82%)
17+
Space Complexity: O(n)
18+
> 크기가 n + 2인 배열 2개를 사용했으므로 O(2 * (n + 2)) ~= O(n)
19+
"""
20+
def solve_with_prefix_and_suffix(self, nums: List[int]) -> List[int]:
21+
forward_product = nums[:]
22+
reverse_product = nums[:]
23+
for i in range(1, len(nums)):
24+
forward_product[i] *= forward_product[i - 1]
25+
reverse_product[len(nums) - i - 1] *= reverse_product[len(nums) - i]
26+
forward_product = [1] + forward_product + [1]
27+
reverse_product = [1] + reverse_product + [1]
28+
29+
return [forward_product[i - 1] * reverse_product[i + 1] for i in range(1, len(nums) + 1)]
30+
31+
32+
class _LeetCodeTestCases(TestCase):
33+
def test_1(self):
34+
nums = [1, 2, 3, 4]
35+
output = [24, 12, 8, 6]
36+
self.assertEqual(Solution.productExceptSelf(Solution(), nums), output)
37+
38+
def test_2(self):
39+
nums = [-1, 1, 0, -3, 3]
40+
output = [0, 0, 9, 0, 0]
41+
self.assertEqual(Solution.productExceptSelf(Solution(), nums), output)
42+
43+
44+
if __name__ == '__main__':
45+
main()

two-sum/EGON.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
from collections import defaultdict
4+
5+
6+
class Solution:
7+
def twoSum(self, nums: List[int], target: int) -> List[int]:
8+
return self.solveWithMemoization(nums, target)
9+
10+
"""
11+
Runtime: 3762 ms (Beats 5.00%)
12+
Time Complexity: O(n ** 2)
13+
> 크기가 n인 nums 배열을 2중으로 조회하므로 O(n ** 2)
14+
15+
Memory: 17.42 MB (Beats 61.58%)
16+
Space Complexity: O(1)
17+
> 딱히 저장하는 변수 없음 (반환하는 list 제외)
18+
"""
19+
def solveWithBruteForce(self, nums: List[int], target: int) -> List[int]:
20+
for i in range(len(nums)):
21+
for j in range(len(nums)):
22+
if i != j and nums[i] + nums[j] == target:
23+
return [i, j]
24+
25+
"""
26+
Runtime: 52 ms (Beats 89.73%)
27+
Time Complexity: O(n)
28+
1. nums 배열을 돌며 idx를 저장하는 dict 생성에 O(n)
29+
2. 첫 숫자를 선택하기 위해 len(nums)를 for문으로 조회하는데 O(n)
30+
> O(2n) ~= O(n)
31+
32+
Memory: 19.96 MB (Beats 8.42%)
33+
Space Complexity: O(n)
34+
- 크기가 n인 defaultdict 변수 사용
35+
"""
36+
def solveWithMemoization(self, nums: List[int], target: int) -> List[int]:
37+
num_to_idx_dict = defaultdict(list)
38+
for idx, num in enumerate(nums):
39+
num_to_idx_dict[num].append(idx)
40+
41+
for i in range(len(nums)):
42+
first_num = nums[i]
43+
second_num = target - nums[i]
44+
45+
if first_num != second_num:
46+
if not (len(num_to_idx_dict[first_num]) and len(num_to_idx_dict[second_num])):
47+
continue
48+
else:
49+
if not (2 <= len(num_to_idx_dict[first_num])):
50+
continue
51+
52+
first_idx = num_to_idx_dict[first_num].pop()
53+
second_idx = num_to_idx_dict[second_num].pop()
54+
55+
if first_num != second_num:
56+
return [first_idx, second_idx]
57+
else:
58+
return [second_idx, first_idx]
59+
60+
61+
class _LeetCodeTestCases(TestCase):
62+
def test_1(self):
63+
nums = [2, 7, 11, 15]
64+
target = 9
65+
output = [0, 1]
66+
self.assertEqual(Solution.twoSum(Solution(), nums, target), output)
67+
68+
def test_2(self):
69+
nums = [3,2,4]
70+
target = 6
71+
output = [1, 2]
72+
self.assertEqual(Solution.twoSum(Solution(), nums, target), output)
73+
74+
def test_3(self):
75+
nums = [3, 3]
76+
target = 6
77+
output = [0, 1]
78+
self.assertEqual(Solution.twoSum(Solution(), nums, target), output)
79+
80+
def test_4(self):
81+
nums = [3, 2, 3]
82+
target = 6
83+
output = [0, 2]
84+
self.assertEqual(Solution.twoSum(Solution(), nums, target), output)
85+
86+
87+
if __name__ == '__main__':
88+
main()

0 commit comments

Comments
 (0)