-
-
Notifications
You must be signed in to change notification settings - Fork 195
[thispath98] Week 4 #821
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
[thispath98] Week 4 #821
Changes from all commits
e488dfb
5b77982
4b57613
bf45649
a338957
3e8a753
a30cffa
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,39 @@ | ||
class Solution: | ||
def coinChange(self, coins: List[int], amount: int) -> int: | ||
""" | ||
Intuition: | ||
dp 배열에 이전 금액에 대한 최소 개수를 저장해두고 | ||
갱신하는 방식으로 작동한다. | ||
|
||
for 루프를 돌면서 현재 가격에서 coin만큼의 가격을 | ||
뺐을 때 거슬러줄 수 있다면, 그 값에서 1개를 더해준 | ||
개수를 prev_coins에 저장한다. | ||
|
||
이후 prev_coins가 존재하면 현재 인덱스에서 거슬러줄 수 있는 | ||
동전의 최소 개수를 갱신한다. | ||
|
||
Time Complexity: | ||
O(amount x coins.length): | ||
amount 만큼 루프를 순회하는데 각 루프마다 | ||
coins.length 만큼 prev_coins 배열을 만든다. | ||
|
||
Space Complexity: | ||
O(amount): | ||
amount만큼의 크기를 가지는 dp 배열을 저장한다. | ||
""" | ||
dp = [0 for _ in range(amount + 1)] | ||
|
||
for coin in coins: | ||
if coin <= amount: | ||
dp[coin] = 1 | ||
|
||
for i in range(1, amount + 1): | ||
if dp[i]: | ||
continue | ||
|
||
prev_coins = [dp[i - coin] + 1 for coin in coins if i >= coin and dp[i - coin] > 0] | ||
if prev_coins: | ||
dp[i] = min(prev_coins) | ||
|
||
answer = -1 if amount > 0 and dp[amount] == 0 else dp[amount] | ||
return answer |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# Definition for singly-linked list. | ||
# class ListNode: | ||
# def __init__(self, val=0, next=None): | ||
# self.val = val | ||
# self.next = next | ||
class Solution: | ||
def mergeTwoListsList(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: | ||
""" | ||
Intuition: | ||
두 리스트의 원소를 각각 비교하면서 한번씩 스캔한다. | ||
결과적으로 한번씩만 스캔하면 정렬할 수 있다. | ||
|
||
Time Complexity: | ||
O(N): | ||
두개의 리스트를 1번 순회하며 답을 찾으므로, | ||
O(N)의 시간복잡도가 소요된다. | ||
|
||
Space Complexity: | ||
O(N): | ||
sorted_list에 정렬된 배열을 저장하므로, | ||
O(N)의 공간복잡도가 소요된다. | ||
""" | ||
sorted_list = [] | ||
while list1 is not None and list2 is not None: | ||
if list1.val < list2.val: | ||
sorted_list.append(list1.val) | ||
list1 = list1.next | ||
else: | ||
sorted_list.append(list2.val) | ||
list2 = list2.next | ||
|
||
while list1 is not None: | ||
sorted_list.append(list1.val) | ||
list1 = list1.next | ||
while list2 is not None: | ||
sorted_list.append(list2.val) | ||
list2 = list2.next | ||
|
||
sorted_node = None | ||
while sorted_list: | ||
val = sorted_list.pop() | ||
sorted_node = ListNode(val, sorted_node) | ||
|
||
return sorted_node | ||
|
||
def mergeTwoListsNode(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: | ||
""" | ||
Intuition: | ||
파이썬 리스트를 사용하지 않고 | ||
주어진 ListNode로부터 바로 시작한다. | ||
|
||
Time Complexity: | ||
O(N): | ||
두개의 리스트를 1번 순회하며 답을 찾으므로, | ||
O(N)의 시간복잡도가 소요된다. | ||
|
||
Space Complexity: | ||
O(1): | ||
ListNode를 바로 사용하므로 | ||
상수 만큼의 O(1)의 공간복잡도가 소요된다. | ||
|
||
Key takeaway: | ||
링크드 리스트를 오랜만에 접하니 잘 풀지 못했던 것 같다. | ||
전통적인 자료구조를 OOP 관점으로 고민해보자. | ||
""" | ||
sorted_node = ListNode() | ||
current_node = sorted_node | ||
|
||
while True: | ||
if list1 is None: | ||
current_node.next = list2 | ||
break | ||
elif list2 is None: | ||
current_node.next = list1 | ||
break | ||
Comment on lines
+69
to
+75
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.
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. 감사합니다. 무한 루프는 최소화해보겠습니다! |
||
|
||
if list1.val < list2.val: | ||
current_node.next = ListNode(list1.val) | ||
current_node = current_node.next | ||
list1 = list1.next | ||
else: | ||
current_node.next = ListNode(list2.val) | ||
current_node = current_node.next | ||
list2 = list2.next | ||
|
||
return sorted_node.next |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
class Solution: | ||
def missingNumber(self, nums: List[int]) -> int: | ||
""" | ||
Intuition: | ||
주어진 리스트의 개수를 얻어 범위를 구한다. | ||
이후 세트를 이용해서 범위 내의 정수가 | ||
세트 안에 없으면 그 수를 리턴한다. | ||
|
||
Time Complexity: | ||
O(N): | ||
세트(해시)는 접근하는 데에 상수의 시간이 걸리므로 | ||
최대 N + 1번의 접근을 하므로 | ||
O(N)의 시간복잡도가 소요된다. | ||
|
||
Space Complexity: | ||
O(N): | ||
리스트를 해시로 변환하여 저장하고 있으므로 | ||
O(N)의 공간복잡도가 소요된다. | ||
""" | ||
num_set = set(nums) | ||
for i in range(len(nums) + 1): | ||
if i not in num_set: | ||
return i | ||
Comment on lines
+20
to
+23
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. 코드 직관적이라서 정말 좋아요 👍 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
class Solution: | ||
def countSubstrings(self, s: str) -> int: | ||
""" | ||
Intuition: | ||
2중 루프를 돌면서 각 substring에 대해 | ||
palindrome인지 아닌지 확인한다. | ||
한번 palindrome인지 확인했으면, set에 추가하여 | ||
중복 확인을 한다. | ||
|
||
Time Complexity: | ||
O(N^2 x s.length): | ||
2중 루프는 N^2만큼 소요되고, | ||
각 루프에 palindrome을 체크하는 것은 | ||
s.length만큼 소요된다. | ||
|
||
Space Complexity: | ||
O(N^2): | ||
palindrome이 모두 중복되지 않을 경우 set에 | ||
s의 substring 개수만큼 저장한다. | ||
이는 대략 N^2이다. | ||
""" | ||
def is_palindrome(s): | ||
return s == s[::-1] | ||
|
||
palindrome_set = set() | ||
answer = 0 | ||
for i in range(1, len(s) + 1): | ||
for j in range(0, len(s) - i + 1): | ||
substr = s[j: j + i] | ||
if substr in palindrome_set or is_palindrome(substr): | ||
palindrome_set.add(substr) | ||
answer += 1 | ||
return answer | ||
|
||
|
||
class SolutionDPSet: | ||
def countSubstrings(self, s: str) -> int: | ||
""" | ||
Intuition: | ||
위 solution에서 중복을 제거할 수 있는 방법은, | ||
start : end 길이를 갖는 substring에서 | ||
s[start] == s[end]이고, start + 1 : end - 1의 | ||
substring이 palindrome이라면, 이 substring은 | ||
palindrome이라고 볼 수 있다. | ||
|
||
Time Complexity: | ||
O(N^2): | ||
DP로 인해 palindrome을 찾는 함수가 대략 | ||
상수의 시간복잡도가 걸린다고 볼 수 있다. | ||
따라서 substring을 만드는 이중 루프에서의 | ||
시간복잡도가 걸릴 수 있다고 보면 된다. | ||
|
||
Space Complexity: | ||
O(N^2): | ||
dp set에 index set을 저장하는데, 최악의 경우 | ||
index set은 N^2개만큼 저장될 수 있다. | ||
|
||
Key takeaway: | ||
dp를 이용해서 푸는 방식에 대해 익숙해져야겠다. | ||
|
||
의문점은 leetcode에서 bruteforce보다 시간이 더 소요되었다는 것이다. | ||
아마 list 크기를 초과할 경우에 append를 할 경우, | ||
리스트 크기를 2배만큼 늘리는 list doubling 방식이 | ||
set에도 적용이 되어 느려진 것으로 보인다. | ||
""" | ||
dp = set() | ||
|
||
|
||
def is_palindrome(start, end): | ||
while start < end: | ||
if s[start] != s[end]: | ||
return False | ||
if (start, end) in dp: | ||
return True | ||
start += 1 | ||
end -= 1 | ||
|
||
return True | ||
|
||
|
||
answer = 0 | ||
for length in range(1, len(s) + 1): | ||
for start in range(0, len(s) - length + 1): | ||
end = start + length - 1 | ||
if (start, end) in dp or is_palindrome(start, end): | ||
dp.add((start, end)) | ||
answer += 1 | ||
return answer | ||
|
||
|
||
class SolutionDPList: | ||
def countSubstrings(self, s: str) -> int: | ||
""" | ||
Intuition: | ||
DP solution에서 set로 저장하지 않고, | ||
이중 리스트로 저장하는 것으로 수정했다. | ||
length = 2인 경우에는 start와 end만 동일하면 | ||
palindrome으로 판단할 수 있어 조건을 추가했다. | ||
|
||
Time Complexity: | ||
O(N^2): | ||
DP로 인해 palindrome을 찾는 함수가 대략 | ||
상수의 시간복잡도가 걸린다고 볼 수 있다. | ||
따라서 substring을 만드는 이중 루프에서의 | ||
시간복잡도가 걸릴 수 있다고 보면 된다. | ||
|
||
Space Complexity: | ||
O(N^2): | ||
dp 리스트에 substring 이중 리스트를 저장하므로 | ||
N^2개만큼 저장될 수 있다. | ||
|
||
Key takeaway: | ||
이 방법이 가장 빠르게 동작했다. | ||
""" | ||
dp = [[False for _ in s] for _ in s] | ||
answer = 0 | ||
|
||
for i in range(len(s)): | ||
dp[i][i] = True | ||
answer += 1 | ||
|
||
for length in range(2, len(s) + 1): | ||
for start in range(len(s) - length + 1): | ||
end = start + length - 1 | ||
if s[start] == s[end]: | ||
if length == 2 or dp[start + 1][end - 1]: | ||
dp[start][end] = True | ||
answer += 1 | ||
|
||
return answer |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
class Solution: | ||
def exist(self, board: List[List[str]], word: str) -> bool: | ||
""" | ||
Intuition: | ||
보드를 돌면서 dfs를 수행한다. | ||
dfs는 상하좌우를 돌면서 word의 index와 | ||
board의 word가 동일한지 확인한다. | ||
|
||
Time Complexity: | ||
O(M x N + w.length^4): | ||
M x N 크기의 배열을 돌면서, | ||
각 칸마다 상하좌우 4번씩 확인한다. | ||
최대 word length번만큼 반복한다. | ||
|
||
Space Complexity: | ||
O(M x N + w.length): | ||
M x N 크기의 visited 배열을 초기화하고, | ||
dfs의 호출 스택은 word length만큼 반복한다. | ||
""" | ||
visited = [[False for _ in board[0]] for _ in board] | ||
|
||
def dfs(y, x, index): | ||
if index == len(word): | ||
return True | ||
if not (0 <= y < len(board) and 0 <= x < len(board[0])): | ||
return False | ||
if visited[y][x]: | ||
return False | ||
if word[index] != board[y][x]: | ||
return False | ||
|
||
visited[y][x] = True | ||
for dy, dx in [[-1, 0], [1, 0], [0, -1], [0, 1]]: | ||
ny = y + dy | ||
nx = x + dx | ||
|
||
if dfs(ny, nx, index + 1): | ||
return True | ||
|
||
visited[y][x] = False | ||
return False | ||
|
||
for i in range(len(board)): | ||
for j in range(len(board[0])): | ||
if dfs(i, j, 0): | ||
return True | ||
|
||
return False |
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.
dp의 요소 초기값을 0이 아니라 최대값으로 주면 어떨까요?
저도 이번에 배웠는데, 최소 개수 구하는 문제라서 최대값으로 초기화하니까 if문도 줄고, 로직이 더 명확해지더라구요!!