Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions 3sum/jongwanra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""
[Problem]
https://leetcode.com/problems/3sum/description/

[Brainstorm]
3 <= nums.length <= 3,000
Brute Force: O(N^3) => 27,000,000,000 => time limited
O(N^2)의 풀이는 시간 제한에 걸리지 않을 것으로 보인다. 3,000 * 3,000 => 9,000,000

[Plan]
1. map을 설정한다. key = elements value: index list
2. nested-loop을 순회한다.
2-1. i == j인 경우 제외
2-2. map에서 동일한 인덱스인 경우 제외하고 구한다.

"""

from typing import List


class Solution:
"""
Attempt-1 My solution (incorrect)
time limited
"""

def threeSum1(self, nums: List[int]) -> List[List[int]]:
map = {}
for index in range(len(nums)):
values = map.get(nums[index], [])
values.append(index)
map[nums[index]] = values

triplets = set()
for i in range(len(nums)):
for j in range(len(nums)):
if i == j:
continue
complement = -(nums[i] + nums[j])
# print(f"nums[{i}]={nums[i]}, nums[{j}]={nums[j]} , complement={complement}")
if complement in map:
values = map[complement]
for k in values:
if k == i or k == j:
continue
triplets.add(tuple(sorted([nums[i], nums[j], nums[k]])))
return list(triplets)

"""
Attempt-2 Another solution
ref: https://www.algodale.com/problems/3sum/

"""

def threeSum2(self, nums: List[int]) -> List[List[int]]:
triplets = set()

for index in range(len(nums) - 2):
seen = set()
for j in range(index + 1, len(nums)):
complement = -(nums[index] + nums[j])
if complement in seen:
triplet = [nums[index], nums[j], complement]
triplets.add(tuple(sorted(triplet)))
seen.add(nums[j])

return list(triplets)

"""
Attempt-3 Another solution
ref: https://www.algodale.com/problems/3sum/

[Plan]
two-pointer로 접근한다.

[Complexity]
N: nums.length
Time: O(N^2)
Space: O(1)
"""

def threeSum3(self, nums: List[int]) -> List[List[int]]:
triplets = set()
nums.sort()

for index in range(len(nums) - 2):
left = index + 1
right = len(nums) - 1

while left < right:
three_sum = nums[index] + nums[left] + nums[right]
if three_sum < 0:
left += 1
continue
if three_sum > 0:
right -= 1
continue
triplets.add((nums[index], nums[left], nums[right]))
left += 1
right -= 1
return list(triplets)


sol = Solution()
print(sol.threeSum3([-1, 0, 1, 2, -1, 4]))

98 changes: 98 additions & 0 deletions climbing-stairs/jongwanra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
[Problem]
https://leetcode.com/problems/climbing-stairs/

[Brainstorming]
한 번에 갈 수 있는 계단: 1 or 2
갈 수 있는 경우의 수를 구해야 하는 문제
constraints: 1 <= n <= 45

n = 1, 1
n = 2, 2
n = 3, 3
n = 4, 5
...
f(n) = f(n - 1) + f(n - 2)

[Plan]
1. n + 1 크기의 list를 만든다.
2. list[1] = 1, list[2] = 2를 대입한다.
3. for-loop를 3부터 n까지 순회한다.
3-1. Bottom-Top 방식으로 n까지 값을 채워간다.
4. n값을 반환한다.

[Complexity]
Time: O(n)
Space: O(n)
"""


class Solution:
def climbStairs(self, n: int) -> int:
answer = [0, 1, 2]
for index in range(3, n + 1):
answer.append(answer[index - 1] + answer[index - 2])

return answer[n]

"""
another solution
ref: https://www.algodale.com/problems/climbing-stairs/
[Summary]
Bottom Top 방식인데 공간을 최적화하여 접근
[Complexity]
Time: O(n)
Space: O(1) - Space Optimization

[Plan]
1. prev = 1, cur = 2를 저장한다.
2. for-loop를 순회한다. 3 to n + 1

"""

def climbStairs2(self, n: int) -> int:
if n <= 3:
return n

prev, cur = [2, 3]
for index in range(4, n + 1):
tmp = cur
cur = cur + prev
prev = tmp
return cur

"""
another solution
ref: https://www.algodale.com/problems/climbing-stairs/
[Summary]
Top-Bottom으로 재귀적으로 접근
[Complexity]
Time: O(n)
Space: O(n)
"""

def climbStairs3(self, n: int) -> int:
cache = {}

def dfs(num: int) -> int:
nonlocal cache
if num <= 3:
return num
if num in cache:
return cache[num]
cache[num] = dfs(num - 1) + dfs(num - 2)
return cache[num]

return dfs(n)


sol = Solution()

# Normal Case
print(sol.climbStairs3(2) == 2)
print(sol.climbStairs3(3) == 3)
print(sol.climbStairs3(4) == 5)
print(sol.climbStairs3(5) == 8)
print(sol.climbStairs3(38))
# Edge Case
print(sol.climbStairs3(1) == 1)
52 changes: 52 additions & 0 deletions product-of-array-except-self/jongwanra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
[Problem]
https://leetcode.com/problems/product-of-array-except-self/

배열 nums가 주어졌을 때, 배열 answer를 반환해라.
answer[i]는 nums[i]를 제외한 모든 요소들의 곱이어야 한다.

[Brainstorming]
전체 요소들의 곱을 구한다. 해당 요소만 나눗셈으로 제외한다.
-> 이 방식은 다루기 어렵다. 0이 들어갈 수 있음.

Brute Force 방식으로 밖에 떠오르지 않음..
O(n)으로 어떻게 풀 수 있지?

"""
from typing import List
class Solution:
"""
another solution
ref: https://www.algodale.com/problems/product-of-array-except-self/

[Brainstorming]
각 인덱스 요소를 제외한 요소들의 곱은 아래와 같다.
nums[0] * nums[1] * ... * nums[index - 1] * nums[index + 1] * ... nums[len(nums) - 1]
즉, nums[index] 전의 곱을 누적한 배열과, nums[index] 이후의 곱을 누적한 배열을 구하면 답을 구할 수 있다.

[Complexity]
N: nums.length
Time: O(N)
Space: O(N)
"""
def productExceptSelf(self, nums: List[int]) -> List[int]:
before_products = [1] * len(nums)
for index in range(1, len(nums)):
before_products[index] = before_products[index - 1] * nums[index- 1]

after_products = [1] * len(nums)
for index in range(len(nums) - 2, -1, -1):
after_products[index] = after_products[index + 1] * nums[index + 1]

answer = []
for index in range(len(nums)):
answer.append(before_products[index] * after_products[index])

return answer

sol = Solution()
print(sol.productExceptSelf([1,2,3,4]))
print(sol.productExceptSelf([-1,1,0,-3,3]))
print(sol.productExceptSelf([2,3,4,5]))


50 changes: 50 additions & 0 deletions valid-anagram/jongwanra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""
[Problem]
https://leetcode.com/problems/valid-anagram/

[Brain Storm]
아나그램인 경우, true, 아니면 false 반환
동일한 알파벳이 중복되어 사용될 수 있다.

[Plan]
1. s에 대해 for-loop을 순회하며 alphabet-count 형태로 map을 만든다.
2. t에 대해 for-loop을 순회한다.
2-1. t의 alphabet이 alphabet-count > 0 인 경우, count -= 1을 한다.
2-2. 없는 경우, false로 return 한다.
3. alphabet-count 가 빈 경우 true 그렇지 않으면 false를 반환한다.

[Complexity]
t.length = N
s.length = M

Time: O(N + M)
Space: O(M)
"""


class Solution:
def isAnagram(self, s: str, t: str) -> bool:
alphabet_to_count = {}
for alphabet in s:
alphabet_to_count[alphabet] = alphabet_to_count.get(alphabet, 0) + 1

for alphabet in t:
count = alphabet_to_count.get(alphabet, -1)
if count == -1:
return False

count -= 1
if count == 0:
alphabet_to_count.pop(alphabet)
else:
alphabet_to_count[alphabet] = count

return len(alphabet_to_count) == 0



sol = Solution()
# Normal Case
print(sol.isAnagram("anagram", "nagaram"))
print(sol.isAnagram("rat", "car"))

67 changes: 67 additions & 0 deletions validate-binary-search-tree/jongwanra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""
[Problem]
https://leetcode.com/problems/validate-binary-search-tree/

[Brainstorming]
BST가 유효한지를 검증하는 문제.
1 <= number of nodes <= 10^4(100,000)

[Plan]
1. Tree를 DFS를 이용하여 만든다.
2. 실제로 입력을 넣어보며, 존재하지 않는 경우 False를 반환한다.
"""
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right

class Solution:
"""
Attempt 1 - My Solution (incorrect)
이 풀이의 경우, 자기 자신과 자기 자식 노드들 까지만으로 한정한다.
따라서, BST를 제대로 검증할 수 없다.
root = [5,4,6,null,null,3,7] 인 경우에는 검증할 수 없다. (Edge Case)
"""
def isValidBST1(self, root: Optional[TreeNode]) -> bool:
invalid_flag = False
def dfs(node:Optional[TreeNode])->None:
nonlocal invalid_flag
if invalid_flag:
return

if not node:
return

if node.left and node.left.val > node.val:
invalid_flag = True
return
if node.right and node.right.val < node.val:
invalid_flag = True
return

dfs(node.left)
dfs(node.right)

dfs(root)
return not invalid_flag
"""
Attempt 2 - Another Solution
ref: https://www.algodale.com/problems/validate-binary-search-tree
[Complexity]
N: number of nodes in trees
Time: O(N) => Traverse all nodes in the tree.
Space: worst case: O(N), best case: O(log N)
"""

def isValidBST(self, root:Optional[TreeNode])->bool:
def dfs(node:Optional[TreeNode], min_limit:int, max_limit:int)->bool:
if not node:
return True
if not (min_limit < node.val < max_limit):
return False

return dfs(node.left, min_limit, node.val) and dfs(node.right, node.val, max_limit)