diff --git a/alien-dictionary/yyyyyyyyyKim.py b/alien-dictionary/yyyyyyyyyKim.py new file mode 100644 index 000000000..6a7969c3c --- /dev/null +++ b/alien-dictionary/yyyyyyyyyKim.py @@ -0,0 +1,95 @@ +""" +LintCode 892. Alien Dictionary +https://www.lintcode.com/problem/892/ +https://leetcode.com/problems/alien-dictionary/ + +summary: +외계인 단어 목록이 사전 순으로 정렬되어 있을 때, +단어 간의 비교를 통해 문자의 순서를 유추해 전체 외계인 알파벳 순서를 구하는 문제 +""" +from typing import ( + List, +) + +class Solution: + """ + @param words: a list of words + @return: a string which is correct order + """ + def alien_order(self, words: List[str]) -> str: + + # 무조건다시풀어볼것. 어려움어려움. + # DFS기반 후위순회(post-order) 위상 정렬 + # 시간복잡도 O(N*L+V+E), 공간복잡도 O(V+E) + # V = 등장한 문자의 수(최대26개), E = 간선수(문자간순서), N = 단어수, L = 각 단어의 평균 길이 + + # 그래프 만들기(문자별 연결 정보) + graph = {} + + # visited + # 0: 방문 안함 + # 1: 방문 중(현재 탐색 중) + # 2: 방문 완료(사이클 없음 확인 완료) + visited = {} + answer = [] + + # 노드 초기화(모든 문자 그래프에 넣기) + for word in words: + for i in word: + if i not in graph: # 없으면 넣기(중복방지) + graph[i] = [] + + # 문자 간선 설정 + for i in range(len(words)-1): + w1 = words[i] + w2 = words[i+1] + min_len = min(len(w1), len(w2)) + + # 앞단어가 뒷단어의 접두사인데 더 길면 잘못된 입력이므로 '' 반환 + if w1[:min_len] == w2[:min_len] and len(w1) > len(w2): + return '' + + for j in range(min_len): + c1 = w1[j] + c2 = w2[j] + # c1과 c2가 다른 문자이고 c1에 c2가 없으면 c1의 그래프에 c2 추가 + if c1 != c2: + if c2 not in graph[c1]: + graph[c1].append(c2) + break + + # 자식 노드가 여러 개일 경우, 사전순 탐색을 위해 자식 노드 정렬 + for i in graph: + graph[i].sort() + + # DFS 함수 + def dfs(node): + # visited.get(node, 0) : 방문하지 않은 경우 기본값 0 반환 + if visited.get(node, 0) == 1: + return False # 사이클 발생 + if visited.get(node, 0) == 2: + return True # 방문 완료 + + # 방문 중 표시 + visited[node] = 1 + + # 인접 노드(다음문자) 탐색 + for i in graph[node]: + if not dfs(i): + return False + + # 탐색 완료(방문 완료) + visited[node] = 2 + answer.append(node) + + return True + + # 모든 노드 dfs 탐색 + for i in graph: + if visited.get(i, 0) == 0: + if not dfs(i): + return '' # 사이클이면 잘못된 입력 + + + # post-order니까 역순으로 반환 + return ''.join(reversed(answer)) diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/yyyyyyyyyKim.py b/construct-binary-tree-from-preorder-and-inorder-traversal/yyyyyyyyyKim.py new file mode 100644 index 000000000..75c1afb85 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/yyyyyyyyyKim.py @@ -0,0 +1,48 @@ +""" +LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal +https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + +summary: +전위순회(preorder)와 중위순회(inorder)를 기반으로 이진트리 구성하기 +""" +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + + # DFS + # 시간복잡도 O(n), 공간복잡도 최악O(n)/평균O(log n) (n = 노드수) + self.preorder_idx = 0 # preorder에 현재 위치 인덱스(처음값 = 루트노드값) + + inorder_map = {} # inorder 값을 인덱스로 매핑(빠른 검색을 위해) + for i in range(len(inorder)): + inorder_map[inorder[i]] = i + + + def dfs(left: int, right:int) -> Optional[TreeNode]: + # 왼쪽인덱스가 오른쪽인덱스보다 크면(범위를벗어나면) 빈 서브트리 -> 종료 + if left > right: + return None + + # 현재 preorder에서 현재 루트 값 가져오고 한 칸 이동 + root_val = preorder[self.preorder_idx] + self.preorder_idx += 1 + + # 루트 노드 생성 + root = TreeNode(root_val) + + # inorder에서 현재 루트 위치 찾기 + i = inorder_map[root_val] + + # 왼쪽, 오른쪽 서브트리 구성 + root.left = dfs(left, i-1) + root.right = dfs(i+1, right) + + return root + + # 전체 inorder dfs 돌기 + return dfs(0,len(inorder)-1) diff --git a/longest-palindromic-substring/yyyyyyyyyKim.py b/longest-palindromic-substring/yyyyyyyyyKim.py new file mode 100644 index 000000000..7c91cedd2 --- /dev/null +++ b/longest-palindromic-substring/yyyyyyyyyKim.py @@ -0,0 +1,36 @@ +""" +LeetCode 5. Longest Palindromic Substring +https://leetcode.com/problems/longest-palindromic-substring/ + +summary: +주어진 문자열 s에서 "가장 긴 팰린드룸 부분 문자열"을 찾아 반환 +""" + +class Solution: + def longestPalindrome(self, s: str) -> str: + + # DP (시간복잡도 O(n^2), 공간복잡도 O(n^2)) + n = len(s) + dp = [[False]*n for _ in range(n)] # dp[i][j] = s[i..j]의 팰린드룸 여부 저장 + answer = '' + + # i = 부분 문자열의 길이(1부터 n까지) + for i in range(1,n+1): + # j = 부분 문자열의 시작 인덱스 + for j in range(n - i + 1): + # 끝 인덱스 = 시작인덱스 + 길이 - 1 + end = j + i -1 + # 양 끝 문자가 같을 경우 + if s[j] == s[end]: + # 길이가 3이하면 팰린드룸 + if i <= 3: + dp[j][end] = True + # 양 끝이 같고 안쪽도 팰린드룸이면 전체도 팰린드룸 + else: + dp[j][end] = dp[j+1][end-1] + + # 현재 팰린드룸의 길이가 answer보다 길면 업데이트 + if dp[j][end] and i > len(answer): + answer = s[j:end+1] + + return answer diff --git a/rotate-image/yyyyyyyyyKim.py b/rotate-image/yyyyyyyyyKim.py new file mode 100644 index 000000000..471e56d7a --- /dev/null +++ b/rotate-image/yyyyyyyyyKim.py @@ -0,0 +1,25 @@ +""" +LeetCode 48. Rotate Image +https://leetcode.com/problems/rotate-image/ + +summary: +n x n 행렬을 시계 방향으로 90도 회전(in-place, 추가 공간없이 행렬 자체를 수정) +""" +class Solution: + def rotate(self, matrix: List[List[int]]) -> None: + """ + Do not return anything, modify matrix in-place instead. + """ + # 시간복잡도 O(n^2), 공간복잡도 O(1) + + n = len(matrix) + + # 행과 열 바꾸기 + for i in range(n): + for j in range(i+1, n): + matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] + + # 행을 좌우반전하기 + for i in range(n): + for j in range(n//2): + matrix[i][j], matrix[i][n-j-1] = matrix[i][n-j-1], matrix[i][j] diff --git a/subtree-of-another-tree/yyyyyyyyyKim.py b/subtree-of-another-tree/yyyyyyyyyKim.py new file mode 100644 index 000000000..3a767289e --- /dev/null +++ b/subtree-of-another-tree/yyyyyyyyyKim.py @@ -0,0 +1,46 @@ +""" +LeetCode 572. Subtree of Another Tree +https://leetcode.com/problems/subtree-of-another-tree/ + +summary: +root 트리 안에 subRoot 트리와 동일한 구조 & 값을 가진 서브트리가 있는지 확인 +""" + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: + + # DFS + # 시간복잡도 O(n*m) : n = root트리 노드 개수, m = subRoot트리 노드 개수 + # 공간복잡도 O(h1+h2) : h1 = root의 최대높이(최악O(n)), h2 = subRoot의 최대높이(최악O(m)) + # 두 트리의 구조와 값이 같은지 확인 + def isSameTree(node1, node2): + # 둘 다 없으면 True + if not node1 and not node2: + return True + # 둘 중 하나만 없으면 False + if not node1 or not node2: + return False + # 현재 노드 값이 다르면 False + if node1.val != node2.val: + return False + + # root 트리랑 subRoot 트리의 양쪽 서브트리 재귀 탐색 + return isSameTree(node1.left, node2.left) and isSameTree(node1.right, node2.right) + + # root 트리를 DFS로 돌면서 subRoot와 같은지 확인 + def dfs(node): + if not node: + return False + if isSameTree(node, subRoot): + return True + + # root 트리의 양쪽 서브트리 탐색 + return dfs(node.left) or dfs(node.right) + + return dfs(root)