문제 연결 리스트를 뒤집어라. 입력 : 1 → 2 → 3 → 4 → 5 → NULL 출력 : 5 → 4 → 3 → 2 → 1 → NULL 코드 """ fixme https://leetcode.com/problems/reverse-linked-list/ Q15. 역순 연결 리스트 연결 리스트를 뒤집어라. 입력 : 1 → 2 → 3 → 4 → 5 → NULL 출력 : 5 → 4 → 3 → 2 → 1 → NULL """ class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class Solution: def reverseListRecur(self, head: ListNode) -> ListNode: def re..
문제 높이를 입력받아 비 온 후 얼마나 많은 물이 쌓일 수 있는지 계산하라. 입력 : height = [0,1,0,2,1,0,1,3,2,1,2,1] 출력 : 6 입력 : height = [4,2,0,3,2,5] 출력 : 9 코드 from typing import List class Solution: def trapTwoPointer(self, height: List[int]) -> int: if not height: return 0 volume = 0 left, right = 0, len(height) - 1 left_max, right_max = height[left], height[right] while left < right: left_max, right_max = max(left_max, heig..
0-1 배낭 문제 분할 가능 배낭 문제와 달리 짐을 쪼갤 수 없는 0-1 배낭 문제를 풀이해 보자. 이 문제는 '탐욕 선택 속성'이 있는 문제가 아니며 '중복된 하위 문제들' 속성을 갖고 있으므로 다이나믹 프로그래밍으로는 풀 수 있다. 단가 순으로 그리디하게 배치해서 풀이했던 분할 가능 배낭 문제와 달리, 0-1 배낭 문제는 짐을 쪼갤 수 없다. 이 경우 모든 경우의 수를 계산해야 한다. 먼저, 입력값으로 짐을 정의하고 zero_one_knapsack() 풀이 함수를 호출한다. cargo = [ (4, 12), (2, 1), (10, 4), (1, 1), (2, 2), ] print(zero_one_knapsack(cargo)) zero_one_knapsack() 함수는 다음과 같이 정의한다. def z..
그리디 알고리즘 그리디 알고리즘이란 바로 눈앞의 이익만을 좇는 알고리즘을 말한다. 그리디 알고리즘은 최적화 문제를 대상으로 한다. 최적해를 찾을 수 있으면 그것을 목표로 삼고, 찾기 어려운 경우에는 주어진 시간 내에 그런대로 괜찮은 해를 찾는 것을 목표로 삼는다. 그리디 알고리즘이 잘 작동하는 문제들은 탐욕 선택 속성을 갖고 있는 최적 부분 구조인 문제들이다. 여기서 탐욕 선택 속성이란 앞의 선택이 이후 선택에 영향을 주지 않는 것을 말한다. 다시 말해 그리디 알고리즘은 선택을 다시 고려하지 않는다. 또한 최적 부분 구조란 문제의 최적 해결 방법이 부분 문제에 대한 최적 해결 방법으로 구성되는 경우를 말한다. 이렇게 탐욕 선택 속성과 최적 부분 구조의 2가지 조건을 만족하면 최적해를 찾을 수 있다. 하지..
문제 숫자와 연산자를 입력 받아 가능한 모든 조합의 결과를 출력하라. 입력 : "2 - 1 - 1" 출력 : [0, 2] 설명 ((2 - 1) - 1) = 0 (2 - (1 - 1)) = 2 입력 : "2 * 3 - 4 * 5" 출력 : [-34, -14, -10, -10, 10] 설명 (2 * (3 - (4 * 5))) = -34 ((2 * 3) - (4 * 5)) = -14 ((2 * (3 - 4)) * 5) = -10 (2 * ((3 - 4) * 5)) = -10 (((2 * 3) - 4) * 5) = 10 코드 """ fixme. https://leetcode.com/problems/different-ways-to-add-parentheses/ fixme. Q84. 괄호를 삽입하는 여러 가지 방법..
리스트 append()와 extend()의 차이점 >>> a = [1,2,3] >>> b = [4,5] >>> a.append(b) >>> a [1, 2, 3, [4, 5]] 리스트에 또 다른 리스트를 삽입할 때 append()는 이처럼 리스트 전체를 또 다른 엘리먼트로 처리한다. >>> a = [1,2,3] >>> b = [4, 5] >>> a.extend(b) >>> a [1, 2, 3, 4, 5] 반면 extend()는 삽입 대상의 리스트를 풀어서 각각의 엘리먼트로 확장(Extend)해 삽입한다. 출처 파이썬 알고리즘 인터뷰 (글 : 박상길 그림 : 정진호) [책만]
문제 원형으로 경로가 연결된 주유소 목록이 있다. 각 주유소는 gas[i] 만큼의 기름을 갖고 있으며, 다음 주유소로 이동하는데 cost[i]가 필요하다. 기름이 부족하면 이동할 수 없다고 할 때 모든 주유소를 방문할 수 있는 출발점의 인덱스를 출력하라. 출발점이 존재하지 않을 경우 -1을 리턴하며, 출발점은 유일하다. 입력 : gas = [1,2,3,4,5], cost = [3,4,5,1,2] 출력 : 3 설명 3번 인덱스(기름을 4만큼 충전할 수 있는)에서 출발할 경우는 다음과 같다. 3번 → 4번 -1 fuel 3 4번 → 0번 -1 fuel 6 0번 → 1번 -1 fuel 4 1번 → 2번 -1 fuel 2 2번 → 3번 -1 fuel 0 정확하게 기름이 0까지 소모되며, 모든 주유소를 방문할 수..
문제 문자열 S와 T를 입력받아 O(n)에 T의 모든 문자가 포함된 S의 최소 윈도우를 찾아라. 입력 : S = "AD0BEC0DEBANC", T = "ABC" 출력 : "BANC" 코드 """ fixme. https://leetcode.com/problems/minimum-window-substring/ fixme. Q76. 부분 문자열이 포함된 최소 윈도우 fixme. 문자열 S와 T를 입력받아 O(n)에 T의 모든 문자가 포함된 S의 최소 윈도우를 찾아라. fixme. 입력 : S = "AD0BEC0DEBANC", T = "ABC" fixme. 출력 : "BANC" """ import collections class Solution: def minWindowPointer(self, s: str, t..
문제 두 정수 a와 b의 합을 구하라. +또는 -연산자는 사용할 수 없다. 입력 a = 1, b = 2 출력 3 입력 a = -2, b = 3 출력 1 코드 전가산기 구현 class Solution: def getSum(self, a: int, b: int) -> int: MASK = 0xFFFFFFFF INT_MAX = 0x7FFFFFFF a_bin = bin(a & MASK)[2:].zfill(32) b_bin = bin(b & MASK)[2:].zfill(32) result = [] carry = 0 sum = 0 for i in range(32): A = int(a_bin[31 - i]) B = int(b_bin[31 - i]) # 전가산기 구현 Q1 = A & B Q2 = A ^ B Q3 = Q..
파이썬의 진법 표현 파이썬이 이진수(binary), 십진수(decimal), 16진수(hexadecimal)를 저장하고 표현하는 방법을 살펴보자. 먼저 이진수와 십진수는 다음과 같이 각각 bin()과 int()를 사용해서 서로 변환할 수 있다. >>> bin(87) '0b1010111' >>> int('0b1010111', 2) 87 bin()을 사용하면 이진수가 문자형 0b1010111으로 리턴된다. 반면 int()을 사용하면, 문자형인 이진수가 십진수 숫자 정수형 87로 리턴된다. 이진수임을 의미하는 접두사(Prefix) 0b는 다음과 같이 생략도 가능하다. >>> int('1010111', 2) 87 bin()을 사용해 변수에 값을 할당하면 다음과 같이 문자형이 된다. >>> a = bin(87) ..
any()와 all() 함수 >>> any([True, False, False]) True any()는 포함된 값 중 어느 하나가 참이라면 항상 참으로 출력한다. 논리 연산의 OR과 비슷하다. 반면 all()이라는 함수는 모든 값이 참이어야 True를 출력한다. >>> all([True, False, False]) False >>> all([True, True, True]) True 이처럼 all()은 논리 연산의 AND와 유사하다. 출처 파이썬 알고리즘 인터뷰 (글 : 박상길 그림 : 정진호) [책만]
문제 연결 리스트를 삽입정렬로 정렬하라. 코드 class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class Solution: def insertionSortList(self, head: ListNode) -> ListNode: cur = parent = ListNode(0) while head: while cur.next and cur.next.val head.val: cur = parent return parent.ne..
@staticmethod 데코레이터 @staticmethod는 자바의 메소드 static 선언과도 비슷한데, 이렇게 정의한 메소드는 클래스와 독립적인 함수로서의 의미를 강하게 갖는다. 파라미터에도 항상 따라붙는 self가 빠져 있고, 함수 자체가 별도의 자료형으로 선언되어 있다. class CLASS: def a(self): pass @staticmethod def b(): pass 이 같은 클래스가 선언되어 있을 때, 함수 a()와 b()의 타입을 출력해보자. >>> type(CLASS.a), type(CLASS.b) (, ) 클래스를 생성하지 않고 바깥에서 직접 호출했을 때 타입은 이처럼 둘 다 함수가 된다. >>> cls = CLASS() >>> type(cls.a), type(cls.b) (, )..
문제 동일한 값을 지닌 가장 긴 경로를 찾아라. 입력 출력 2 입력 출력 2 코드 class TreeNode: def __init__(self, val, left=None, right=None): self.val = val self.left = left self.right = right class Solution: result: int = 0 def longestUnivaluePath(self, root: TreeNode): def dfs(node: TreeNode): if node is None: return 0 left = dfs(node.left) right = dfs(node.right) if node.left and node.left.val == node.val: left += 1 else: l..
트리 트리는 재귀로 정의된 자기 참조 자료구조이다. 쉽게 말하면, 트리는 자식도 트리고 또 그 자식도 트리다. 즉 여러 개의 트리가 쌓아 올려져 큰 트리가 된다. 흔히 서브트리로 구성된다고 표현한다. 트리의 각 명칭 트리는 항상 루트에서부터 시작된다. 루트는 자식 노드를 가지며, 간선으로 연결되어 있다. 자식 노드의 개수는 차수라고 하며, 크기는 자신을 포함한 모든 자식 노드의 개수다. 높이는 현재 위치에서부터 리프까지의 거리, 깊이는 루트에서부터 현재 노드까지의 거리다. 트리는 그 자식도 트리인 서브 트리 구성을 띈다. 레벨은 0에서부터 시작한다. 트리는 항상 단방향이기 때문에 간선의 화살표는 생략 가능하다. 일반적으로 간선의 방향은 위에서 아래로 향한다. 그래프 vs 트리 트리는 순환 구조를 갖지 않..
문제 K부터 출발해 모든 노드가 신호를 받을 수 있는 시간을 계산하라. 불가능할 경우 -1을 리턴한다. 입력값 (u, v, w)는 각각 출발지, 도착지, 소요시간으로 구성되며, 전체 노드의 개수는 N으로 입력받는다. 입력 times = [[2,1,1], [2,3,1], [3,4,1]], N = 4, K = 2 출력 2 코드 from typing import List import collections, heapq class Solution: def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int: graph = collections.defaultdict(list) for u, v, w in times: graph[u].append..
문제 입력 2, [[1,0]] 출력 True 입력 2, [[1,0],[0,1]] 출력 False 코드 visited 없을 때 from typing import List import collections class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: graph = collections.defaultdict(list) for x, y in prerequisites: graph[x].append(y) traced = set() def dfs(i): if i in traced: return False traced.add(i) for y in graph[i]: if not dfs(y): ret..
문제 [from, to]로 구성된 항공권 목록을 이용해 JFK에서 출발하는 여행 일정을 구성하라. 여러 일정이 있는 경우 사전 어휘순으로 방문한다. 입력 [["MUC", "LHR"], ["JFK", "MUC"], ["SF0", "SJC"], ["LHR", "SF0"]] 출력 ["JFK", "MUC", "SF0", "SJC"] 입력 [["JFK", "SF0"], ["JFK", "ATL"], ["SF0", "ATL"], ["ATL", "JFK"], ["ATL", "SF0"]] 출력 ["JFK", "ATL", "JFK", "SF0", "ATL", "SF0"] 코드 DFS from typing import List import collections class Solution: def findItinerary(s..
문제 서로 다른 정수를 입력 받아 가능한 모든 순열을 리턴하라. 입력 [1,2,3] 출력 [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 코드 재귀 함수 from typing import List class Solution: def permute(self, nums: List[int]) -> List[List[int]]: results = [] prev_elements = [] def dfs(elements): if len(elements) == 0: results.append(prev_elements[:]) for e in elements: next_elements = elements[:] next_elements.remove(e) prev_elem..
객체 복사 파이썬의 중요한 특징 중 하나는 모든 것이 객체라는 점이다. 심지어 숫자, 문자까지도 모두 객체다. 숫자, 문자가 리스트, 딕셔너리 같은 객체와의 차이점이라면 불변 객체라는 차이뿐이다. 그러다 보니 별도로 값을 복사하지 않는 한, 변수에 값을 할당하는 모든 행위는 값 객체에 대한 참조가 된다. 이 말은 참조가 가리키는 원래의 값을 변경하면 모든 참조, 즉 모든 변수의 값 또한 함께 변경된다는 말이기도 하다. 그렇다면 참조가 되지 않도록 값 자체를 복사하려면 어떻게 해야 할까? 가장 간단한 방법은 [:]로 처리하는 방법이다. >>> a = [1,2,3] >>> b = a >>> c = a[:] >>> id(a), id(b), id(c) (4362781552, 4362781552, 43615800..
전화 번호 문자 조합 문제 2에서 9까지 숫자가 주어졌을 때 전화 번호로 조합 가능한 모든 문자를 출력하라. 입력 : "23" 출력 : ["ad","ae","af","bd","be","bf","cd","ce","cf"] 코드 from typing import List class Solution: def letterCombinations(self, digits: str) -> List[str]: def dfs(index, path): if len(path) == len(digits): result.append(path) return for i in range(index, len(digits)): for j in dic[digits[i]]: dfs(i + 1, path + j) if not digits: r..
중첩함수 중첩 함수(Nested Function)란 함수 내에 위치한 또 다른 함수로, 바깥에 위치한 함수들과 달리 부모 함수의 변수를 자유롭게 읽을 수 있다는 장점이 있다. 중첩 함수가 부모 함수의 변수를 공유하는 예제는 다음과 같다. def outer_function(t: str): test: str = t def inner_function(): print(text) inner_function( outer_function("Hello!') ----- Hello! 여기서 outer_function()은 inner_functuion()을 호출했고, 아무런 파라미터도 남기지 않았지만 부모 함수의 text 변수를 자유롭게 읽어들여 그 값인 Hello!를 출력했다. 이처럼 매번 파라미터를 전달하지 않아..
지금으로부터 300여 년 전 프로이센 공국의 쾨니히스베르크에는 프레겔 강이 흐르고 있었다. 프레겔 강에는 2개의 큰 섬이 있었고, 섬과 도시를 연결하는 7개의 다리가 놓여 있었다. 어느날 도시의 시민 한 명이 "이 7개 다리를 한 번씩만 건너서 모두 지나갈 수 있을까?"라는 흥미로운 문제를 냈다. 그러나 쉽게 풀릴 것처럼 보였던 이 문제를 풀 수 있는 이는 아무도 없었다. '수학의 모차르트'라 불리는 레온하르트 오일러가 '쾨니히스베르크의 다리 문제'를 조사하기 시작했다. 오일러는 이 문제가 도형 문제처럼 보이지만, 당시까지 알려진 기하학으로는 풀 수 없음을 알았다. 그리고 미지의 영역에 그 해법이 있다는 사실을 처재적인 직관으로 간파했다. 이것이 바로 '그래프 이론&#..
파이썬에서 언팩킹과 팩킹 zip()의 파라미터는 1개가 될 수도 있고, 2개가 될 수도, 10개가 될 수도 있다. 밑의 실험을 보면 알 수 있다. >>> a = ['a1', 'a2'] >>> b = ['b1', 'b2'] >>> c = ['c1', 'c2'] >>> d = ['d1', 'd2'] >>> list(zip(a)) [('a1',), ('a2',)] >>>list(zip(a, b)) [('a1', 'b1'), ('a2', 'b2')] >>> list(zip(a, b, c)) [('a1', 'b1', 'c1'), ('a2', 'b2', 'c2')] 여기에는 아스테리스크(Asterisk) 혹은 흔히 별표라고 부르는 *를 활용한다. 파이썬에서 *는 언팩(Unpack)이다. 시퀀스 언패킹 연산자(Seq..
해시란? 해시 함수란 임의 크기 데이터를 고정 크기 값으로 매핑하는 데 사용할 수 있는 함수를 말한다. 충돌 아무리 좋은 해시 함수라도 밑의 그림과 같이 충돌은 발생하게 된다. 이 그림에서 윤아와 서현은 해시 값이 2로 같은 값이 되어 충돌이 발생했다. 이처럼 충돌이 발생할 경우 어떤 식으로 처리하게 되는지 살펴보자. 개별 체이닝 입력 값을 밑의 표와 같이 정해보자. 해시는 키를 해싱한 결과며, '윤아'와 '서현'을 해싱한 결과는 충돌한다고 가정한다. 이 표를 개별 체이닝(Separate Chaining) 방식으로 구현하면 밑의 그림과 같다. 해시 테이블의 기본 방식이기도 한 개별 체이닝은 충돌 발생 시 이 그림과 같이 연결 리스트로 연결하는 방식이다. 충돌이 발생한 '..
k개 정렬 리스트 병합 문제 k개의 정렬된 리스트를 1개의 정렬 리스트로 병합하라 입력 [1 → 4 → 5, 1 → 3 → 4, 2 → 6] 출력 1 → 1 → 2 → 3 → 4 → 4 → 5 → 6 코드 from typing import List import heapq class ListNode: def __init__(self, x): self.val = x self.next = None class Solution: def mergeKLists(self, lists: List[ListNode]) -> ListNode: root = result = ListNode(None) heap = [] for i in range(len(lists)): if lists[i]: heapq.heappush(heap, ..
내부 함수 명명 규칙 내부에서 사용한다는 의미로 PEP8 명명 규칙 기준에 따라 밑줄(_) 하나로 시작하도록 메소드 명을 정한다. class MycircularDeque: """ skip.... """ def _add(self, node: ListNode, new: ListNode): n = node.right node.right = new new.left, new.right = node, n n.left = new def insertFront(self, value: int) -> bool: if self.len == self.k: return False self.len += 1 self._add(self.head, ListNode(value)) return True 출처 파이썬 알고리즘 인터뷰 (글 :..
중복 문자 제거 문제 중복된 문자를 제외하고 사전식 순서로 나열하라. 예제1 입력 "bcabc", 출력 "abc" 예제2 입력 "cbacdcbc", 출력 "acdb" 코드 정답 # 재귀 class Solution: def removeDuplicateLetters(self, s: str) -> str: # 집합으로 정렬 for char in sorted(set(s)): suffix = s[s.index(char):] # 전체 집합과 접미사 집합이 일치할때 분리 진행 if set(s) == set(suffix): return char + self.removeDuplicateLetters(suffix.replace(char, '')) return '' # 스택 import collections class So..
연결 리스트를 이용한 스택 ADT 구현 코드 class Node: def __init__(self, item, next): self.item = item self.next = next class Stack: def __init__(self): self.last = None def push(self, item): self.last = Node(item, self.last) def pop(self): item = self.last.item self.last = self.last.next return item stack = Stack() stack.push(1) stack.push(2) stack.push(3) stack.push(4) stack.push(5) 헷갈린 부분 처음에 Stack 클래스가 만들어지면..
역순 연결 리스트 Ⅱ 문제 인덱스 m에서 n까지를 역순으로 만들어라. 인덱스 m은 1부터 시작한다. 입력 1→2→3→4→5→NULL, m = 2, n = 4 출력 1→4→3→2→5→NULL 코드 class ListNode: def __init__(self, x): self.val = x self.next = None class Solution: def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode: if not head or m == n: return head root = start = ListNode(None) root.next = head for _ in range(m - 1): start = start.next end = star..