diff --git a/design-add-and-search-words-data-structure/Jay-Mo-99.py b/design-add-and-search-words-data-structure/Jay-Mo-99.py deleted file mode 100644 index ba3a46799..000000000 --- a/design-add-and-search-words-data-structure/Jay-Mo-99.py +++ /dev/null @@ -1,121 +0,0 @@ -# 해석 -# 0. TrieNode 클래스 정의: -# - 각 TrieNode 인스턴스는 다음의 두 가지 속성을 가진다: -# 1) children: 현재 노드의 자식 노드들을 저장하는 딕셔너리 (문자 -> TrieNode 인스턴스). -# 2) is_end_of_word: 현재 노드가 단어의 끝인지 나타내는 Boolean 값. - -# 1. WordDictionary 클래스 정의: -# - WordDictionary 클래스는 Trie 자료구조를 사용하여 단어를 저장(addWord)하고 탐색(search)한다. - -# 1-1. __init__ 함수: -# - root는 TrieNode 클래스로 생성된 인스턴스를 가진다. -# - Trie 자료구조의 시작점(루트 노드) 역할을 한다. - -# 1-2. addWord 함수: -# 1) 루트 노드(self.root)에서 시작. -# 2) 단어의 각 문자를 순회하며: -# - 현재 노드의 children에 문자가 없으면, 새 TrieNode를 생성해 추가. -# - 현재 노드를 해당 문자의 자식 노드로 이동. -# 3) 단어의 마지막 문자를 처리한 후, 해당 노드의 is_end_of_word를 True로 설정. - - -# 1-3. search 함수: -# - 주어진 단어가 Trie에 존재하는지 확인하는 함수. -# - 와일드카드 문자(.)를 처리할 수 있다. -# - 내부적으로 dfs(깊이 우선 탐색) 함수를 사용하여 트라이를 탐색. -# - dfs(index, node): -# 1) 종료 조건: index가 단어 길이에 도달하면, 현재 노드의 is_end_of_word 반환. -# 2) 현재 문자가 '.'인 경우: -# - 현재 노드의 모든 자식 노드에 대해 dfs를 호출. -# - 하나라도 True를 반환하면 True 반환. -# 3) 현재 문자가 일반 문자인 경우: -# - 자식 노드에 문자가 없으면 False 반환. -# - 자식 노드로 이동해 dfs를 재귀 호출. - - - - #Big O - # - N: 저장된 모든 단어의 총 문자 수 (Trie에 저장된 모든 문자의 개수). - # - C: 알파벳의 개수 (영어 기준 최대 26). - - #Time Complexity: O(N) - #- addWord함수 : N에 기반하여 단어 추가 - #- searchWord 함수: - # - 일반 탐색: O(n), n은 단어의 길이. - # - 와일드카드 탐색: 최악의 경우 O(C^N), - # - C는 알파벳 개수 (최대 26). - # - N은 단어의 길이. 와일드카드 문자가 많을수록 모든 경로를 탐색해야 할 수도 있음. - - # - Space Complexity: O(N × C) - # - # - 각 노드는: - # 1) children 딕셔너리를 통해 자식 노드를 저장 (메모리 사용). - # 2) is_end_of_word 변수 (Boolean 값, O(1)). - # - Trie에 저장된 단어의 문자 수가 많을수록 메모리 사용량 증가. - -class TrieNode: - def __init__(self): - self.children = {} #알파벳 a부터 z까지를 자식으로 가짐, 크기 26의 배열이나 딕셔너리를 사용. - self.is_end_of_word = False #어떤 단어의 끝인지 나타내는 Boolean 값 - #예를 들어, "note"이라는 단어의 'e'에 해당하는 노드의 is_end_of_word가 True, 'n' - -class WordDictionary: - def __init__(self): - self.root = TrieNode() # WD로 생성된 인스턴스.root = TrieNode 인스턴스 - - def addWord(self, word: str) -> None: - node = self.root #node에 self.root를 부여 - for char in word: # 매개변수 word를 하나씩 순회하며 char에 저장 (예: word="note" -> char="n", "o", "t", "e") - if char not in node.children: # 만약 char가 현재 노드의 자식 노드 목록에 없다면 - node.children[char] = TrieNode() - #node.children[char]을 TrideNode 인스턴스로 생성 - # self.root.children = { - # "n": TrieNode() # "n" 키가 추가되고, 값으로 새로운 TrieNode 인스턴스가 들어감 - # } - - #Example1: - #root - #└── "n" (children={}, is_end_of_word=False) - - #Example2: - #└── "n" (children={}, is_end_of_word=False) - # └── "o" (children={}, is_end_of_word=False) - node = node.children[char] #node를 현 node의 children[char]로 이동 - #Example1: - # node = node.children["n"] - - #Example2: - # node = node.children["o"] - node.is_end_of_word = True - #After for loop, 끝 노드의 is_end_of_word를 True로 전환 - - #Example 4: - #root - #└── "n" - #└── "o" - #└── "t" - #└── "e" (children={}, is_end_of_word=True) - - def search(self, word: str) -> bool: - def dfs(index, node): # DFS 함수 정의 - # 1) 종료 조건: 모든 문자를 탐색한 경우 - if index == len(word): - return node.is_end_of_word # 단어 끝 여부 반환 - # 2) 현재 문자 처리 - char = word[index] - if char == '.': # 2-1) 와일드카드인 경우 - for child in node.children.values(): # 모든 자식 노드 탐색 - if dfs(index + 1, child): #dfs를 재귀호출 하여 다음 노드로 탐색 재개 - return True #재귀 이후에 있으면 True - return False #없으면 False - else: # 2-2) 일반 문자 처리 - if char not in node.children: # 현재 문자가 자식 노드에 없는 경우 False - return False - return dfs(index + 1, node.children[char]) # 다음 노드로 이동하여 탐색 - - return dfs(0, self.root) - #1. def dfs를 self.root 위치에서 첫 호출. - - - - diff --git a/longest-substring-without-repeating-characters/Jay-Mo-99.py b/longest-substring-without-repeating-characters/Jay-Mo-99.py new file mode 100644 index 000000000..3ddf70e67 --- /dev/null +++ b/longest-substring-without-repeating-characters/Jay-Mo-99.py @@ -0,0 +1,36 @@ + #해석 + # + + + #Big O + #- N: str s의 길이 + + #Time Complexity: O(N) + #- start,end: 각각 최대 N번 움직임 -> O(N) + #- set의 삽입, 삭제 -> O(1) + + #Space Complexity: O(N) + #- chars: 최악의 경우 chars는 s의 모든 char을 저장한다 -> O(N) + +class Solution: + def lengthOfLongestSubstring(self, s: str) -> int: + max_len = 0 + chars = set() #현 윈도우 내 중복 없이 존재하는 문자들을 저장하는 set + start, end = 0, 0 # 슬라이딩 윈도우의 start 인덱스, end 인덱스 + #end가 s의 마지막 인덱스에 도달할때까지 반복 + while end < len(s): + #s[end]가 chars에 존재하면 + if s[end] in chars: + #chars의 s[start]를 제거 + chars.remove(s[start]) + #start를 오른쪽으로 이동, 윈도우 축소 + start += 1 + else: #s[end] 가 chars에 존재하지 않으면 + chars.add(s[end]) #해당 s[end]를 chars에 추가해준다 + end += 1 #end를 오른쪽으로 옮겨 윈도우 확장 + max_len = max(end - start, max_len) #start-end 계산하여 update + return max_len + +mySolution = Solution() +mySolution.lengthOfLongestSubstring("pwwkew") + diff --git a/number-of-islands/Jay-Mo-99.py b/number-of-islands/Jay-Mo-99.py new file mode 100644 index 000000000..c04b294b1 --- /dev/null +++ b/number-of-islands/Jay-Mo-99.py @@ -0,0 +1,60 @@ + #해석 + # r,c nested loop로 grid의 모든 element를 검사한다 + # 만약 1인 element를 만나면 sink함수를 호출한다 + # -sink 함수는 해당 element를 0으로 만들고 해당 element의 좌,우,상,하가 1인지 체크한다 + # -만약 1이 있다면 또 sink를 반복 호출하며 위의 검사를 반복한다.(1이 없을때까지) + # -만약 더이상 연결된 1이 없다면 재귀 호출 종료. + # sink함수 종료 시시 nested loop로 돌아와서 이후후 1인 element를 찾는다. + # grid의 1이 sink로 모두 없어지면 return cls한다. + + + #Big O + #- M: grid의 행의 갯수(r) + #- N: grid의 열의 갯수(c) + + #Time Complexity: O(M*N) + #- for loop: 이중 루프로 grid의 모든 element에 도달 -> O(M*N) + #- sink(row,col): 최악의 경우 sink함수는 M*N번 호출 -> O(M*N) + + #Space Complexity: O(M∗N) + #- sink 재귀호출: + # 최악의 경우 sink함수는 스택에 M*N번 재귀 호출 당한다. + # 스택에 해당 메모리 누적(재귀 호출 스택의 깊이가 M*N) -> O(M*N) +from typing import List +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + def sink(row, col): + grid[row][col] = "0" + + for r, c in [ + (row, col - 1), #Left + (row, col + 1), #Right + (row - 1, col), #Up + (row + 1, col), #Down + ]: + # If the neighbor cell is within bounds and is land ("1"), sink it recursively. + if 0 <= r < len(grid) and 0 <= c < len(grid[r]): + if grid[r][c] == "1": + sink(r, c) + + cnt = 0 # Count the number of islands. + # Loop through every cell in the grid. + for r in range(len(grid)): + for c in range(len(grid[r])): + if grid[r][c] == "1": + cnt += 1 + sink(r, c) ## Sink the entire island by converting all connected "1"s to "0"s. + return cnt + +mySolution = Solution() +mySolution.numIslands( + [ + ["1","1","1","1","0"], + ["1","1","0","1","0"], + ["1","1","0","0","0"], + ["0","0","0","0","0"] + ] +) + + + diff --git a/reverse-linked-list/Jay-Mo-99.py b/reverse-linked-list/Jay-Mo-99.py new file mode 100644 index 000000000..727884465 --- /dev/null +++ b/reverse-linked-list/Jay-Mo-99.py @@ -0,0 +1,48 @@ + #해석 + # 매개변수 head (ListNode 클래스의 인스턴스)에서 값을 추출하여 temp 리스트에 저장한다. + # temp 리스트를 역순으로 정렬(reverse)하여 연결 리스트를 뒤집는다. + # temp 리스트의 각 값을 ListNode 클래스를 사용해 새 연결 리스트(myNode)를 생성하며 순서대로 추가한다. + # 최종적으로 myNode의 next를 반환한다(이것은 새 연결 리스트의 시작점을 가리킨다). + + + #Big O + #- N: 입력 연결 리스트(head)의 노드 갯수 + + #Time Complexity: O(N) + #- while head: 연결 리스트의 모든 노드를 순회하며 val을 temp에 저장하므로 O(N). + #- for value in temp: temp 리스트의 모든 값을 순회하며 새로운 노드를 생성하므로 O(N). + + #Space Complexity: O(N) + #- temp : 연결 리스트의 모든 val을 저장하므로 O(N). + #- myNode 인스턴스: for loop 동안 current.next에 ListNode 인스턴스를 생성한다. 이 작업은 O(1) 작업이 N번 반복되므로 O(N). + + +# Definition for singly-linked list. +# class ListNode(object): +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution(object): + def reverseList(self, head): + """ + :type head: Optional[ListNode] + :rtype: Optional[ListNode] + """ + temp =[] + while head: + temp.append(head.val) + head = head.next + + temp = temp[::-1] #Reverse the temp list + + myNode = ListNode() #Create the Listnode instance + current = myNode #Update current to myNode for Initialize + + for value in temp: + current.next = ListNode(value) ## Create new ListNode Instance and assign it to current.next , + current = current.next #Move to the current.next(new Instance base on ListNode ) + + return myNode.next ## Return the head of the newly created linked list + + + diff --git a/set-matrix-zeroes/Jay-Mo-99.py b/set-matrix-zeroes/Jay-Mo-99.py new file mode 100644 index 000000000..ff4f722dd --- /dev/null +++ b/set-matrix-zeroes/Jay-Mo-99.py @@ -0,0 +1,45 @@ + #해석 + # matrix의 모든 element를 검사한다, 만약 0을 발견하면 해당 element와 같은 row와 col을 가진 element를 "a" 로 바꾼다. + # 두번째로 matrix의 모든 element를 검사한다, 만약 a를 발견하면 이를 0으로 바꾼다. + + + #Big O + #- N: row의 크기(matrix 행의 갯수) + #- K: col의 크기(matrix 열의 갯수 ) + + #Time Complexity: O(N*K*(N+K)) + #- for nested loop : 행의 갯수(N) 당 열의 갯수만큼(K) 루프 작동 -> O(N*K) + # - 최악의 경우, 첫번째 루프에서 for i in range(rows)가 M번 발동, for j in range(cols)가 N번 발동 -> O(N+K) + + #Space Complexity: O(1) + #- rows, cols: 변수의 할당과 업데이트는 상수 취급한다 -> O(1) +from typing import List + + +class Solution: + def setZeroes(self, matrix: List[List[int]]) -> None: + """ + Do not return anything, modify matrix in-place instead. + """ + rows, cols = len(matrix), len(matrix[0]) #rows와 cols에 matrix의 좌표 부여 + + # 1차 matrix 순회: 0을 발견하면, 그 element의 같은 행과 열의 0이 아닌 수를 임시 값 "a"로 바꾸기 + for r in range(rows): + for c in range(cols): + if matrix[r][c] == 0: + # 해당 행과 열의 0이 아닌 모든 값을 임시로 "a"로 변경 + for i in range(rows): # 해당 열의 모든 값 + if matrix[i][c] != 0: + matrix[i][c] = "a" + for j in range(cols): # 해당 행의 0이 아닌 모든 값을 "a"로 변경 + if matrix[r][j] != 0: + matrix[r][j] = "a" + + # 2차 matrix순회: "a"를 가진 수를 0으로 바꿔준다. + for r in range(rows): + for c in range(cols): + if matrix[r][c] == "a": + matrix[r][c] = 0 + + + diff --git a/unique-paths/Jay-Mo-99.py b/unique-paths/Jay-Mo-99.py new file mode 100644 index 000000000..0d27adec2 --- /dev/null +++ b/unique-paths/Jay-Mo-99.py @@ -0,0 +1,37 @@ + # 해석 + # grid는 행렬(matrix)처럼 격자의 형태이다. + # 행이 m개라는 뜻은 좌표 상 (0,0), (1,0), ..., (m-1,0)까지 존재한다는 뜻이다. + # 열이 n개라는 뜻은 좌표 상 (0,0), (0,1), ..., (0,n-1)까지 존재한다는 뜻이다. + # 따라서 (0,0)에서 (m-1,n-1)까지의 모든 가능한 경로 수를 구해야 한다. + # - 아래로 m-1번 이동하고, 오른쪽으로 n-1번 이동해야 한다. + # - 총 이동 횟수는 m-1 + n-1 = m+n-2이다. + # - 총 이동 횟수 내부에서 아래로 m-1번 오른쪽으로 (n-1)번의 조합의 경우의 수를 구한다. + # - 예를 들어, 아래로 3번 오른쪽으로 다섯번으로 만들수 있는 모든 경우의 수를 구한다 ^^^>>>>> : ^와 > 가능한 조합을 찾아준다. + # 공식: (m+n-2)! / (m-1)! / (n-1)! + + + #Big O + #- N: int m의 크기 + #- K: int n의 크기 + + #Time Complexity: O(M+K) + #- for i in range(1,m+1-1): m과 n의 크기의 합에 영향받아 계산 진행 -> O(N+K) + + #Space Complexity: O(1) + #- up,down1,down2 - 변수와 변수를 업데이트하는 사칙연산은 상수로 여겨져 O(1), +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + up = 1 #분자 + down1 = 1 #분모 1 + down2=1 #분모 2 + + + for i in range(1,m+n-1): + up *= i + for i in range(1,m): + down1 *= i + for i in range(1,n): + down2 *= i + + return int(up/(down1*down2)) +