Skip to content

Commit

Permalink
Merge pull request #731 from dusunax/main
Browse files Browse the repository at this point in the history
[SunaDu] Week 2
  • Loading branch information
dusunax authored Dec 21, 2024
2 parents e4bf20a + b506385 commit c11f6cb
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 0 deletions.
50 changes: 50 additions & 0 deletions 3sum/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'''
# Leetcode 15. 3Sum
use **two pointers** to solve this problem.
## Time and Space Complexity
```
TC: O(n^2)
SC: O(1)
```
### TC is O(n^2):
- sorting the list = O(n log n)
- iterating through the list and using two pointers to find the sum of three numbers. = O(n^2)
### SC is O(1):
- sorting in place = O(1)
'''

class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort() # TC: O(n log n), SC: O(1)
result = [] # result are part of the output => do not count toward auxiliary (extra) space.

for i in range(len(nums)): # TC: O(n^2)
if i > 0 and nums[i] == nums[i - 1]:
continue

j = i + 1
k = len(nums) - 1
while j < k:
currSum = nums[i] + nums[j] + nums[k]

if currSum < 0:
j += 1
elif currSum > 0:
k -= 1
else:
result.append([nums[i], nums[j], nums[k]])

while j < k and nums[j] == nums[j + 1]:
j += 1
while j < k and nums[k] == nums[k - 1]:
k -= 1

j += 1
k -= 1

return result
71 changes: 71 additions & 0 deletions climbing-stairs/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'''
# Leetcode 70. Climbing Stairs
use `dynamic programming` to solve the problem.
1. Bottom-up approach
2. Top-down approach
## Time and Space Complexity
### 1. Bottom-up approach
```
TC: O(n)
SC: O(1)
```
#### TC is O(n):
- iterating with a for loop. O(n)
#### SC is O(1):
- using a constant space to store the previous two steps. O(1)
### 2. Top-down approach
```
TC: O(n)
SC: O(n)
```
#### TC is O(n):
- performing a recursive call for each step. O(n)
#### SC is O(n):
- using a memoization object to store the previous two steps. O(n)
'''

class Solution:
'''
1. Bottom-up approach
'''
def climbStairsLoop(self, n: int) -> int:
if n == 1 or n == 2:
return n

# SC: O(1)
prev2 = 1 # ways to step 0
prev1 = 2 # ways to step 1

for i in range(3, n + 1): # TC: O(n)
current = prev1 + prev2 # ways to (n-1) + (n-2)
prev2 = prev1
prev1 = current

return prev1

'''
2. Top-down approach
'''
def climbStairsRecursive(self, n: int) -> int:
memo = {} # SC: O(n)

def dp(step: int, memo: int) -> int: # TC: O(n)
if step == 1 or step == 2:
memo[step] = step
if step not in memo:
memo[step] = dp(step - 1, memo) + dp(step - 2, memo) # ways to (n-1) + (n-2)
return memo[step]

return dp(n, memo)

119 changes: 119 additions & 0 deletions construct-binary-tree-from-preorder-and-inorder-traversal/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@

'''
# Leetcode 105. Construct Binary Tree from Preorder and Inorder Traversal
use **recursive** to solve this problem.
## Time and Space Complexity
### A. recursive & change range of preorder and inorder
```
TC: O(n)
SC: O(n)
```
### B. recursive & search index (of inorder)
```
TC: O(n^2)
SC: O(n)
```
### C. recursive & hash table
```
TC: O(n)
SC: O(n)
```
'''
class Solution:
'''
A. ์žฌ๊ท€ ํ’€์ด
preorder์™€ inorder์˜ ๊ฐ๊ฐ์˜ ๋ฒ”์œ„๋ฅผ ์กฐ์ •ํ•˜์—ฌ ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑ
'''
def buildTreeA(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
def setTree(pre_left, pre_right, in_left, in_right):
# ์žฌ๊ท€ ์ข…๋ฃŒ ์กฐ๊ฑด: preorder ๋ฒ”์œ„๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ
if pre_left > pre_right:
return None

val = preorder[pre_left] # preorder์˜ ํ˜„์žฌ ๋ฃจํŠธ ๋…ธ๋“œ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
mid = TreeNode(val) # ๋ฃจํŠธ ๋…ธ๋“œ๋ฅผ ๋จผ์ € ์ƒ์„ฑ

mid_inorder = inorder_idx_map[val] # ๋ฃจํŠธ ๋…ธ๋“œ์˜ inorder ์ธ๋ฑ์Šค ๊ฐ€์ ธ์˜ค๊ธฐ
left_size = mid_inorder - in_left # ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ํฌ๊ธฐ ๊ณ„์‚ฐ

# ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ ์ƒ์„ฑ: preorder์™€ inorder์˜ ๋ฒ”์œ„๋ฅผ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ๋กœ ์กฐ์ •
mid.left = setTree(
pre_left + 1, pre_left + left_size, in_left, mid_inorder - 1
)

# ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ ์ƒ์„ฑ: preorder์™€ inorder์˜ ๋ฒ”์œ„๋ฅผ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ๋กœ ์กฐ์ •
mid.right = setTree(
pre_left + left_size + 1, pre_right, mid_inorder + 1, in_right
)

return mid # ํ˜„์žฌ ๋…ธ๋“œ ๋ฐ˜ํ™˜

# inorder๋ฅผ ๊ฐ’ -> ์ธ๋ฑ์Šค ๋งตํ•‘ํ•œ ๋”•์…”๋„ˆ๋ฆฌ ์ƒ์„ฑ - TC: O(n), SC: O(n)
inorder_idx_map = {value: idx for idx, value in enumerate(inorder)}

# ํŠธ๋ฆฌ ์ƒ์„ฑ ์‹œ์ž‘ (preorder์™€ inorder ์ „์ฒด ๋ฒ”์œ„ ์‚ฌ์šฉ) - TC: O(n), SC: O(n)
return setTree(0, len(preorder) - 1, 0, len(inorder) - 1)


'''
# B. ์žฌ๊ท€ ํ’€์ด + ๊ณต๊ฐ„ ์ตœ์ ํ™”
# ๋ ˆํผ๋Ÿฐ์Šค ๋งํฌ์˜ ํ’€์ด 2: https://www.algodale.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
# ํŠน์ง•: ์ˆœํšŒ ์‹œ๋งˆ๋‹ค ์ธ๋ฑ์Šค๋ฅผ ์ฐพ๋Š” ๊ณผ์ •์ด ์žˆ์Œ
'''
def buildTreeB(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
# pre: ํ˜„์žฌ preorder์—์„œ ํ™•์ธํ•  ์ธ๋ฑ์Šค
# start, end: inorder์—์„œ ์‚ฌ์šฉํ•  ์‹œ์ž‘/์ข…๋ฃŒ ๋ฒ”์œ„
def setTree(pre, start, end):
# ์žฌ๊ท€ ์ข…๋ฃŒ ์กฐ๊ฑด: ๋ฒ”์œ„๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๊ฑฐ๋‚˜ ํŠธ๋ฆฌ๋ฅผ ๋” ์ด์ƒ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ
if not (pre < len(preorder) and start <= end): # preorder์—์„œ ํ™•์ธํ•  ์ธ๋ฑ์Šค๊ฐ€ ๋ฒ”์œ„์—์„œ ๋‚˜๊ฐ, ํˆฌ ํฌ์ธํ„ฐ๊ฐ€ ๋งŒ๋‚จ
return None

val = preorder[pre] # ํ˜„์žฌ ๋…ธ๋“œ์˜ ๊ฐ’
root = inorder.index(val) # ํŠธ๋ฆฌ/์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋ฃจํŠธ ๋…ธ๋“œ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ - TC: O(n)

left = setTree(pre + 1, start, root - 1)
# inorder์—์„œ root๋…ธ๋“œ์˜ ์™ผ์ชฝ์€ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ
# pre์˜ ๋ณ€ํ™”: ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋ฃจํŠธ ๋…ธ๋“œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด +1 ์ด๋™

right = setTree(pre + 1 + root - start, root + 1, end)
# inorder์—์„œ root๋…ธ๋“œ์˜ ์˜ค๋ฅธ์ชฝ์€ ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ
# pre์˜ ๋ณ€ํ™”: ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋ฃจํŠธ ๋…ธ๋“œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด +1 ์ด๋™ + (root - start) ๐Ÿ‘ˆ ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ์˜ ํฌ๊ธฐ ๋งŒํผ ๋” ์ด๋™

return TreeNode(preorder[pre], left, right) # ํŠธ๋ฆฌ ๋…ธ๋“œ ์ƒ์„ฑ

# preorder ์ตœ์ดˆ ์ธ๋ฑ์Šค = ๋ฃจํŠธ ๋…ธ๋“œ(0), inorder์˜ ์ฒ˜์Œ(0)๊ณผ ๋(len(inorder) - 1) ์ธ๋ฑ์Šค
return setTree(0, 0, len(inorder) - 1) # TC: O(n^2), SC: O(n)

'''
C. ์žฌ๊ท€ ํ’€์ด + ์‹œ๊ฐ„ ์ตœ์ ํ™”
๋ ˆํผ๋Ÿฐ์Šค ๋งํฌ์˜ ํ’€์ด 3: https://www.algodale.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
ํŠน์ง•: A์—์„œ preorder๋ฅผ ์ฐพ๋Š” O(n) ๊ณผ์ •์„ ํ•ด์‹œ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜์—ฌ O(1)๋กœ ์ตœ์ ํ™”
'''
def buildTreeC(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
# enumerate: ์ธ๋ฑ์Šค์™€ ๊ฐ’์„ ๋™์‹œ์— ๋ฐ˜ํ™˜
# inorder๋ฅผ val -> idx๋กœ ๋งคํ•‘ํ•œ ๋”•์…”๋„ˆ๋ฆฌ ์ƒ์„ฑ
inorder_index_map = {val: idx for idx, val in enumerate(inorder)}
# preorder๋ฅผ ์ˆœํšŒํ•˜๊ธฐ ์œ„ํ•œ iterator ๊ฐ์ฒด ์ƒ์„ฑ
pre_iter = iter(preorder)

def setTree(start, end):
if start > end: # ์žฌ๊ท€ ์ข…๋ฃŒ ์กฐ๊ฑด: ๋ฒ”์œ„๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๊ฑฐ๋‚˜ ํŠธ๋ฆฌ๋ฅผ ๋” ์ด์ƒ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ
return None

root_val = next(pre_iter) # ํ˜„์žฌ ๋…ธ๋“œ์˜ ๊ฐ’, ๋งค ์ˆœํšŒ๋งˆ๋‹ค ๋‹ค์Œ preorder ๋…ธ๋“œ(root)์˜ ๊ฐ’์„ ๊ฐ€์ ธ์˜ด
root = inorder_index_map[root_val] # ํŠธ๋ฆฌ/์„œ๋ธŒํŠธ๋ฆฌ์˜ ๋ฃจํŠธ ๋…ธ๋“œ ์ธ๋ฑ์Šค๋ฅผ O(1) ์‹œ๊ฐ„์œผ๋กœ ์ฐพ๊ธฐ

left = setTree(start, root - 1) # ์™ผ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ
right = setTree(root + 1, end) # ์˜ค๋ฅธ์ชฝ ์„œ๋ธŒํŠธ๋ฆฌ
return TreeNode(root_val, left, right) # ํŠธ๋ฆฌ ๋…ธ๋“œ ์ƒ์„ฑ

return setTree(0, len(inorder) - 1) # inorder์˜ ์ฒ˜์Œ(0)๊ณผ ๋(len(inorder) - 1) ์ธ๋ฑ์Šค
56 changes: 56 additions & 0 deletions decode-ways/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'''
# Leetcode 91. Decode Ways
use **dynamic programming** to solve this problem.
## Time and Space Complexity
```
TC: O(n)
SC: O(n)
```
### TC is O(n):
- iterating through the string and checking if the current character is decodable. = O(n)
### SC is O(n):
- creating a dp array of size n + 1 = O(n)
'''
class Solution:
def isDecodable(self, str: str):
return 1 <= int(str) <= 26 and str[0] != '0'

def numDecodings(self, s: str) -> int:
if s[0] == "0":
return 0

n = len(s)
dp = (n + 1) * [0]
dp[0] = 1
dp[1] = 1

for i in range(2, n + 1):
one = s[i - 1]
two = s[i - 2:i]

if self.isDecodable(one):
dp[i] += dp[i - 1]
if self.isDecodable(two):
dp[i] += dp[i - 2]

return dp[n]

'''
# sudo code
- ํ—ฌํผํ•จ์ˆ˜: 0์œผ๋กœ ์‹œ์ž‘ํ•˜์ง€ ์•Š๊ณ , 1~26์ธ ๊ฒฝ์šฐ True
- numDecodingsํ•จ์ˆ˜
1. n: ๋ฌธ์ž์—ด s์˜ ๊ธธ์ด
2. dp: ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  ๋ฐฐ์—ด, n+1
3. BaseCase: dp[0] = 1, dp[1] = 1
4. for loop 2 to n:
one = s์˜ i-1 ์œ„์น˜์˜ 1๊ธ€์ž (ํ˜„์žฌ ๊ธ€์ž)
two = s์˜ i-2๋ถ€ํ„ฐ i๊นŒ์ง€ ์ž๋ฅธ 2๊ธ€์ž (ํ˜„์žฌ ๊ธ€์ž ํฌํ•จ ์ด์ „ ๊ธ€์ž)
if one is decodable => dp[i] += dp[i - 1] i๊ธธ์ด์ผ ๋•Œ, dp์˜ -1 ๊ฒฝ์šฐ์˜ ๋งŒํผ์ˆ˜ ์ถ”๊ฐ€ (ํ˜„์žฌ ๊ธ€์ž๋ฅผ ํ•œ ๊ธ€์ž๋กœ ํ•ด์„)
if two is decodable => dp[i] += dp[i - 2] i๊ธธ์ด์ผ ๋•Œ, dp์˜ -2 ๊ฒฝ์šฐ์˜ ์ˆ˜ ๋งŒํผ ์ถ”๊ฐ€ (ํ˜„์žฌ ๊ธ€์ž๋ฅผ ๋‘ ๊ธ€์ž๋กœ ํ•ด์„)
5. dp[n] ๋ฐ˜ํ™˜: ์ตœ์ข… ๋””์ฝ”๋“œ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ์˜ ์ˆ˜ ๊ฒฐ๊ณผ
'''
44 changes: 44 additions & 0 deletions valid-anagram/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'''
# Leetcode 242. Valid Anagram
use `Counter` to (1)compare the frequency of characters in both strings, and (2)try to compare the frequency more efficiently. ๐Ÿ”
## Time and Space Complexity
```
TC: O(n)
SC: O(n)
```
### A. use frequency object
#### TC is O(n):
- iterating through the strings just once to compare the frequency of characters. O(n)
#### SC is O(n):
- creating a new string `converted_s` to store the
### B. use Counter more efficiently
#### TC is O(n):
- iterating through the strings just once to compare the frequency of characters. O(n)
#### SC is O(n):
- creating a new string `converted_s` to store the
'''
class Solution:
def isAnagramA(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False

frequency = Counter(s) # SC: O(n)

for char in t: # TC: O(n)
if char not in frequency or frequency[char] == 0: # TC: O(1)
return False
frequency[char] -= 1

return True

def isAnagramB(self, s: str, t: str) -> bool:
return Counter(s) == Counter(t) # TC: O(n), SC: O(n)

0 comments on commit c11f6cb

Please sign in to comment.