├── .idea
├── .gitignore
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── python_practice.iml
└── vcs.xml
├── 00_자료구조 구현
├── BFS_너비우선탐색.py
├── BFS_최단경로용.py
├── DFS_깊이우선탐색.py
├── __pycache__
│ └── graph_그래프.cpython-37.pyc
├── binary_search_tree_이진탐색트리.py
├── binary_tree_이진트리.py
├── chaining용 연결리스트.py
├── circular_queue_배열을 이용해서.py
├── complete_binary_tree_리스트를 이용해서.py
├── deque_리스트를 이용해서.py
├── doubly_linked_list_이중 연결 리스트.py
├── graph_그래프.py
├── hash_function_해시함수몇가지.py
├── hash_table_해시테이블.py
├── linked list_연결 리스트.py
├── priority_queue_힙을 이용해서.py
├── queue_스택을 이용해서.py
├── queue_파이썬의 데크 이용.py
├── singly_linked_list_단일 연결 리스트.py
├── stack_연결리스트를 이용해서.py
├── stack_큐를 이용해서.py
├── stack_파이썬의 데크 이용.py
├── stations.txt
└── 자료구조 노트필기
│ ├── 그래프.txt
│ ├── 배열.txt
│ ├── 연결리스트.txt
│ ├── 자료구조 구현 복습
│ ├── 그래프.py
│ ├── 스택.py
│ ├── 우선순위큐.py
│ ├── 큐.py
│ ├── 트라이.py
│ ├── 노드로 이진트리.py
│ ├── 연결리스트.py
│ ├── 이중연결리스트.py
│ ├── 이진탐색트리.py
│ ├── 해시테이블_chaining.py
│ └── 해시테이블_open addressing.py
│ ├── 추상자료형.txt
│ ├── 트리.txt
│ └── 해시테이블.txt
├── 01_알고리즘 구현
├── heap sort.py
├── memoization.py
├── merge sort.py
├── quick sort.py
├── tabulation.py
└── 알고리즘구현복습
│ ├── breadth first search.py
│ ├── bubble sort.py
│ ├── depth first search.py
│ ├── 다이나믹프로그래밍.py
│ ├── 정렬알고리즘들.py
│ └── 탐색알고리즘들.py
├── 02_leetcode
├── .idea
│ ├── .gitignore
│ ├── inspectionProfiles
│ │ └── profiles_settings.xml
│ ├── leetcode.iml
│ ├── misc.xml
│ ├── modules.xml
│ └── vcs.xml
├── 0001 two sum.py
├── 0002 add two numbers.py
├── 0003 longest substring without repeating characters.py
├── 0005 longest palindromic substring.py
├── 0015 3sum.py
├── 0017 letter combination of a phone number.py
├── 0020 valid parenthesis.py
├── 0021 merge two sorted lists.py
├── 0022 generate parentheses.py
├── 0023 merge k sorted lists.py
├── 0024 swap nodes in pairs.py
├── 0042 trapping rain water.py
├── 0046 permutations.py
├── 0049 group anagrams.py
├── 0053 maximum subarray.py
├── 0077 combinations.py
├── 0092 reverse linked list 2.py
├── 0094 binary tree inorder traversal.py
├── 0096 unique binary search tree.py
├── 0111 minimum depth of binary tree.py
├── 0113 path sum2.py
├── 0116 populating next right pointers in each node.py
├── 0121 best time to buy and sell stock.py
├── 0125 valid palindrome.py
├── 0200 number of islands.py
├── 0206 reverse linked list.py
├── 0225 implement stack using queues.py
├── 0232 implement queue using stacks.py
├── 0234 palindrome linked list.py
├── 0238 product of array except self.py
├── 0310 minimum height trees.py
├── 0316 remove duplicate letters.py
├── 0328 odd even linked list.py
├── 0344 reverse string.py
├── 0347 top k frequent elements.py
├── 0404 sum of left leaves.py
├── 0509 fibonacci number.py
├── 0561 array partition.py
├── 0617 merge two binary trees.py
├── 0622 design circular queue.py
├── 0641 design circular deque.py
├── 0653 two sum4 input is a BST.py
├── 0671 second minimum node in a binary tree.py
├── 0700 search in a binary search tree.py
├── 0706 design hashmap.py
├── 0739 daily temperatures.py
├── 0743 network delay time.py
├── 0771 jewels and stones.py
├── 0787 cheapest flights within K stops.py
├── 0819 most common word.py
├── 0841 keys and rooms.py
├── 0937 reorder log files.py
├── 0938 range sum of bst.py
├── 0968 binary tree cameras.py
├── 0979 distribute coins in binary tree.py
├── 0997 find the town judge.py
├── 1008 construct binary search tree from preorder traversal.py
├── 1022 sum of root to leaf binary numbers.py
├── 1161 maximum level sum of a binary tree.py
└── 1306 jump game3.py
├── 03_codeit
├── 삼송전자 주식 최대이익.py
├── 알고리즘 노트필기.txt
├── 중복되는 항목 찾기.py
└── 투자 귀재 규식이.py
├── 04_프로그래머스
├── 2018카카오_다트게임.py
├── 2018카카오_비밀지도.py
├── 2019카카오_실패율.py
├── 2019카카오_크레인인형뽑기게임.py
├── 2020카카오_키패드누르기.py
├── 9월코드챌린지_두개뽑아서더하기.py
├── 9월코드챌린지_삼각달팽이.py
├── 9월코드챌린지_풍선터트리기.py
├── lev1_2016년.py
├── lev1_x만큼간격이있는n개의숫자.py
├── lev1_가운데글자가져오기.py
├── lev1_같은숫자는싫어.py
├── lev1_나누어떨어지는숫자배열.py
├── lev1_두정수사이의합.py
├── lev1_모의고사.py
├── lev1_문자열내p와y의개수.py
├── lev1_문자열내림차순으로배치하기.py
├── lev1_문자열내마음대로정렬하기.py
├── lev1_문자열다루기기본.py
├── lev1_서울에서김서방찾기.py
├── lev1_소수찾기.py
├── lev1_수박수박수.py
├── lev1_시저암호.py
├── lev1_약수의합.py
├── lev1_예산.py
├── lev1_이상한문자만들기.py
├── lev1_자릿수더하기.py
├── lev1_자연수뒤집어배열로만들기.py
├── lev1_정수내림차순으로배치하기.py
├── lev1_정수제곱근판별.py
├── lev1_제일작은수제거하기.py
├── lev1_직사각형별찍기.py
├── lev1_짝수와홀수.py
├── lev1_체육복.py
├── lev1_최대공약수와최소공배수.py
├── lev1_콜라츠추측.py
├── lev1_핸드폰번호가리기.py
├── lev1_행렬의덧셈.py
├── lev2_124나라의숫자.py
├── lev2_H-index.py
├── lev2_가장큰수.py
├── lev2_소수찾기.py
├── lev2_영어끝말잇기.py
├── lev2_큰수만들기.py
├── lev2_타겟넘버.py
├── lev2_피보나치수.py
├── lev2_기능개발.py
├── lev2_다리를지나는트럭.py
├── lev2_더맵게.py
├── lev2_멀쩡한사각형.py
├── lev2_스킬트리.py
├── lev2_위장.py
├── lev2_전화번호목록.py
├── lev2_주식가격.py
├── lev2_카펫.py
├── lev2_하샤드수.py
├── lev3_섬연결하기.py
├── lev3_베스트앨범.py
└── lve2_프린터.py
├── 05_백준
├── 11725_트리의부모찾기.py
└── 2225_합분해.py
├── 99_etc
├── baekjoon
│ ├── 1000 A+B.py
│ ├── 10171 고양이.py
│ ├── 10818 최소,최대.py
│ ├── 10869 사칙연산.py
│ ├── 10871 X보다 작은 수.py
│ ├── 1110 더하기 사이클.py
│ ├── 14681 quadrant.py
│ ├── 2438 별찍기 -1.py
│ ├── 2557 Hello World.py
│ ├── 2753 윤년.py
│ ├── 2884 알람시계.py
│ └── 6987 WorldCup.py
├── etc
│ ├── .idea
│ │ ├── .gitignore
│ │ ├── etc.iml
│ │ ├── inspectionProfiles
│ │ │ └── profiles_settings.xml
│ │ ├── misc.xml
│ │ ├── modules.xml
│ │ └── vcs.xml
│ └── numpy practice.py
├── hackerrank
│ ├── .idea
│ │ ├── .gitignore
│ │ ├── hackerrank.iml
│ │ ├── inspectionProfiles
│ │ │ └── profiles_settings.xml
│ │ ├── misc.xml
│ │ ├── modules.xml
│ │ └── vcs.xml
│ ├── find a string.py
│ ├── find the runner-up score.py
│ ├── finding the percentage.py
│ ├── list comprehensions.py
│ ├── merge the tools.py
│ ├── nested lists.py
│ ├── string split and join.py
│ ├── swap case.py
│ ├── the minion game.py
│ └── tuples.py
├── leetcode
│ ├── best time to buy and sell stock 2.py
│ └── remove duplicates from sorted array.py
└── programmers
│ └── 완주하지 못한 선수.py
└── README.md
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/python_practice.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/00_자료구조 구현/BFS_너비우선탐색.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 | from graph_그래프 import create_station_graph
3 | # graph 파일에서 만든 지하철 스테이션 그래프를 가져옴
4 |
5 | def bfs(graph, start_node):
6 | queue = deque()
7 |
8 | for station_node in graph.values():
9 | station_node.visited = False
10 | # 일단 모든 노드에 대해 방문하지 않음으로 표시
11 |
12 | queue.append(start_node)
13 | start_node.visited = True
14 | # 큐에 시작노드를 추가해주고 방문여부를 바꿔줌
15 |
16 | while queue:
17 | node_to_search = queue.popleft()
18 | for connected_node in node_to_search.adjacent_stations:
19 | if connected_node.visited == False:
20 | connected_node.visited = True
21 | queue.append(connected_node)
22 |
23 |
24 | stations = create_station_graph("./stations.txt") # stations.txt 파일로 그래프를 만든다
25 |
26 | gangnam_station = stations["강남"]
27 |
28 | # 강남역과 경로를 통해 연결된 모든 노드를 탐색
29 | bfs(stations, gangnam_station)
30 |
31 | # 강남역과 서울 지하철 역들이 연결됐는지 확인
32 | print(stations["강동구청"].visited)
33 | print(stations["평촌"].visited)
34 | print(stations["송도"].visited)
35 | print(stations["개화산"].visited)
36 |
37 | # 강남역과 대전 지하철 역들이 연결됐는지 확인
38 | print(stations["반석"].visited)
39 | print(stations["지족"].visited)
40 | print(stations["노은"].visited)
41 | print(stations["(대전)신흥"].visited)
--------------------------------------------------------------------------------
/00_자료구조 구현/BFS_최단경로용.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 | from graph_그래프 import create_station_graph
3 | # graph 파일에서 만든 지하철 스테이션 그래프를 가져옴
4 |
5 | def bfs(graph, start_node):
6 | queue = deque()
7 |
8 | for station_node in graph.values():
9 | station_node.visited = False
10 | # 일단 모든 노드에 대해 방문하지 않음으로 표시
11 |
12 | queue.append(start_node)
13 | start_node.visited = True
14 | # 큐에 시작노드를 추가해주고 방문여부를 바꿔줌
15 |
16 | while queue:
17 | node_to_search = queue.popleft()
18 | for connected_node in node_to_search.adjacent_stations:
19 | if connected_node.visited == False:
20 | connected_node.visited = True
21 | connected_node.predecessor = node_to_search
22 | queue.append(connected_node)
23 |
24 | def back_track(destination_node):
25 | # 최단 경로를 찾는 백트래킹 함수
26 | str = ''
27 | iterator = destination_node
28 |
29 | while iterator is not None:
30 | str = f'{iterator.station_name} {str}'
31 | iterator = iterator.predecessor
32 |
33 | return str
34 |
35 |
36 | stations = create_station_graph("./stations.txt")
37 |
38 | bfs(stations, stations["을지로3가"])
39 | # 지하철 그래프에서 을지로3가역을 시작노드로 bfs 실행
40 |
41 | print(back_track(stations["강동구청"]))
42 | # 을지로3가에서 강동구청역까지 최단 경로 출력
--------------------------------------------------------------------------------
/00_자료구조 구현/DFS_깊이우선탐색.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 | from graph_그래프 import *
3 | # graph 파일에서 만든 지하철 스테이션 그래프를 가져옴
4 |
5 | def dfs(graph, start_node):
6 | stack = deque()
7 | # 리스트 사용해도 되긴함
8 |
9 | for station_node in graph.values():
10 | station_node.visited = 0
11 |
12 | stack.append(start_node)
13 | start_node.visited = 1
14 | # 큐에 시작노드를 추가해주고 방문여부를 바꿔줌
15 |
16 | while stack:
17 | node_to_search = stack.pop()
18 | node_to_search.visited = 2
19 | # 스택에서 뺄 때 방문여부를 2로 수정
20 | for connected_node in node_to_search.adjacent_stations:
21 | if connected_node.visited == 0:
22 | stack.append(connected_node)
23 | connected_node.visited = 1
24 | # 방문한 적 없으면 stack에 추가하고 방문여부를 1로 바꿔줌
25 |
26 |
27 | stations = create_station_graph("./stations.txt") # stations.txt 파일로 그래프를 만든다
28 |
29 | gangnam_station = stations["강남"]
30 |
31 | # 강남역과 경로를 통해 연결된 모든 노드를 탐색
32 | dfs(stations, gangnam_station)
33 |
34 | # 강남역과 서울 지하철 역들 연결됐는지 확인
35 | print(stations["강동구청"].visited)
36 | print(stations["평촌"].visited)
37 | print(stations["송도"].visited)
38 | print(stations["개화산"].visited)
39 |
40 | # 강남역과 대전 지하철 역들 연결됐는지 확인
41 | print(stations["반석"].visited)
42 | print(stations["지족"].visited)
43 | print(stations["노은"].visited)
44 | print(stations["(대전)신흥"].visited)
--------------------------------------------------------------------------------
/00_자료구조 구현/__pycache__/graph_그래프.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiukeem/python_algorithm/e25d02c53a088640f34a578e63da0f58c0decbaa/00_자료구조 구현/__pycache__/graph_그래프.cpython-37.pyc
--------------------------------------------------------------------------------
/00_자료구조 구현/binary_tree_이진트리.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, data):
3 | self.data = data
4 | self.right_child = None
5 | self.left_child = None
6 |
7 | def traverse_inorder(node):
8 | if node is not None:
9 | traverse_inorder(node.left_child)
10 | print(node.data)
11 | traverse_inorder(node.right_child)
12 |
--------------------------------------------------------------------------------
/00_자료구조 구현/chaining용 연결리스트.py:
--------------------------------------------------------------------------------
1 | # 해시테이블에서 충돌이 일어날 경우를 위한 chaining 용 링크드리스트 구현
2 |
3 | class Node:
4 | def __init__(self, key, value):
5 | self.key = key
6 | self.value = value
7 | self.next = None
8 | self.prev = None
9 |
10 | class LinkedList:
11 | def __init__(self):
12 | self.head = None
13 | self.tail = None
14 |
15 | def find_node_with_key(self, key):
16 | iterator = self.head
17 |
18 | while iterator is not None:
19 | if iterator.key == key:
20 | return iterator
21 | iterator = iterator.next
22 |
23 | return None
24 |
25 | def append(self, key, value):
26 | new_node = Node(key, value)
27 | if self.head is None:
28 | self.head = new_node
29 | self.tail = new_node
30 | else:
31 | self.tail.next = new_node
32 | new_node.prev = self.tail
33 | self.tail = new_node
34 |
35 | def delete(self, node_to_delete):
36 | if self.head is self.tail:
37 | self.head = None
38 | self.tail = None
39 |
40 | elif self.tail is node_to_delete:
41 | self.tail = self.tail.prev
42 | self.tail.next = None
43 |
44 | elif self.head is node_to_delete:
45 | self.head = self.head.next
46 | self.head.prev = None
47 |
48 | else:
49 | node_to_delete.prev.next = node_to_delete.next
50 | node_to_delete.next.prev = node_to_delete.prev
51 |
52 | def __str__(self):
53 | str = ''
54 | iterator = self.head
55 |
56 | while iterator is not None:
57 | str += f'{iterator.key}: {iterator.value}\n'
58 | iterator = iterator.next
59 |
60 | return str
61 |
62 | my_list = LinkedList()
63 | my_list.append('지우', '0521')
64 | my_list.append('지희', '0117')
65 | my_list.append('광호', '0207')
66 | print(my_list)
--------------------------------------------------------------------------------
/00_자료구조 구현/circular_queue_배열을 이용해서.py:
--------------------------------------------------------------------------------
1 | # leetcode 622번 문제
2 |
3 | class MyCircularQueue:
4 |
5 | def __init__(self, k: int):
6 | self.q = [None] * k
7 | self.maxlen = k
8 | self.p1 = 0
9 | # 맨 첫요소 인덱스
10 | self.p2 = 0
11 | # 맨 마지막 요소 다음 인덱스
12 |
13 | def enQueue(self, value: int) -> bool:
14 | """
15 | Insert an element into the circular queue. Return true if the operation is successful.
16 | """
17 | if self.q[self.p2] is None:
18 | self.q[self.p2] = value
19 | self.p2 = (self.p2 + 1) % self.maxlen
20 | return True
21 | else:
22 | return False
23 |
24 | def deQueue(self) -> bool:
25 | """
26 | Delete an element from the circular queue. Return true if the operation is successful.
27 | """
28 | if self.q[self.p1] is None:
29 | return False
30 | else:
31 | self.q[self.p1] = None
32 | self.p1 = (self.p1 + 1) % self.maxlen
33 | return True
34 |
35 | def Front(self) -> int:
36 | """
37 | Get the front item from the queue.
38 | """
39 | if self.q[self.p1] is None:
40 | return -1
41 | else:
42 | return self.q[self.p1]
43 |
44 | def Rear(self) -> int:
45 | """
46 | Get the last item from the queue.
47 | """
48 | if self.q[self.p2 - 1] is None:
49 | return -1
50 | else:
51 | return self.q[self.p2 - 1]
52 |
53 | def isEmpty(self) -> bool:
54 | """
55 | Checks whether the circular queue is empty or not.
56 | """
57 | if self.p1 == self.p2 and self.q[self.p1] is None:
58 | return True
59 | else:
60 | return False
61 |
62 | def isFull(self) -> bool:
63 | """
64 | Checks whether the circular queue is full or not.
65 | """
66 | if self.p1 == self.p2 and self.q[self.p1] is not None:
67 | return True
68 | else:
69 | return False
70 |
71 | # Your MyCircularQueue object will be instantiated and called as such:
72 | # obj = MyCircularQueue(k)
73 | # param_1 = obj.enQueue(value)
74 | # param_2 = obj.deQueue()
75 | # param_3 = obj.Front()
76 | # param_4 = obj.Rear()
77 | # param_5 = obj.isEmpty()
78 | # param_6 = obj.isFull()
--------------------------------------------------------------------------------
/00_자료구조 구현/complete_binary_tree_리스트를 이용해서.py:
--------------------------------------------------------------------------------
1 | def find_left_child(complete_binary_tree, index):
2 | left_child = index * 2
3 | if left_child < len(complete_binary_tree):
4 | return complete_binary_tree[left_child]
5 | return None
6 | # 없을 경우 None 을 반환
7 |
8 | def find_right_child(complete_binary_tree, index):
9 | right_child = index * 2 + 1
10 | if right_child < len(complete_binary_tree):
11 | return complete_binary_tree[right_child]
12 | return None
13 |
14 | def find_parent(complete_binary_tree, index):
15 | parent = index // 2
16 | return complete_binary_tree[parent]
17 |
18 |
19 | # heapify 구현
20 | def swap(complete_binary_tree, index1, index2):
21 | complete_binary_tree[index1], complete_binary_tree[index2] = \
22 | complete_binary_tree[index2], complete_binary_tree[index1]
23 |
24 | def heapify(tree, index, tree_size):
25 | left_child_index = index * 2
26 | right_child_index = index * 2 + 1
27 |
28 | max_index = index
29 | if left_child_index <= tree_size:
30 | if tree[max_index] < tree[left_child_index]:
31 | max_index = left_child_index
32 |
33 | if right_child_index <= tree_size:
34 | if tree[max_index] < tree[right_child_index]:
35 | max_index = right_child_index
36 |
37 | if max_index != index:
38 | swap(tree, index, max_index)
39 | heapify(tree, max_index, tree_size)
40 |
--------------------------------------------------------------------------------
/00_자료구조 구현/graph_그래프.py:
--------------------------------------------------------------------------------
1 | # 지하철역을 이용해서 그래프를 만들어보자.
2 | # 무방향그래프이고, 인접리스트를 사용함
3 |
4 | class StationNode:
5 | def __init__(self, station_name):
6 | self.station_name = station_name
7 | self.adjacent_stations = []
8 | self.visited = False
9 | # 탐색할 때 방문여부 저장
10 | self.predecessor = None
11 | # bfs를 통한 최단경로탐색시 사용
12 |
13 | def add_connection(self, station_to_connect):
14 | # 무방향그래프이므로 양쪽 노드에 서로를 추가
15 | self.adjacent_stations.append(station_to_connect)
16 | station_to_connect.adjacent_stations.append(self)
17 |
18 | def __str__(self):
19 | # 노드의 역 이름 : 인접한 역들 식으로 출력됨
20 | str = f'{self.station_name}: '
21 |
22 | for station in self.adjacent_stations:
23 | str += f'{station.station_name} '
24 |
25 | return str
26 |
27 |
28 | def create_station_graph(input_file):
29 | # 같은 폴더내에 위치한 stations 텍스트파일을 읽어와서 노드로 생성후, 노드들을 저장한 딕셔러리를 return
30 | stations = {}
31 |
32 | with open(input_file) as stations_raw_file:
33 | for line in stations_raw_file:
34 | previous_station = None
35 | subway_line = line.strip().split('-')
36 | # 앞뒤 띄어쓰기 벗기고 -기준으로 쪼갬
37 |
38 | for name in subway_line:
39 | station_name = name.strip()
40 | # 역이름 앞뒤에 스페이스가 있을지 모르니 한번 더 처리
41 | if station_name not in stations:
42 | current_station_node = StationNode(station_name)
43 | stations[station_name] = current_station_node
44 | else:
45 | current_station_node = stations[station_name]
46 |
47 | if previous_station is not None:
48 | stations[station_name].add_connection(previous_station)
49 |
50 | previous_station = current_station_node
51 |
52 | return stations
53 |
54 | stations = create_station_graph("./stations.txt")
55 |
56 | # stations에 저장한 역과 인접역(인접리스트)들 이름 출력
57 | for station in stations.keys():
58 | print(stations[station])
--------------------------------------------------------------------------------
/00_자료구조 구현/hash_function_해시함수몇가지.py:
--------------------------------------------------------------------------------
1 | # 나누기 방법
2 | def hash_function_remainder(key, array_size):
3 | return key % array_size
4 | # 키가 40, 120, 788, 2307이고 배열의 크기를 200으로 설정한다고 하면
5 | # 40, 120, 188, 107이 return된다.
6 |
7 | # 곱셈 방법
8 | def hash_function_multiplication(key, array_size, a):
9 | temp = a * key
10 | temp = temp - int(temp)
11 |
12 | return int(array_size * temp)
13 | # 원리는 간단하다. a는 0에서 1사이의 값이고 요걸 key에 곱해준 뒤 소숫점만 취한다.
14 | # 그럼 key마다 각자의 0에서 1사이 값을 가지겠죵
15 | # 여기다가 배열의 크기를 곱하면 이 값들은 0에서 배열범위 안의 실수가 된다.
16 | # 이 실수에서 소수부분을 버리면 우리가 원하는 범위 내에서 자연수로 변환하기 성공
17 |
--------------------------------------------------------------------------------
/00_자료구조 구현/hash_table_해시테이블.py:
--------------------------------------------------------------------------------
1 | # 파이썬 리스트 자료형을 이용해서 구현
2 | # chaining 사용
3 |
4 | from chaining용 연결리스트 import LinkedList
5 | # 이거 왜 안불러와지지ㅜㅜ
6 |
7 |
8 | class HashTable:
9 | def __init__(self, capacity):
10 | self._capacity = capacity
11 | self._table = [LinkedList() for _ in range(self._capacity)]
12 | # 해시테이블이 사용할 파이썬 리스트이며, 각 인덱스에 비어있는 연결리스트를 생성
13 | # 외부에서 접근하면 안된다는 걸 알릴 때는 앞에 _를 붙여줌
14 |
15 | def _hash_function(self, key):
16 | return hash(key) % self._capacity
17 | # hash는 파이썬 내장함수로, 불변타입형 값에 고유 정수값을 부여함
18 |
19 | def get_linked_list_for_key(self, key):
20 | # 탐색, 삽입 연산에서 쓰이는 헬퍼메소드
21 | # key에 해당하는 인덱스의 연결리스트를 return
22 | index = self._hash_function(key)
23 | return self._table[index]
24 |
25 | def _look_up_node(self, key):
26 | # 헬퍼 메소드2
27 | # 연결리스트에서 해당 키를 가진 노드가 있는지 확인
28 | # 없으면 None 을 return
29 | linked_list = self.get_linked_list_for_key(key)
30 | return linked_list.find_node_with_key(key)
31 |
32 | def look_up_value(self, key):
33 | # 탐색 연산
34 | node_with_the_key = self._look_up_node(key)
35 | if node_with_the_key is None:
36 | # 키에 해당하는 노드가 없는 경우
37 | return None
38 | else:
39 | return node_with_the_key.value
40 |
41 | def insert(self, key, value):
42 | # 삽입 연산
43 | node_with_the_key = self._look_up_node(key)
44 | if node_with_the_key is None:
45 | linked_list = self.get_linked_list_for_key(key)
46 | linked_list.append(key, value)
47 | else:
48 | node_with_the_key.value = value
49 |
50 | def delete_by_key(self, key):
51 | # 삭제 연산
52 | node_with_the_key = self._look_up_node(key)
53 | linked_list = self._get_linked_list_for_key(key)
54 | if node_with_the_key is not None:
55 | linked_list.delete(node_with_the_key)
56 |
57 | def __str__(self):
58 | str = ''
59 | for linked_list in self._table:
60 | str += str(linked_list)
61 | # linked_list는 LinkedList 클래스이므로
62 | # 해당 클래스 내에서 설정한 __str__ 이 바로 적용된다.
63 |
64 | return str
65 |
--------------------------------------------------------------------------------
/00_자료구조 구현/priority_queue_힙을 이용해서.py:
--------------------------------------------------------------------------------
1 | def swap(complete_binary_tree, index1, index2):
2 | complete_binary_tree[index1], complete_binary_tree[index2] = \
3 | complete_binary_tree[index2], complete_binary_tree[index1]
4 |
5 | def heapify(tree, index, tree_size):
6 | left_child_index = index * 2
7 | right_child_index = index * 2 + 1
8 |
9 | max_index = index
10 | if left_child_index <= tree_size:
11 | if tree[max_index] < tree[left_child_index]:
12 | max_index = left_child_index
13 |
14 | if right_child_index <= tree_size:
15 | if tree[max_index] < tree[right_child_index]:
16 | max_index = right_child_index
17 |
18 | if max_index != index:
19 | swap(tree, index, max_index)
20 | heapify(tree, max_index, tree_size)
21 |
22 | def reverse_heapify(tree, index):
23 | # 삽입하는 노드를 힙 속성을 지키는 위치로 이동
24 | parent_index = index // 2
25 | if parent_index < 1:
26 | return
27 | if tree[parent_index] < tree[index]:
28 | swap(tree, parent_index, index)
29 | reverse_heapify(tree, parent_index)
30 |
31 |
32 | class PriorityQueue:
33 | def __init__(self):
34 | self.heap = [None]
35 |
36 | def insert(self, data):
37 | # 데이터를 추가하면 힙속성에 맞는 위치로 옮겨주는 함수
38 | self.heap.append(data)
39 | index = len(self.heap) - 1
40 | reverse_heapify(self.heap, index)
41 |
42 | def extract_max(self):
43 | # 최우선 순위 데이터를 추출
44 | # 크기가 곧 우선순위인 경우(클수록 우선순위가 높음)
45 | swap(self.heap, 1, len(self.heap)-1)
46 | max_value = self.heap.pop()
47 | heapify(self.heap, 1, len(self.heap))
48 |
49 | return max_value
50 |
51 | def __str__(self):
52 | return str(self.heap)
53 |
54 |
55 | # 실행 코드
56 | priority_queue = PriorityQueue()
57 |
58 | priority_queue.insert(6)
59 | priority_queue.insert(9)
60 | priority_queue.insert(1)
61 | priority_queue.insert(3)
62 | priority_queue.insert(10)
63 | priority_queue.insert(11)
64 | priority_queue.insert(13)
65 |
66 | print(priority_queue)
--------------------------------------------------------------------------------
/00_자료구조 구현/queue_스택을 이용해서.py:
--------------------------------------------------------------------------------
1 | # leetcode의 232번 문제
2 | # 파이썬에는 스택이 따로 없으니 리스트를 이용하며 스택의 기능만을 이용한다.
3 |
4 | class Queue:
5 | def __init__(self):
6 | self.input = []
7 | self.output = []
8 |
9 | def push(self, x):
10 | # x를 맨 뒤에 추가
11 | self.input.append(x)
12 |
13 | def pop(self):
14 | # 가장 먼저 들어온 요소를 삭제하고 해당 요소를 return
15 | self.peek()
16 | return self.output.pop()
17 |
18 | def peek(self):
19 | # 가장 먼저 들어온 요소 return
20 | if self.output is None:
21 | while self.input:
22 | self.output.append(self.input.pop())
23 | return self.output[-1]
24 |
25 | def empty(self):
26 | # queue가 비었는지 여부
27 | return not self.output and not self.input
28 |
29 | # 큐를 이용한 스택구현과 다르게 얘는 스택이 두개 필요하다.
30 | # 큐는 앞에서 빼서 뒤에 추가하는게 가능하기 때문에 in-place로 요소를 재정렬할 수 있지만
31 | # 스택은 맨뒤에서 빼서 맨 뒤에 추가하는 것 밖에 못하기 때문에 긴 실린더에 들어있는 공들처럼
32 | # 두 개를 사용해야 재정렬이 가능함
--------------------------------------------------------------------------------
/00_자료구조 구현/queue_파이썬의 데크 이용.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 |
3 | queue = deque()
4 |
5 | # 맨 뒤에 데이터 추가
6 | queue.append('지우')
7 | queue.append('광호')
8 | queue.append('지희')
9 | queue.append('주현')
10 | queue.append('용현')
11 |
12 | print(queue)
13 |
14 | # 맨 앞 데이터에 접근
15 | print(queue[0])
16 |
17 | # 맨 앞 데이터 삭제(삭제하는 데이터를 return)
18 | print(queue.popleft())
19 | print(queue.popleft())
20 | print(queue.popleft())
21 |
22 | print(queue)
23 |
--------------------------------------------------------------------------------
/00_자료구조 구현/singly_linked_list_단일 연결 리스트.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, data):
3 | self.data = data
4 | self.next = None
5 |
6 | class LinkedList:
7 | def __init__(self):
8 | self.head = None
9 | self.tail = None
10 |
11 | def append(self, data): # 마지막에 새로운 노드 추가
12 | new_node = Node(data)
13 | if self.head is None: # 연결리스트가 비어있는 경우
14 | self.head = new_node
15 | self.tail = new_node
16 | else:
17 | self.tail.next = new_node
18 | self.tail = new_node
19 |
20 | def prepend(self, data): # 리스트의 가장 앞에 추가
21 | new_node = Node(data)
22 | if self.head is None:
23 | self.head = new_node
24 | self.tail = new_node
25 | else:
26 | new_node.next = self.head
27 | self.head = new_node
28 |
29 | def insert_after(self, previous_node, data): # 삽입 연산 메소드
30 | new_node = Node(data)
31 | if self.tail is previous_node: # 맨 마지막에 삽입하는 경우 즉, append일 때
32 | self.append(data)
33 | else:
34 | new_node.next = previous_node.next
35 | previous_node.next = new_node
36 |
37 | def delete_after(self, previous_node): # 삭제 연산 메소드
38 | data = previous_node.next.data
39 | if previous_node.next is self.tail:
40 | previous_node.next = None
41 | self.tail = previous_node
42 | else:
43 | previous_node.next = previous_node.next.next
44 |
45 | return data # 삭제하는 노드의 데이터를 return하는게 관습이라고 함
46 |
47 | def find_node_at(self, index): # 접근 연산 메소드
48 | iterator = self.head
49 | for _ in range(index):
50 | iterator = iterator.next
51 | return iterator
52 |
53 | def find_node_with_data(self, data): # 탐색 연산 메소드
54 | iterator = self.head
55 | while iterator is not None:
56 | if iterator.data == data:
57 | return iterator
58 | iterator = iterator.next
59 | return None
60 |
61 | def __str__(self):
62 | to_print = '|'
63 | iterator = self.head
64 |
65 | while iterator is not None:
66 | to_print += f' {iterator.data} |'
67 | iterator = iterator.next
68 | return to_print
69 |
70 | my_list = LinkedList()
71 | my_list.append(2)
72 | my_list.append(3)
73 | my_list.append(5)
74 | my_list.append(7)
75 | print(my_list)
76 |
77 | node_2 = my_list.find_node_at(2)
78 | my_list.insert_after(node_2, 6)
79 | print(my_list)
80 |
81 | head_node = my_list.head
82 | my_list.insert_after(head_node, 14)
83 | print(my_list)
84 |
85 | my_list.delete_after(node_2)
86 | print(my_list)
87 |
88 | second_to_last_node = my_list.find_node_at(3)
89 | print(my_list.delete_after(second_to_last_node))
90 | print(my_list)
--------------------------------------------------------------------------------
/00_자료구조 구현/stack_연결리스트를 이용해서.py:
--------------------------------------------------------------------------------
1 | # 우선 노드부터
2 | class Node:
3 | def __init__(self, item, next):
4 | self.item = item
5 | self.next = next
6 | # 노드는 값(item)과 링크(next)를 가지고 있음
7 |
8 | class Stack:
9 | def __init__(self):
10 | self.last = None
11 |
12 | def push(self, item):
13 | self.last = Node(item, self.last)
14 | # push를 통해 item을 마지막 노드로 집어넣으며, 얘는 다시 기존의 last로 향하는 링크를 가짐
15 |
16 | def pop(self):
17 | item = self.last.item
18 | self.last = self.last.next
19 | return item
20 | # self.last의 값(item)을 반환하며, self.last를 self.last의 다음 노드로 바꾼다.
21 |
22 | stack = Stack()
23 | stack.push(1)
24 | stack.push(2)
25 | stack.push(3)
26 | stack.push(4)
27 | stack.push(5)
28 |
29 | for _ in range(5):
30 | print(stack.pop())
31 | # >> 5, 4, 3, 2, 1 순서로 출력
32 |
33 | # stack의 노드의 링크는 전부 자신의 이전값을 가리킨다. 즉 회살표가 시작되는 노드가 last이다.
34 | # 나중에 들어온 개체가 head가 된다. .next라고 표기하는게 헷갈림을 초래하는 것 같다.
35 | # 구글링하면서 내가 다시 구현해보자. 후입선출의 개념을 좀 더 잘 드러낼 수 있도록
36 |
37 |
38 | # next를 prev로 바꿔서 한번 더
39 | class Node:
40 | def __init__(self, data):
41 | self.data = data
42 | self.prev = None
43 |
44 | def __str__(self):
45 | return str(self.data)
46 |
47 | class Stack2:
48 | def __init__(self):
49 | self.head = None
50 | self.top = 0
51 |
52 | def push(self, data):
53 | new_node = Node(data)
54 | if self.head == None:
55 | # 첫 push인 경우
56 | self.head = new_node
57 | return
58 |
59 | new_node.prev = self.head
60 | # push할 노드의 링크를 head로 지정
61 | self.head = new_node
62 |
63 | def pop(self):
64 | data = self.head.data
65 | self.head = self.head.prev
66 | return data
67 |
68 | def __str__(self):
69 | node = self.head
70 | if node == None:
71 | return None
72 | print_stack = '<=> [ '
73 | while node:
74 | print_stack += str(node.data) + ' '
75 | node = node.prev
76 | print_stack += ']'
77 | return print_stack
78 |
79 | # 위의 코드보다 훨씬 번잡하다. 근데 이렇게 한번 더 써보고 나서 위의 코드를 다시 읽으니 이해가 됐다!
80 | # 좋아좋아 스택 문제 다 덤벼
81 |
82 | stack = Stack2()
83 | stack.push(1)
84 | stack.push(2)
85 | stack.push(3)
86 | stack.push(4)
87 | stack.push(5)
88 |
89 | for _ in range(5):
90 | print(stack.pop())
91 |
92 | if __name__=="__main__":
93 | m_stack = Stack2()
94 | m_stack.push(5)
95 | m_stack.push(4)
96 | m_stack.push(3)
97 | print(m_stack)
98 | print('Stack pop :', m_stack.pop())
99 | print(m_stack)
100 | print('Stack pop :', m_stack.pop())
101 | print('Stack pop :', m_stack.pop())
102 | print(m_stack)
--------------------------------------------------------------------------------
/00_자료구조 구현/stack_큐를 이용해서.py:
--------------------------------------------------------------------------------
1 | # leetcode의 225번 문제
2 | # 파이썬에는 큐가 따로 없으니 데크를 이용하며 큐의 기능만을 이용한다.
3 |
4 | from collections import deque
5 |
6 | class Stack:
7 | def __init__(self):
8 | self.queue = deque()
9 |
10 | def push(self, x):
11 | # x를 맨 뒤에 추가
12 | self.queue.append(x)
13 | for _ in range(len(self.queue) - 1):
14 | self.queue.append(self.queue.popleft())
15 | # 새로 넣는 애를 제외하고 나머지를 앞에서 꺼내서 뒤로 추가.
16 | # 요소들 순서를 재정렬하는 것
17 |
18 | def pop(self):
19 | # 가장 마지막으로 들어온 요소를 삭제하고 해당 요소를 return
20 | return self.queue.popleft()
21 |
22 | def top(self):
23 | # 가장 마지막으로 들어온 요소 return
24 | return self.queue[0]
25 |
26 | def empty(self):
27 | # 스택이 비었는지 여부
28 | return not self.queue
--------------------------------------------------------------------------------
/00_자료구조 구현/stack_파이썬의 데크 이용.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 |
3 | stack = deque()
4 |
5 | # 맨 끝에 데이터 추가
6 | stack.append('J')
7 | stack.append('E')
8 | stack.append('E')
9 | stack.append('W')
10 | stack.append('O')
11 | stack.append('O')
12 |
13 | print(stack)
14 |
15 | # 맨 끝 데이터에 접근
16 | print(stack[-1])
17 |
18 | # 맨 끝 데이터 삭제
19 | print(stack.pop())
20 | print(stack.pop())
21 | print(stack.pop())
22 |
23 | print(stack)
24 |
25 | # 큐와 다르게 스택은 동적배열과 이중연결리스트가 같은 시간복잡도를 가진다
26 | # 데크는 이중연결리스트 자료구조를 사용하고 파이썬의 리스트는 동적배열을 사용하므로
27 | # 파이썬의 리스트 자료형을 이용해도 스택을 구현할 수 있다.
28 | # stack = deque() 대신 stack = []를 해도 똑같다 (메소드 이름도 똑같음)
29 |
--------------------------------------------------------------------------------
/00_자료구조 구현/stations.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiukeem/python_algorithm/e25d02c53a088640f34a578e63da0f58c0decbaa/00_자료구조 구현/stations.txt
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/연결리스트.txt:
--------------------------------------------------------------------------------
1 | -----------연결리스트(linked list)-----------
2 | * 연결리스트는 파이썬으로 클래스를 구현하면서 공부했기 때문에 노트 필기는 별로 없다. 연결리스트 구현 파일을 참조
3 |
4 | 연결리스트도 데이터를 순서대로 저장하며 요소를 계속 추가할 수 있다.
5 |
6 | 노드는 데이터와 링크(포인터, next)로 이루어져있음. 보통은 .next메소드를 통해 구현. node1.next = node2 로 설정을 해줬다면 node1.next는 node2에 대한 레퍼런스가 된다고 말할 수 있다. 노드들은 배열과 달리 메모리에 물리적으로 연속되게 저장되지 않는다. 각각 독립적으로 존재하지만 다음 노드에 대한 레퍼런스를 지니기 때문에 다음 노드가 어디 저장되어 있는지 알 수 있다. 첫번째 노드를 head라고 한다.
7 |
8 | -링크드 리스트의 접근 연산은 비효율적이다. 배열에서는 인덱스 주소를 계산해서 O(1)에 접근할 수 있었지만 얘는 head에서 시작해서 원하는 인덱스 횟수만큼 next로 이동해야하기 때문. 시간복잡도는 O(n)
9 |
10 | -탐색(인덱스가 아닌 값으로 찾는 것)의 경우도 배열과 마찬가지로 선형 탐색을 사용한다. 시간복잡도는 O(n)
11 |
12 | -삽입 연산과 삭제 연산은 해당 노드 앞뒤의 레퍼런스만 변경해주면 되기 때문에 O(1)이지만 사실상 previous node까지 접근한 다음에야 삽입/삭제 연산을 수행할 수 있으므로 O(n)이라고 보는게 맞다
13 |
14 | -특수한 경우인 맨앞과 맨뒤일 때 : 맨앞은 삽입, 삭제 모두 O(1)이다. head노드는 한번에 접근할 수 있음. tail도 똑같을거라는 생각이 들지만 조금 다르다. 만약 삽입일 경우(=append) tail노드를 가져와서 링크를 이어주면 되기 때문에 O(1)이지만 마지막을 삭제하는 경우 마지막의 앞 노드(맨끝에서 두번째 노드)에 접근해야하기 때문에 O(n)이다. 이건 지금 singly linked list여서 그렇고 double linked list를 사용하면 이 부분은 O(1)로 개선할 수 있을 것 같다. 정리하자면 맨 앞에 삽입, 삭제는 O(1) 맨 뒤 삽입도 O(1), 맨 뒤 삭제만 O(n)
15 |
16 | 지금까지 본, 노드가 다음 노드의 레퍼런스만 가지고 있는 연결리스트를 singly linked list라고 한다. 이와 달리 다음 노드뿐만 아니라 이전 노드의 레퍼런스까지 가지고 있는 연결리스트는 doubly linked list다. 싱글이 왼쪽이서 오른쪽으로만 향한다치면 얘는 쌍방향 화살표를 가지고 있는 격.
17 |
18 | 이중 연결리스트를 구현할 건데 우선 접근연산과 탐색연산은 단일 연결리스트와 동일하다. 접근연산의 경우 중간을 기준으로 인덱스가 앞부분인지 뒷부분인지 판단하에 접근하면 더 빠르지 않을까 생각했는데 그래봤자 n/2 이니 시간복잡도는 똑같다. 탐색연산도 마찬가지다. 양쪽끝에서부터 시작해서 동시에 선형탐색을 진행하면 더 빠르지 않을까 생각했는데 그것도 n/2다. 뭐 조금 빠르게 하기 위해 이런식으로 메소드 코드를 바꿔서 짤 수는 있겠지만 어쨌든 시간복잡도는 동일하다.
19 |
20 | 삭제 연산의 경우 단일 연결리스트와는 다르게 지우려는 노드의 앞노드(previous node)가 아닌 해당 노드를 파라미터로 넣는다. prev와 next를 둘 다 가지고 있어서 해당 노드만 있어도 previous node에 접근할 수 있기 때문
21 |
22 | 이중연결리스트의 head/tail 의 삽입 및 삭제는 전부 O(1)이다. 단일 연결리스트와는 tail삭제의 시간복잡도가 다르다. prev를 통해 뒤에서 앞으로 접근할 수 있기 때문
23 |
24 | 두 연결리스트의 추가적 공간을 살펴보자. 추가적 공간이란 자료구조가 사용하는 공간 중 실제 저장하려는 데이터를 제외한 정보가 저장되는 공간을 말한다. 노드의 링크(레퍼런스)가 차지하는 공간이 추가적 공간이 되겠지. 단일 연결리스트는 O(n)의 추가공간을 사용하고 이중 연결리스트는 O(2n) -> O(n) 공간을 사용한다. 공간복잡도는 같지만 실제로는 이중 연결리스트가 더 많이 사용한다고 보면 된다.
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/자료구조 구현 복습/스택.py:
--------------------------------------------------------------------------------
1 | # 파이썬의 리스트 이용(동적배열)
2 | class Stack:
3 | def __init__(self):
4 | self.stack = []
5 |
6 | # 맨 뒤에 삽입 O(1)
7 | def append(self, data):
8 | self.stack.append(data)
9 |
10 | # 맨 뒤 요소에 접근 O(1)
11 | def last_data(self):
12 | return self.stack[-1]
13 |
14 | # 맨 뒤 요소 추출 O(1)
15 | def pop_last(self):
16 | return self.pop()
17 |
18 | # 이중연결리스트로 구
19 | class Node:
20 | def __init__(self, data):
21 | self.data = data
22 | self.next = None
23 | self.prev = None
24 |
25 | class Stack_2:
26 | def __init__(self):
27 | self.head = None
28 | self.tail = None
29 | self.size = 0
30 |
31 | # 맨 뒤에 삽입 O(1)
32 | def append(self, data):
33 | node = Node(data)
34 | if self.size == 0:
35 | self.head = node
36 | self.tail = node
37 | else:
38 | self.tail.next, node.prev = node, self.tail
39 |
40 | self.size += 1
41 |
42 | # 맨 뒤 요소에 접근 O(1)
43 | def last_data(self):
44 | if self.tail:
45 | return self.tail.data
46 | else:
47 | return None
48 |
49 | # 맨 뒤 요소 추출 O(1)
50 | def pop_last(self):
51 | if self.size == 0:
52 | return None
53 | elif self.size == 1:
54 | ans = self.tail.data
55 | self.head, self.tail = None, None
56 | self.size -= 1
57 | return ans
58 | else:
59 | prev_node = self.tail.prev
60 | prev_node.next = None
61 | ans = self.tail.data
62 | self.tail = prev_node
63 | self.size -= 1
64 | return ans
65 |
66 | # 데크(이중연결리스트) 이용
67 | from collections import deque
68 | stack = deque()
69 | stack.append()
70 | stack.pop()
71 | stack[-1]
72 |
73 | # 큐를 이용. 파이썬에는 큐가 따로 없으니 데크를 사용하되, 큐의 기능만을 이용(맨끝추가, 맨앞접근, 맨앞추출)
74 | # 스택과 큐를 서로를 이용해서 구현할 때는 실린더를 생각해주면 좀 더 쉽다.
75 | class Stack:
76 | def __init__(self):
77 | self.queue = deque()
78 |
79 | def append(self, data):
80 | self.queue.append(data)
81 | for _ in range(len(self.queue) - 1):
82 | element = self.queue.popleft()
83 | self.queue.append(element)
84 |
85 | def last_data(self):
86 | return self.queue[0]
87 |
88 | def pop_last(self):
89 | return self.queue.popleft()
90 |
91 |
92 |
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/자료구조 구현 복습/우선순위큐.py:
--------------------------------------------------------------------------------
1 | # 힙으로 구현
2 | # 리스트로 구현된 완전이진트리를 받는다.
3 |
4 | # 리스트에 힙속성 부여(무작위 리스트에 이걸 맨 끝 인덱스부터 n번 돌려야 힙 완성)
5 | def heapify(tree, idx, end_idx):
6 | left_child_idx = idx * 2
7 | right_child_idx = idx * 2 + 1
8 |
9 | max_idx = idx
10 | if left_child_idx < end_idx and tree[left_child_idx] > tree[max_idx]:
11 | max_idx = left_child_idx
12 |
13 | if right_child_idx < end_idx and tree[right_child_idx] > tree[max_idx]:
14 | max_idx = right_child_idx
15 |
16 | if max_idx != idx:
17 | tree[max_idx], tree[idx] = tree[idx], tree[max_idx]
18 | heapify(tree, max_idx, end_idx)
19 |
20 | def heapify_upward(heap, idx):
21 | parent_idx = idx // 2
22 | if parent_idx < 1:
23 | return
24 | if heap[parent_idx] < heap[idx]:
25 | heap[parent_idx], heap[idx] = heap[idx], heap[parent_idx]
26 | heapify_upward(heap, parent_idx)
27 |
28 |
29 | class PriorityQueue:
30 | def __init__(self):
31 | self.heap = [None]
32 |
33 | def insert(self, data):
34 | self.heap.append(data)
35 | heapify_upward(self.heap, len(self.heap) - 1)
36 |
37 | def extract_max(self):
38 | idx = len(self.heap) - 1
39 | self.heap[1], self.heap[idx] = self.heap[idx], self.heap[1]
40 | max_value = self.heap.pop()
41 | heapify(self.heap, 1, len(self.heap) - 1)
42 | return max_value
43 |
44 | def __str__(self):
45 | return str(self.heap)
46 |
47 | pq = PriorityQueue()
48 | pq.insert(3)
49 | pq.insert(2)
50 | pq.insert(45)
51 | pq.insert(17)
52 | pq.insert(31)
53 | pq.insert(106)
54 | pq.insert(63)
55 | pq.insert(5)
56 | print(pq.extract_max())
57 | print(pq.extract_max())
58 | print(pq.extract_max())
59 |
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/자료구조 구현 복습/큐.py:
--------------------------------------------------------------------------------
1 | # 데크(이중연결리스트) 이용
2 | from collections import deque
3 | queue = deque()
4 | queue.append()
5 | queue[0]
6 | queue.popleft()
7 |
8 | # 스택으로 구현 - 파이썬에는 스택이 따로 없으니 리스트를 사용(데크 사용해도 무방)
9 | # 큐는 한쪽에서 들어오고 반대쪽에서 나가기 때문에 큐 한개만으로 스택을 구현할 수 있지만
10 | # 스택은 들어간쪽으로 나오니 스택 한개로는 안쪽에 접근할 수가 없다(한쪽이 막힌 실린더처럼) 그래서 스택을 이용한 큐 구현시에는 스택이 2개 필요함
11 | # 공 옮길 때처럼 완전히 뒤집어서 다른 실린더에 집어넣으면 순서관계가 뒤바뀌게 됨
12 |
13 | class Queue:
14 | def __init__(self):
15 | self.i_stack = []
16 | self.o_stack = []
17 |
18 | def append(self, data):
19 | self.i_stack.append(data)
20 |
21 | def first_data(self):
22 | if not self.o_stack:
23 | while self.i_stack:
24 | self.o_stack.append(self.i_stack.pop())
25 |
26 | return self.o_stack[-1]
27 |
28 | def pop_first(self):
29 | self.first_data()
30 | return self.o_stack.pop()
31 |
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/자료구조 구현 복습/트라이.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 |
3 | class Node:
4 | def __init__(self, char):
5 | self.char = char
6 | self.children = {}
7 | self.terminal = None
8 |
9 | class Trie:
10 | def __init__(self):
11 | self.root = Node(None)
12 |
13 | def insert(self, string):
14 | current_node = self.root
15 | for char in string:
16 | if char not in current_node.children.keys():
17 | current_node.children[char] = Node(char)
18 | current_node = current_node.children[char]
19 | current_node.terminal = string
20 |
21 | def start_with(self, prefix):
22 | current_node = self.root
23 | ans = []
24 |
25 | for char in prefix:
26 | if char in current_node.children.keys():
27 | current_node = current_node.children[char]
28 | else:
29 | return None
30 |
31 | root_node = current_node
32 | queue = deque()
33 | queue.append(root_node)
34 | # 그래프가 아니라 트리형태니까 visited 프로퍼티는 없어도 괜찮을 것 같다.
35 |
36 | while queue:
37 | node_to_search = queue.popleft()
38 | if node_to_search.terminal:
39 | ans.append(node_to_search.terminal)
40 | for node in node_to_search.children.values():
41 | queue.append(node)
42 |
43 | return ans
44 |
45 | def search(self, string):
46 | current_node = self.root
47 | for char in string:
48 | if char not in current_node.children.keys():
49 | return False
50 | current_node = current_node.children[char]
51 |
52 | return True if current_node.terminal else False
53 |
54 | trie = Trie()
55 | trie.insert('hello')
56 | trie.insert('hell')
57 | trie.insert('sunny')
58 | trie.insert('sunshine')
59 | trie.insert('swimming')
60 | print(trie.start_with('sun'))
61 | print(trie.start_with('s'))
62 | print(trie.search('hell'))
63 | print(trie.search('swim'))
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/자료구조 구현 복습/노드로 이진트리.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, data):
3 | self.data = data
4 | self.right = None
5 | self.left = None
6 |
7 | def __str__(self):
8 | return self.data
9 |
10 | class BinaryTree:
11 | def __init__(self):
12 | self.root = None
13 |
14 | # 순회구현
15 | def preorder_traversal(self, node):
16 | if node:
17 | print(node)
18 | self.preorder_traversal(node.left)
19 | self.preorder_traversal(node.right)
20 |
21 | def inorder_traversal(self, node):
22 | if node:
23 | self.inorder_traversal(node.left)
24 | print(node)
25 | self.inorder_traversal(node.right)
26 |
27 | def postorder_traversal(self, node):
28 | if node:
29 | self.postorder_traversal(node.left)
30 | self.postorder_traversal(node.right)
31 | print(node)
32 |
33 |
34 |
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/자료구조 구현 복습/연결리스트.py:
--------------------------------------------------------------------------------
1 | # 노드 구현
2 | class Node:
3 | def __init__(self, value):
4 | self.value = value
5 | self.next = None
6 |
7 | def __str__(self):
8 | return str(self.value)
9 |
10 | # 단일연결리스트 구현
11 | class SinglyLinkedList:
12 | def __init__(self, value):
13 | new_node = Node(value)
14 | self.head = new_node
15 | self.list_size = 1
16 |
17 | def access(self, idx):
18 | if idx >= self.list_size:
19 | return
20 | iter = self.head
21 | for _ in range(idx):
22 | iter = iter.next
23 | return iter
24 |
25 | def search(self, value):
26 | iter = self.head
27 | while iter:
28 | if iter.value == value:
29 | return iter
30 | else:
31 | iter = iter.next
32 | return None
33 |
34 | def insert(self, value, idx):
35 | node = Node(value)
36 | if idx >= self.list_size:
37 | return
38 | prev_node = self.access(idx-1)
39 | prev_node.next, node.next = node, prev_node.next
40 | self.list_size += 1
41 |
42 | def delete(self, idx):
43 | prev_node = self.access(idx-1)
44 | prev_node.next = prev_node.next.next
45 | self.list_size -= 1
46 |
47 | def insert_head(self, value):
48 | node = Node(value)
49 | self.head, node.next = node, self.head
50 | self.list_size += 1
51 |
52 | def insert_tail(self, value):
53 | node = Node(value)
54 | iter = self.head
55 | while iter.next:
56 | iter = iter.next
57 | iter.next = node
58 | self.list_size += 1
59 |
60 | def delete_head(self):
61 | if self.head and self.head.next:
62 | self.head = self.head.next
63 | self.list_size -= 1
64 |
65 | def delete_tail(self):
66 | tail_prev = self.access(self.list_size - 2)
67 | tail_prev.next = None
68 | self.list_size -= 1
69 |
70 | def __str__(self):
71 | to_print = '['
72 | iter = self.head
73 | while iter:
74 | to_print += str(iter)
75 | if iter.next:
76 | to_print += ", "
77 | iter = iter.next
78 |
79 | to_print += ']'
80 | return to_print
81 |
82 | my_list = SinglyLinkedList(3)
83 | print(my_list)
84 | my_list.insert_tail(4)
85 | my_list.insert(12, 1)
86 | print(my_list)
87 | my_list.insert_head(8)
88 | node = my_list.search(3)
89 | print(node, my_list)
90 |
91 |
92 |
--------------------------------------------------------------------------------
/00_자료구조 구현/자료구조 노트필기/자료구조 구현 복습/해시테이블_open addressing.py:
--------------------------------------------------------------------------------
1 | class HashTableOA:
2 | def __init__(self, size):
3 | self._size = size
4 | self._table = [() for _ in range(self._size)]
5 | self.idx = None
6 |
7 | def hash(self, key):
8 | return key % self._size
9 |
10 | def search_with_key(self, key):
11 | idx = self.hash(key)
12 | while self._table[idx] != () and idx < self._size:
13 | if self._table[idx][0] == key:
14 | self.idx = idx
15 | return self._table[idx]
16 | idx = (idx + 1) % self._size
17 | self.idx = idx
18 | return None
19 |
20 | def insert(self, key, value):
21 | element = (key, value)
22 | _ = self.search_with_key(key)
23 | self._table[self.idx] = element
24 | self.idx = None
25 | return
26 |
27 | def delete(self, key):
28 | existing = self.search_with_key(key)
29 | if existing:
30 | self._table[self.idx] = 'deleted'
31 | self.idx = None
32 | return
33 |
34 | def __str__(self):
35 | to_print = "["
36 | for element in self._table:
37 | if element == ():
38 | to_print += "()"
39 | elif element == 'deleted':
40 | to_print += "(" + element + ")"
41 | else:
42 | to_print += "(" + str(element[0]) +": " + str(element[1]) + ")"
43 | return to_print
44 |
45 | ht = HashTableOA(5)
46 | ht.insert(2, 7)
47 | ht.insert(79, 1)
48 | print(ht)
49 | ht.insert(47, 9)
50 | print(ht)
51 | ht.insert(72, 10)
52 | print(ht)
53 | ht.delete(47)
54 | print(ht)
55 | print(ht.search_with_key(72))
56 | # 너무 재밌당 내가 만들었는데도 신기해 호호
--------------------------------------------------------------------------------
/01_알고리즘 구현/heap sort.py:
--------------------------------------------------------------------------------
1 | # heapify 구현 (오름차순 정렬용)
2 | def swap(complete_binary_tree, index1, index2):
3 | complete_binary_tree[index1], complete_binary_tree[index2] = \
4 | complete_binary_tree[index2], complete_binary_tree[index1]
5 |
6 | def heapify(tree, index, tree_size):
7 | left_child_index = index * 2
8 | right_child_index = index * 2 + 1
9 |
10 | max_index = index
11 | if left_child_index < tree_size:
12 | if tree[max_index] < tree[left_child_index]:
13 | max_index = left_child_index
14 |
15 | if right_child_index < tree_size:
16 | if tree[max_index] < tree[right_child_index]:
17 | max_index = right_child_index
18 |
19 | if max_index != index:
20 | swap(tree, index, max_index)
21 | heapify(tree, max_index, tree_size)
22 |
23 | def heapsort(tree):
24 | tree_size = len(tree)
25 |
26 | # 리스트를 힙으로 만든다
27 | for i in range(tree_size-1, 0, -1):
28 | heapify(tree, i, tree_size)
29 |
30 | for i in range(tree_size-1, 1, -1):
31 | swap(tree, 1, i)
32 | heapify(tree, 1, i)
33 |
34 |
35 | data_to_sort = [None, 6, 1, 4, 7, 10, 3, 8, 5, 1, 5, 7, 4, 2, 1]
36 | heapsort(data_to_sort)
37 | print(data_to_sort)
--------------------------------------------------------------------------------
/01_알고리즘 구현/memoization.py:
--------------------------------------------------------------------------------
1 | # memoization 방식으로 피보나치 수열 값 구하는 함수 구현
2 |
3 | # 내 풀이
4 | def fib_memo(n, cache):
5 | if n < 3:
6 | return 1
7 |
8 | if n in cache:
9 | return cache[n]
10 |
11 | return fib_memo(n - 2, cache) + fib_memo(n - 1, cache)
12 |
13 |
14 | def fib(n):
15 | # n번째 피보나치 수를 담는 사전
16 | fib_cache = {}
17 | for i in range(n):
18 | if i < 3:
19 | fib_cache[i] = 1
20 | else:
21 | fib_cache[i] = fib_cache[i - 1] + fib_cache[i - 2]
22 |
23 | return fib_memo(n, fib_cache)
24 | # 코드는 제대로 짰지만 왜 함수가 두개로 나뉘는지에 대한 파악을 제대로 못했다.
25 |
26 |
27 | # 코드잇 풀이
28 | def fib_memo(n, cache):
29 | # base case
30 | if n < 3:
31 | return 1
32 |
33 | # 이미 n번째 피보나치를 계산했으면:
34 | # 저장된 값을 바로 리턴한다
35 | if n in cache:
36 | return cache[n]
37 |
38 | # 아직 n번째 피보나치 수를 계산하지 않았으면:
39 | # 계산을 한 후 cache에 저장
40 | cache[n] = fib_memo(n - 1, cache) + fib_memo(n - 2, cache)
41 |
42 | # 계산한 값을 리턴한다
43 | return cache[n]
44 |
45 |
46 | def fib(n):
47 | # n번째 피보나치 수를 담는 사전
48 | fib_cache = {}
49 |
50 | return fib_memo(n, fib_cache)
51 | # fib에는 사전만 만들어주고 fib_memo에서 새로 연산되는 값들을 return 함과 동시에 cache에 저장해준다.
52 | # 근데 함수 하나만 있으면 되는거 아냐..? 왜 이렇게 하시는거에요
53 | # 아! 한개로 합쳐서 작성하면 재귀를 들어갈 때마다 cache가 빈 딕트로 정의되어버린다.
54 | # dict는 재귀 바깥에서 계속 몸집을 불려나가야 하므로 이런 구조를 취하는 것
--------------------------------------------------------------------------------
/01_알고리즘 구현/merge sort.py:
--------------------------------------------------------------------------------
1 | def merge(list1, list2):
2 | output = []
3 | while list1 and list2:
4 | if list1[0] <= list2[0]:
5 | output.append(list1[0])
6 | del list1[0]
7 | else:
8 | output.append(list2[0])
9 | del list2[0]
10 |
11 | rest = list1 or list2
12 | output += rest
13 | return output
14 |
15 |
16 | # 합병 정렬
17 | def merge_sort(my_list):
18 | if len(my_list) == 1:
19 | return my_list
20 | mid = len(my_list) // 2
21 | return merge(merge_sort(my_list[:mid]), merge_sort(my_list[mid:]))
22 |
23 |
24 | # 테스트
25 | print(merge_sort([1, 3, 5, 7, 9, 11, 13, 11]))
26 | print(merge_sort([28, 13, 9, 30, 1, 48, 5, 7, 15]))
27 | print(merge_sort([2, 5, 6, 7, 1, 2, 4, 7, 10, 11, 4, 15, 13, 1, 6, 4]))
28 |
--------------------------------------------------------------------------------
/01_알고리즘 구현/quick sort.py:
--------------------------------------------------------------------------------
1 | # 두 요소의 위치를 바꿔주는 helper function
2 | def swap_elements(my_list, index1, index2):
3 | my_list[index1], my_list[index2] = my_list[index2], my_list[index1]
4 |
5 |
6 | # 퀵 정렬에서 사용되는 partition 함수
7 | def partition(my_list, start, end):
8 | b = start
9 | i = start
10 | p = my_list[end]
11 |
12 | while i < end:
13 | if my_list[i] < p:
14 | swap_elements(my_list, i, b)
15 | b += 1
16 | i += 1
17 |
18 | swap_elements(my_list, b, end)
19 | return b
20 |
21 |
22 | # 퀵 정렬
23 | def quicksort(my_list, start, end):
24 | if end - start < 1:
25 | return
26 |
27 | b = partition(my_list, start, end)
28 | quicksort(my_list, start, b - 1)
29 | quicksort(my_list, b + 1, end)
30 |
31 |
32 | # 테스트 1
33 | list1 = [1, 3, 5, 7, 9, 11, 13, 11]
34 | quicksort(list1, 0, len(list1) - 1)
35 | print(list1)
36 |
37 | # 테스트 2
38 | list2 = [28, 13, 9, 30, 1, 48, 5, 7, 15]
39 | quicksort(list2, 0, len(list2) - 1)
40 | print(list2)
41 |
42 | # 테스트 3
43 | list3 = [2, 5, 6, 7, 1, 2, 4, 7, 10, 11, 4, 15, 13, 1, 6, 4]
44 | quicksort(list3, 0, len(list3) - 1)
45 | print(list3)
--------------------------------------------------------------------------------
/01_알고리즘 구현/tabulation.py:
--------------------------------------------------------------------------------
1 | # tabulation 방식으로 피보나치 수열 값 구하는 함수 구현
2 |
3 | def fib_tab(n):
4 | table = {}
5 | for i in range(1, n + 1):
6 | if i < 3:
7 | table[i] = 1
8 | else:
9 | table[i] = table[i - 1] + table[i - 2]
10 |
11 | return table[n]
12 |
13 |
14 | # 공간최적화 O(1)도 가능하다
15 | def fib_optimized(n):
16 | current = 1
17 | prev = 0
18 | for _ in range(n - 1):
19 | current, prev = current + prev, current
20 |
21 | return current
22 |
23 |
24 | # 테스트
25 | print(fib_optimized(16))
26 | print(fib_optimized(53))
27 | print(fib_optimized(213))
28 |
--------------------------------------------------------------------------------
/01_알고리즘 구현/알고리즘구현복습/breadth first search.py:
--------------------------------------------------------------------------------
1 | # 그래프 구현에서 만든 stations 를 그대로 사용
2 | # stations는 dict이며,stations[name] = node 로 저장되어 있음
3 |
4 | # bfs는 큐를 사용하므로 데크 사용
5 | from collections import deque
6 |
7 | def bfs(graph, start_node):
8 | queue = deque()
9 |
10 | for station_node in graph.values():
11 | station_node.visited = False
12 |
13 | queue.append(start_node)
14 | start_node.visited = True
15 |
16 | while queue:
17 | node_to_search = queue.popleft()
18 | for connected_node in node_to_search.adjacent_stations:
19 | if connected_node.visited == False:
20 | connected_node.visited = True
21 | queue.append(connected_node)
22 |
23 | # predecessor 프로퍼티를 사용하는 최단경로용 bfs 한번더
24 | def bfs(graph, start_node):
25 | queue = deque()
26 |
27 | for station_node in graph.values():
28 | station_node.visited = False
29 |
30 | queue.append(start_node)
31 | start_node.visited = True
32 |
33 | while queue:
34 | node_to_search = queue.popleft()
35 | for connected_node in node_to_search.adjacent_stations:
36 | if connected_node.visited == False:
37 | connected_node.visited = True
38 | connected_node.predecessor = node_to_search
39 | queue.append(connected_node)
40 |
41 | def back_track(destination_node):
42 | to_print = ""
43 | iter = destination_node
44 |
45 | while iter:
46 | to_print += destination_node.station_name
47 | iter = iter.predecessor
48 |
49 | return to_print
--------------------------------------------------------------------------------
/01_알고리즘 구현/알고리즘구현복습/bubble sort.py:
--------------------------------------------------------------------------------
1 | def swap(list, idx1, idx2):
2 | list[idx1], list[idx2] = list[idx2], list[idx1]
3 |
4 | def bubble_sort(list):
5 | for i in range(len(list)-1, 0, -1):
6 | for j in range(i):
7 | if list[j] > list[j + 1]:
8 | swap(list, j, j + 1)
9 |
10 | return list
11 |
12 | array = [4, 5, 1, 87, 23, 103, 143, 21, 9, 43, 5]
13 | print(bubble_sort(array))
14 |
--------------------------------------------------------------------------------
/01_알고리즘 구현/알고리즘구현복습/depth first search.py:
--------------------------------------------------------------------------------
1 | # 그래프 구현에서 만든 stations 를 그대로 사용
2 | # stations는 dict이며,stations[name] = node 로 저장되어 있음
3 |
4 | def dfs(graph, start_node):
5 | stack = []
6 |
7 | for station_node in graph.values():
8 | station_node.visited = 0
9 |
10 | stack.append(start_node)
11 | start_node.visited = 1
12 |
13 | while stack:
14 | node_to_search = stack.pop()
15 | node_to_search.visited = 2
16 | for connected_node in node_to_search.adjacent_stations:
17 | if connected_node.visited == 0:
18 | stack.append(connected_node)
19 | connected_node.visited = 1
20 |
21 | # 재귀 사용한 dfs
22 | def dfs_visit(adjacent_list, element, visited):
23 | visited.append(element)
24 | for next_ele in adjacent_list[element]
25 | if next_ele not in visited:
26 | dfs_visit(adjacent_list, next_ele, visited)
27 |
28 | def dfs(adjacent_list, start):
29 | visited = []
30 | dfs_visit(adjacent_list, start, visited)
31 | return visited
32 |
--------------------------------------------------------------------------------
/01_알고리즘 구현/알고리즘구현복습/다이나믹프로그래밍.py:
--------------------------------------------------------------------------------
1 | # 피보나치 수열 구현
2 |
3 | # tabulation 방식
4 | def tabulation(n):
5 | table = {
6 | 1: 1,
7 | 2: 1
8 | }
9 | for i in range(3, n+1):
10 | table[i] = table[i-1] + table[i-2]
11 |
12 | return table[n]
13 |
14 | print(tabulation(3))
15 | print(tabulation(4))
16 | print(tabulation(5))
17 | print(tabulation(6))
18 | print(tabulation(7))
19 |
20 | def tabulation_2(n):
21 | prev, current = 0, 1
22 | for _ in range(n - 1):
23 | prev, current = current, prev + current
24 |
25 | return current
26 |
27 | print(tabulation_2(7))
28 |
29 |
30 | # memoization 방식
31 | class Memoization:
32 | def __init__(self):
33 | self.cache = {}
34 | for i in range(1,3):
35 | self.cache[i] = 1
36 |
37 | def fib(self, n):
38 | if n in self.cache.keys():
39 | return self.cache[n]
40 | else:
41 | self.cache[n] = self.fib(n - 1) + self.fib(n - 2)
42 | return self.cache[n]
43 |
44 | memo = Memoization()
45 | print(memo.fib(18))
46 |
47 | # Time Complexity: O(n)
48 | # 좀 헤맸는데 클래스로 구현하니 쉽다. self 프로퍼티가 있어서 간편하당
49 |
50 |
51 | def fib(n, cache):
52 | if n in cache:
53 | return cache[n]
54 | else:
55 | cache[n] = fib(n-1, cache) + fib(n-2, cache)
56 | return cache[n]
57 |
58 | cache = {1: 1, 2: 1}
59 | print(fib(18, cache))
60 | # 함수로도 성공했다. 나이스
61 |
--------------------------------------------------------------------------------
/01_알고리즘 구현/알고리즘구현복습/탐색알고리즘들.py:
--------------------------------------------------------------------------------
1 | # 선형 탐색
2 | def linear_search(list, value):
3 | for element in list:
4 | if element == value:
5 | return value
6 | return None
7 | # Time Complexity: O(n)
8 |
9 |
10 | # 이진 탐색
11 | def binary_serach(sorted_list, value):
12 | n = len(sorted_list)
13 | if n == 0:
14 | return None
15 |
16 | mid = sorted_list[n // 2]
17 | if mid == value:
18 | return mid
19 | elif mid < value:
20 | return binary_serach(sorted_list[n//2 + 1:], value)
21 | else:
22 | return binary_serach(sorted_list[:n//2], value)
23 |
24 | array = [1, 3, 4, 7, 10, 17, 24, 75, 87, 92, 98, 106, 109, 201, 492, 589]
25 | print(binary_serach(array, 12))
26 | # Time Complexity: O(lg(n))
27 |
28 | #
29 |
--------------------------------------------------------------------------------
/02_leetcode/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/02_leetcode/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/02_leetcode/.idea/leetcode.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/02_leetcode/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/02_leetcode/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/02_leetcode/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/02_leetcode/0001 two sum.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def twoSum(self, nums: List[int], target: int) -> List[int]:
4 | for left in range(len(nums)):
5 | right = left + 1
6 |
7 | while right < len(nums):
8 | if nums[left] + nums[right] == target:
9 | return [left, right]
10 | else:
11 | right += 1
12 | # 틀리진 않는데 인풋이 길 경우 runtime 에러가 난다. 최악의 경우 모든 경우의 수에 대해서 따져봐서 그러는 듯ㅜㅜ
13 | # left 포인터는 인덱스0부터 포문을 돌리고 right랑 더해보면서 target 값과 다르면 계속 1씩 더해나가는 식
14 | # 아 책설명을 보니 이 방식을 브루트포스라고 한다고 한다. 배열을 두번 반복하면서 모든 조합을 확인하는 방법
15 | # 비효율적인 풀이법의 대명사라고...ㅋㅋㅋㅋ큐ㅠㅠ
16 | # 시간복잡도는 O(n^2)다. (정확히는 n^2/2인데 상수항은 원래 뺀다. 내가 헷갈렸던 이유가 요거네)
17 |
18 |
19 | # 내 풀이2
20 | class Solution:
21 | def twoSum(self, nums: List[int], target: int) -> List[int]:
22 | for left in range(len(nums)):
23 | if target - nums[left] in nums:
24 | return [left, nums.index(target-nums[left])]
25 | # 이 방법은 nums[left] * 2 = target 일 경우 틀린답이 나옴ㅜㅜ
26 | # nums[left]를 nums에서 빼고 진행해볼까 했으나 그러면 index가 앞뒤로 1씩 밀릴테니 안됨
27 |
28 |
29 | # 책 풀이 - in을 이용한 탐색
30 | class Solution:
31 | def twoSum(self, nums: List[int], target: int) -> List[int]:
32 | for i, n in enumerate(nums):
33 | complement = target - n
34 |
35 | if complement in nums[i + 1:]:
36 | return i, nums[i + 1:].index(complement) + (i + 1)
37 | # 내 두번째 풀이와 같은방식이다! 하지만 내가 막힌 부분을 너무 간단히 해결해버렸다
38 | # return 에서 nums.index(complement) 라고 쓰면 if문 사용한 의미가 없이 중복답이 나올 수 있다.
39 | # 그래서 굳이 nums[i + 1:].index(complement) + (i + 1) 라고 쓴다.
40 | # enumerate는 파이썬 내장함수로 인덱스와 값을 한 쌍으로 가져온다.
41 | # 즉, i in range(len(lst)) 하고 lst[i]하면서 쓸거면 range 보다 enumerate 가 훨씬 효율적일 것
42 | # runtime은 상위 75프로로, 효율성이 좋은편은 아니다.
43 | # 책에 따르면 in의 시간복잡도가 O(n)이기 때문에 얘도 전체 시간복잡도가 O(n^2) 지만, 같은 시간복잡도라도 in 이 훨씬 가볍고 빠르다고 한다.
44 | # 그런걸 어떻게 알 수 있지....? 그냥 경험에서 나오는 짬으로 알아야하나요
45 |
46 |
47 | # 책 풀이2 - 값을 키로, 인덱스를 벨류로 서로 바꿔서 딕셔너리 생성
48 | class Solution:
49 | def twoSum(self, nums: List[int], target: int) -> List[int]:
50 | switched = {}
51 | for i, num in enumerate(nums):
52 | switched[num] = i
53 |
54 | for i, num in enumerate(nums):
55 | if target - num in switched and switched[target - num] != i:
56 | return nums.index(num), switched[target - num]
57 | # 와 어떻게 이런 생각을? 진짜 대단하당..
58 | # 시간복잡도 O(n), runtime 상위 25프로
59 | # 아 근데 nums에 같은 값이 여러개 들어갈 수 있을텐데 그럼 dict로 만드는 과정에서 오류가 생길텐데..?
60 | # 아! 그래서 i 대신 nums.index(num) 을 return 하는 거구나. 같은게 두개면 dict과정에서 첫번째는 덮여버릴거고
61 | # nums.index(num)은 num의 첫번째 인덱스가 살아나니까
62 | # 고단수지만 좀 아슬아슬한걸?
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/02_leetcode/0003 longest substring without repeating characters.py:
--------------------------------------------------------------------------------
1 | # 내 풀이 - 큐 이용
2 | class Solution:
3 | def lengthOfLongestSubstring(self, s: str) -> int:
4 | from collections import defaultdict
5 | from collections import deque
6 | num_in_q = defaultdict(int)
7 | # 각 char이 현재 큐 안에 들어있는 횟수
8 | result = 0
9 | queue = deque()
10 | for char in s:
11 | queue.append(char)
12 | while num_in_q[char] > 0:
13 | # 큐 안에 char이 없어질 때까지
14 | deleted_char = queue.popleft()
15 | num_in_q[deleted_char] -= 1
16 | num_in_q[char] += 1
17 | result = max(result, len(queue))
18 |
19 | return result
20 | # 우와 성공!
21 | # runtime 상위30프로 memory usage 상위 70프로
22 | # 공간복잡도는 O(n)인 num_in_q 딕셔너리와 O(n)인 큐가 있어서 O(n)
23 | # 시간복잡도는 최악의 경우 O(n^2)일 듯
24 |
25 |
26 | # 책 풀이
27 | class Solution:
28 | def lengthOfLongestSubstring(self, s: str) -> int:
29 | used = {}
30 | maxlen = 0
31 | start = 0
32 | for i, char in enumerate(s):
33 | if char in used and used[char] >= start:
34 | start = used[char] + 1
35 | used[char] = i
36 | maxlen = max(maxlen, i - start + 1)
37 |
38 | return maxlen
39 | # 내가 짠 코드와 비슷해서 기분이 좋당
40 | # 책의 코드가 더 나은 부분이 있는데 딕셔너리에 등장횟수 대신 인덱스를 value로 넣는 부분!
41 | # 나도 어차피 value(등장횟수)가 1이 넘지 않게 관리하고 있으므로 인덱스가 더 나은 선택이다.
42 | # 그리고 while 문을 쓸 필요없이 바로 해당 인덱스로 갈 수 있어서 시간이 절약된다.
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/02_leetcode/0005 longest palindromic substring.py:
--------------------------------------------------------------------------------
1 | # 내 답안
2 | class Solution:
3 | def longestPalindrome(self, s: str) -> str:
4 | palinedromes = []
5 |
6 | # 왼쪽 포인터는 0부터 끝까지
7 | for left in range(len(s)):
8 | # 오른쪽 포인터는 맨끝부터 앞으로
9 | right = len(s) - 1
10 |
11 | # 두개의 포인터 slice가 펠린드롬인지 판별하고 맞으면 리스트에 추가
12 | # 틀리면 right 한칸씩 앞으로 당기면서 체크
13 | while left <= right:
14 | slice = s[left: right]
15 | if slice == slice[::-1]:
16 | palinedromes.append(slice)
17 | else:
18 | right -= 1
19 |
20 | return palinedromes.sort()[-1]
21 | # 메모리 부족이라고 뜸ㅠㅠ
22 | # 책에서는 슬라이딩 윈도우, 다이나믹 프로그래밍, LCS 등을 사용하고 있는데 아직 안배웠으므로 나중에 다시 풀어보자
23 |
24 |
--------------------------------------------------------------------------------
/02_leetcode/0015 3sum.py:
--------------------------------------------------------------------------------
1 | # 일단은 브루트포스
2 | class Solution:
3 | def threeSum(self, nums: List[int]) -> List[List[int]]:
4 | result = []
5 | nums.sort()
6 |
7 | for i in range(len(nums)-2):
8 | if i > 0 and nums[i] == nums[i-1]:
9 | continue
10 | for j in range(i+1, len(nums)-1):
11 | if j > i+1 and nums[j] == nums[j-1]:
12 | continue
13 | for k in range(j+1, len(nums)):
14 | if k > j+1 and nums[k] == nums[k-1]:
15 | continue
16 | if nums[i] + nums[j] + nums[k] == 0:
17 | result.append((nums[i], nums[j], nums[k]))
18 |
19 | return result
20 | # The solution set must not contain duplicate triplets 조건 만족하게 만드는게 너무 어렵다ㅜㅜ
21 | # 브루트포스조차도 책 보고 겨우겨우 따라했다
22 | # 이 방법은 시간 초과가 뜬다. O(n^3) 이니 당연함
23 | # two sum 과 다르게 인덱스를 보존하지 않아도 된다. 그럴 경우는 sort를 하고 시작하면 편한듯 빅오는 (O(n))일걸
24 |
25 |
26 | # 두번째 방법은 sort 후 i는 위와 똑같고 나머지 두 개를 투포인터로 사용하는 것
27 | class Solution:
28 | def threeSum(self, nums: List[int]) -> List[List[int]]:
29 | result = []
30 | nums.sort()
31 |
32 | for i in range(len(nums)-2):
33 | if i > 0 and nums[i] == nums[i-1]:
34 | continue
35 |
36 | left, right = i + 1, len(nums) - 1
37 | while left < right:
38 | sum = nums[left] + nums[right] + nums[i]
39 | if sum > 0:
40 | right -= 1
41 | elif sum < 0:
42 | left += 1
43 | elif sum == 0 :
44 | result.append((nums[i], nums[left], nums[right]))
45 | while left < right and nums[left] == nums[left+1]:
46 | left += 1
47 | while left < right and nums[right] == nums[right-1]:
48 | right -= 1
49 | left += 1
50 | right -= 1
51 |
52 | return result
53 | # runtime 상위 50프로
54 | # '정렬해서 i는 포문으로 돌리고 나머지 두개를 투포인터로' 라는 개념 자체는 짜기 어렵지 않았는데
55 | # 중복처리하는게 끔찍하게 어렵다ㅜㅜ set으로 중복 요소를 지우고 시작할 수 있는 문제도 아니라서.
56 | # 나 혼자였으면 분명히 중복처리에서 헤맸다.
57 |
58 |
--------------------------------------------------------------------------------
/02_leetcode/0017 letter combination of a phone number.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | class Solution:
3 | def letterCombinations(self, digits: str) -> List[str]:
4 | if len(digits) == 0 or '0' in digits or '1' in digits:
5 | return None
6 |
7 | keypad = {
8 | '2': ['a', 'b', 'c'],
9 | '3': ['d', 'e', 'f'],
10 | '4': ['g', 'h', 'i'],
11 | '5': ['j', 'k', 'l'],
12 | '6': ['m', 'n', 'o'],
13 | '7': ['p', 'q', 'r', 's'],
14 | '8': ['t', 'u', 'v'],
15 | '9': ['w', 'x', 'y', 'z']
16 | }
17 |
18 | def permutation(digits, keypad, idx=None):
19 | if idx is None:
20 | idx = len(digits) - 1
21 |
22 | if idx == 0:
23 | return keypad[digits[0]]
24 | else:
25 | res = []
26 | for alp in keypad[digits[idx]]:
27 | res += [prev + alp for prev in permutation(digits, keypad, idx - 1)]
28 | return res
29 |
30 | return permutation(digits, keypad)
31 | # Status: Accepted
32 | # Algorithm: Recursion (Divide and Conquer)
33 | # Time Complexity: O(3^n)
34 | # Runtime: 28ms (top 16.6%)
35 |
36 |
37 |
--------------------------------------------------------------------------------
/02_leetcode/0021 merge two sorted lists.py:
--------------------------------------------------------------------------------
1 | # Definition for singly-linked list.
2 | # class ListNode:
3 | # def __init__(self, val=0, next=None):
4 | # self.val = val
5 | # self.next = next
6 |
7 | # 책 풀이: 재귀구조로 연결
8 | class Solution:
9 | def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
10 | if (not l1) or (l2 and (l1.val > l2.val)):
11 | l1, l2 = l2, l1
12 |
13 | if l1:
14 | l1.next = self.mergeTwoLists(l1.next, l2)
15 |
16 | return l1
17 | # 재귀구조를 처음 써봤다!
18 | # 지금 하고 있는건 결국 l2를 l1으로 합치고 있는 과정
19 | # 첫번째 if를 보면, l1이 끝났거나, l1이 l2보다 크면 l1과 l2를 뒤집어준다.
20 | # 즉, 현재의 l1 자리를 l2에게 내어준다.(l2가 작아서 앞에 들어가야 함)
21 | # 다음 if를 보자. l1이 아직 남아있다면, 다음 노드를 l1으로 그대로 가지고 갈지,
22 | # l2로 교체할지 다시 위의 과정을 반복해서 정해준다(재귀)
23 | # 흠 근데 헷갈리는 건 첫 if문에서 (not l1.val) 이렇게 해줘야하는거 아닌가? 그 부분이 자꾸 헷갈린다.
24 |
25 |
26 | # -----------------------------------------------------------------------------------------
27 | # 0813(연결리스트 한번 훑기 완료한 날) 책 없이 나 혼자 다시 풀이 도전
28 | # iteratively, not in-place
29 | class Solution:
30 | def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
31 | root = cur = ListNode(None)
32 |
33 | while l1 and l2:
34 | if l1.val < l2.val:
35 | cur.next = l1
36 | l1 = l1.next
37 | else:
38 | cur.next = l2
39 | l2 = l2.next
40 |
41 | cur = cur.next
42 |
43 | cur.next = l1 or l2
44 | return root.next
45 | # ㅎㅎㅎ못품... 이건 누가 올려놓은 답 보고 적은거임
46 | # 개념은 나도 똑같이 생각했는데 return 바로 위에 cur.next = l1 or l2
47 | # 저런 코드가 되는구나. while 연산이 끝나고 난 후 l1, l2 중 남아있는 애들 갖다 붙이는 것.
48 | # 책 풀이인 재귀는 더 명쾌하지만 내가 바로 저렇게 쓸 수 있는 능력이 안된다ㅜㅜ
49 | # dummy가(임시 variable)이 있어도 일단은 이렇게만 풀 줄 알면 충분한 듯. 아 그리고 이건 in place가 아니다
50 |
51 |
52 | # iteratively, in-place
53 | class Solution:
54 | def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
55 | root = cur = ListNode(None)
56 | cur.next = l1
57 |
58 | while l1 and l2:
59 | if l1.val < l2.val:
60 | l1 = l1.next
61 | else:
62 | temp = l2.next
63 | cur.next, l2.next = l2, l1
64 | l2 = temp
65 | cur = cur.next
66 |
67 | cur.next = l1 or l2
68 | return root.next
69 | # 우왓 이건 내가 짠거!!!
70 | # root도 있고 temp도 있지만 그래도 짤 수 있게 됐다!! 신나!
71 |
--------------------------------------------------------------------------------
/02_leetcode/0022 generate parentheses.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | import copy
3 |
4 | class Solution:
5 | def __init__(self):
6 | self.ans = []
7 |
8 | def generateParenthesis(self, n: int) -> List[str]:
9 | count = {")": 0, "(": 0}
10 | self.recursion(n, count, "")
11 | return self.ans
12 |
13 | def recursion(self, n, count: dict, parentheses: str):
14 | if count[")"] == n and count["("] == n:
15 | self.ans.append(parentheses)
16 | return
17 |
18 | if count["("] == n:
19 | parentheses = parentheses + ")"
20 | count[")"] += 1
21 | self.recursion(n, count, parentheses)
22 |
23 | if count["("] > count[")"]:
24 | count1 = copy.deepcopy(count)
25 | count1[")"] += 1
26 | parentheses1 = parentheses + ")"
27 | self.recursion(n, count1, parentheses1)
28 | count2 = copy.deepcopy(count)
29 | count2["("] += 1
30 | parentheses2 = parentheses + "("
31 | self.recursion(n, count2, parentheses2)
32 |
33 | if count["("] == count[")"]:
34 | parentheses = parentheses + "("
35 | count["("] += 1
36 | self.recursion(n, count, parentheses)
37 | # Status: Memory Limit Exceeded
38 | # Intuition: Recursion
39 | # Note: deep copy 때문에 메모리 초과. recursion으로 간단히 되겠다고 생각했는데 생각보다 손이 많이 가서 별로인 것 같다.
40 | # 다시 도전
41 |
42 |
43 | # my solution
44 | class Solution:
45 | def __init__(self):
46 | self.ans = []
47 |
48 | def generateParenthesis(self, n: int) -> List[str]:
49 | self.n = n
50 | self.recursion(0, 0, "")
51 | return self.ans
52 |
53 | def recursion(self, o, c, s):
54 | if o == self.n and c == self.n:
55 | self.ans.append(s)
56 | return
57 |
58 | if o == self.n:
59 | self.recursion(o, c + 1, s + ")")
60 | else:
61 | if o > c:
62 | self.recursion(o, c + 1, s + ")")
63 | self.recursion(o + 1, c, s + "(")
64 | else:
65 | self.recursion(o + 1, c, s + "(")
66 | # Status: Accepted
67 | # Algorithm: Recursion
68 | # Note: 사실 위랑 같은 원리. 처리가 귀찮은 부분들을 바꿔줬다.
69 | # Runtime: 36ms(top 46.3 %)
70 |
--------------------------------------------------------------------------------
/02_leetcode/0023 merge k sorted lists.py:
--------------------------------------------------------------------------------
1 | # Definition for singly-linked list.
2 | # class ListNode:
3 | # def __init__(self, val=0, next=None):
4 | # self.val = val
5 | # self.next = next
6 |
7 | # 책 풀이 - 힙을 이용한 우선순위 큐 사용
8 | class Solution:
9 | def mergeKLists(self, lists: List[ListNode]) -> ListNode:
10 | root = result = ListNode(val=None)
11 | heap = []
12 |
13 | for i in range(len(lists)):
14 | if lists[i]:
15 | heapq.heappush(heap, (lists[i].val, i, lists[i]))
16 |
17 | while heap:
18 | node = heapq.heappop(heap)
19 | idx = node[1]
20 | result.next = node[2]
21 |
22 | result = result.next
23 | if result.next:
24 | heapq.heappush(heap, (result.next.val, idx, result.next))
25 |
26 | return root.next
27 | # 우선 root 와 result 노드를 만들어줬다. result는 계속 업데이트 되므로 자리를 지키고 있을 root가 필요하다
28 | # lists를 돌면서 각 연결리스트의 첫번째 노드들을 노드의 val을 우선순위로 하여 heap에 추가한다. (lists 안의 연결리스트는 변하지 않음)
29 | # 중간에 i값을 가지고 있는 이유는 같은 val을 가진 노드 두개가 들어오면 중복된 값이 추가됐다고 에러가 나기 때문에
30 | # 구분하기 위해 i라는 인자 하나를 더 넣어준 것이다.
31 |
32 | # heap이 있는 동안(heap이 비었으면 전부다 처리한 것이다)
33 | # 가장작은 요소를 node로 빼고 result.next를 해당 노드로(node의 두번째 인덱스) 변경하고 result를 result.next로 옮긴다.
34 | # 만약 result.next가 있다면, 즉 lists안에 이 노드가 있던 연결리스트의 뒷부분이 남아있다면
35 | # 걔를 데려와서 다시 힙에 넣어준다. (lists안에 있는 모든 연결리스트들의 첫번째 노드들이 힙에 들어와있어야 제대로 비교할 수 있으므로)
36 | # heap이 더이상 존재하지 않는다면, 모든 연결리스트들이 다 힙으로 들어왔고,
37 | # 또 전부 우선순위 비교를 통해 result.next로 들어갔다는 뜻이므로 연산을 종료하고 root.next를 return한다.
38 |
39 |
40 | # 책풀이 공부한거 안보고 다시 한 번 써보기
41 | # Definition for singly-linked list.
42 | # class ListNode:
43 | # def __init__(self, val=0, next=None):
44 | # self.val = val
45 | # self.next = next
46 | class Solution:
47 | def mergeKLists(self, lists: List[ListNode]) -> ListNode:
48 | root = result = ListNode(val=None)
49 | heap = []
50 |
51 | for i in range(len(lists)):
52 | heapq.heappush(heap, (lists[i].val, i, lists[i]))
53 |
54 | while heap:
55 | info = heapq.heappop(heap)
56 | idx = info[1]
57 | result.next = info[2]
58 | result = result.next
59 | if result.next:
60 | heapq.heappush(heap, (result.next.val, idx, result.next))
61 |
62 | return root.next
63 | # if lists[i] 부분, 즉 예외처리 부분을 빼고는 올바르게 썼다. 제대로 이해한 것 같다.
64 |
65 |
--------------------------------------------------------------------------------
/02_leetcode/0042 trapping rain water.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def trap(self, height: List[int]) -> int:
4 | extreme_point = []
5 |
6 | # 양쪽 끝에 대한 예외처리
7 | if height[0] > height[1]:
8 | extreme_point.append(heigt[0])
9 | if height[-1] > height[-2]:
10 | extreme_point.append(height[-1])
11 |
12 | # height[i]가 극소, 극대점일 때 extreme_point 에 인덱스 추가
13 | for i in range(1, len(height)-1):
14 | if (height[i-1] > height[i] and height[i+1] > height[i]) or \
15 | (height[i-1] < height[i] and height[i+1] < height[i]):
16 | extreme_point.append(height[i])
17 |
18 | # 극소 양쪽의 극대를 비교해서 (더 작은 극대 - 극소) * 2 가 그 부근 물의 양
19 | total_water = 0
20 | for i in range(1, len(extreme_point), 2):
21 | max_point = min(extreme_point[i-1], extreme_point[i+1])
22 | min_point = extreme_point[i]
23 | water = 2 * (max_point - min_point)
24 | total_water += water
25 |
26 | return total_water
27 | # 코드는 내가 의도한대로 짰는데 아예 풀이방식이 틀렸음. 반영하지 못하는 예외가 너무 많다. W자 형태라던지 등등
28 | # 다른 풀이방법을 생각해보자~~
29 |
30 | # 이거 너무 어렵당ㅜㅜ 책에서는 투포인터와 스택을 쓰는 방법 두가지를 알려주는데 둘 다 이해가 잘 안간다ㅠㅜ
31 | # 일단 패스..
32 |
33 |
34 | # 브루트포스 풀이
35 | class Solution:
36 | def trap(self, height: List[int]) -> int:
37 |
38 | rain = 0
39 | for i, h in enumerate(height):
40 | while True:
41 | condition = 0
42 | for j, height_left in enumerate(height[:i]):
43 | if height_left > h:
44 | condition += 1
45 | break
46 |
47 | for k, height_right in enumerate(height[i + 1:]):
48 | if height_right > h:
49 | condition += 1
50 | break
51 |
52 | if condition == 2:
53 | rain += 1
54 | else:
55 | break
56 |
57 | h += 1
58 |
59 | return rain
60 | # O(n^3) 이라 time limit exceeded 지만 짤 수 있다는 것만으로도 일단은 발전이다
61 |
62 |
63 | # 좀 더 발전한 브루트포스 하나 더!
64 | class Solution:
65 | def trap(self, height: List[int]) -> int:
66 | rain = 0
67 | for i, h in enumerate(height):
68 | max_left = 0
69 | max_right = 0
70 | for j, height_left in enumerate(height[:i]):
71 | max_left = max(height_left, max_left)
72 | for k, height_right in enumerate(height[i+1:]):
73 | max_right = max(max_right, height_right)
74 |
75 | if max_right > h and max_left > h:
76 | rain += min(max_left, max_right) - h
77 |
78 | return rain
79 | # O(n^2) 이다! 여전히 타임아웃이지만 그래도ㅎㅎ
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/02_leetcode/0046 permutations.py:
--------------------------------------------------------------------------------
1 | class Solution:
2 | def permute(self, nums: List[int]) -> List[List[int]]:
3 | def gen_perm(nums, case):
4 | if len(nums) == 0:
5 | self.res.append(case)
6 | else:
7 | for i, num in enumerate(nums):
8 | gen_perm(nums[:i] + nums[i+1:], case + [num])
9 | return
10 |
11 | self.res = []
12 | gen_perm(nums, [])
13 | return self.res
14 | # Status: Accepted
15 | # Algorithm: Recursion
16 | # Time Complexity: O(n!)
17 | # Runtime: 36ms (top 7.6%)
18 | # Intuition: 재귀를 써야겠다는 건 보자마자 생각했는데 구현이 생각보다 어려웠다.
19 | # 기존 nums에 변형을 가자면서 하니 첫 포문에서 끝이 나버렸다.
20 | # 저런 애들은 원본은 그냥 놔두고 인덱싱 등으로 파라미터를 조정해주자
21 | # 근데 이거 시간복잡도가 n! 인데 왜 이렇게 runtime이 짧지? 최선의 방법이 이거라서 그런건가
--------------------------------------------------------------------------------
/02_leetcode/0049 group anagrams.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
4 | # 인풋을 알파벳순으로 정렬
5 | sorted_words = []
6 | for word in strs:
7 | sorted_words.append(''.join(sorted(word)))
8 |
9 | # 정렬된 리스트에서 값이 같은 인덱스들을 딕셔너리의 value로 모음
10 | anagram = {}
11 | for i in range(len(sorted_words)):
12 | if sorted_words[i] in anagram.keys():
13 | anagram[sorted_words[i]].append(strs[i])
14 | else:
15 | anagram[sorted_words[i]] = [strs[i]]
16 |
17 | # 딕셔너리의 value만 출력
18 | return anagram.values()
19 | # runtime 상위 5프로, memory usage 상위 50프로
20 |
21 |
22 | # 책 풀이
23 | class Solution:
24 | def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
25 | anagrams = collections.defaultdict(list)
26 |
27 | for word in strs:
28 | anagrams[''.join(sorted(word))].append(word)
29 |
30 | return anagrams.values()
31 | # 내 풀이와 과정은 똑같지만 두가지가 다르다.
32 | # sorted_words 리스트를 만들 필요없이 바로 딕셔너리의 키로 넣어버림
33 | # 새로 등장하는 key에 대해 에러가 나지 않게 defaultdict 를 사용함 (내가 if로 해결한 부분)
34 | # defaultdict 객체는 존재하지 않는 키에 대해, 에러가 나는 대신 디폴트값을 기준으로 value를 생성해준다.
35 | # 즉 여기서 defaultdict 안에 list를 입력했으므로 append 구문을 돌릴 때 존재하지 않는 키에 대해서는 빈 리스트 []에 append 해준다
36 | # runtime은 내것보다 조금 빠르고, 메모리는 더 차지함
--------------------------------------------------------------------------------
/02_leetcode/0053 maximum subarray.py:
--------------------------------------------------------------------------------
1 | class Solution:
2 | def maxSubArray(self, nums: List[int]) -> int:
3 | res = -sys.maxsize
4 | for i in range(len(nums)):
5 | for j in range(i + 1, len(nums) + 1):
6 | res = max(res, sum(nums[i:j]))
7 |
8 | return res
9 | # Status: Time Limit Exceeded
10 | # Algorithm: Brute Force
11 | # Time Complexity: O(n^2)
12 | # Runtime:
13 |
14 |
--------------------------------------------------------------------------------
/02_leetcode/0077 combinations.py:
--------------------------------------------------------------------------------
1 | class Solution:
2 | def combine(self, n: int, k: int) -> List[List[int]]:
3 | def gen_combi(nums, case, count, k):
4 | if count == k:
5 | self.res.append(case)
6 | else:
7 | for i, num in enumerate(nums):
8 | gen_combi(nums[i + 1:], case + [num], count + 1, k)
9 | return
10 |
11 | self.res = []
12 | nums = [i + 1 for i in range(n)]
13 | gen_combi(nums, [], 0, k)
14 | return self.res
15 | # Status: Accepted
16 | # Algorithm: Recursion
17 | # Time Complexity: O(n!)
18 | # Runtime: 600ms (top 56.5%)
19 | # Intuition: 46번 순열문제와 똑같은 방식
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/02_leetcode/0092 reverse linked list 2.py:
--------------------------------------------------------------------------------
1 | # Definition for singly-linked list.
2 | # class ListNode:
3 | # def __init__(self, val=0, next=None):
4 | # self.val = val
5 | # self.next = next
6 |
7 | # one pass, 즉 시간복잡도 O(n)에 하라는 조건
8 | # 내 풀이
9 | class Solution:
10 | def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
11 | if not head:
12 | return None
13 |
14 | m_node = n_node = head
15 | for _ in range(n-1):
16 | n_node = n_node.next
17 |
18 | for _ in range(m - 1):
19 | m_node = m_node.next
20 |
21 | n_node.val, m_node.val = m_node.val, n_node.val
22 | return head
23 | # 압 원패스도 아니고 문제를 잘못 이해했다. m과 n을 바꾸라는게 아니라 m부터 n까지를 뒤집으라는 얘기다
24 | # 꼼수써서 val만 바꾸려다가 딱걸렸다.
25 |
26 |
27 | # 책 풀이 - 반복구조
28 | class Solution:
29 | def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
30 | if not head or m == n:
31 | return head
32 |
33 | root = start = ListNode(None)
34 | root.next = head
35 |
36 | for _ in range(m - 1):
37 | start = start.next
38 | end = start.next
39 |
40 | for _ in range(n - m):
41 | temp = start.next
42 | start.next = end.next
43 | end.next = end.next.next
44 | start.next.next = temp
45 |
46 | return root.next
47 | # 우와 내 기준엔 이게 제일 어려운 것 같은데?
48 | # .next가 포인터가 가리키는 '노드'를 의미하는지, 아니면 그 '화살표 자체'를 의미하는지가 가장 헷갈린다.
49 | # 이 부분 개념을 제대로 알아야 temp 를 따로 만들어줄지 말지 결정을 내릴 수 있을 듯 함.
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/02_leetcode/0094 binary tree inorder traversal.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def inorderTraversal(self, root: TreeNode) -> List[int]:
11 | self.trav = []
12 |
13 | def inorder(node):
14 | if node:
15 | inorder(node.left)
16 | self.trav.append(node.val)
17 | inorder(node.right)
18 |
19 | inorder(root)
20 | return self.trav
21 | # Status: Accepted
22 | # Algorithm: recursion
23 | # Time Complexity: O(n)
24 | # Runtime: 24ms (top 5.1%)
25 |
26 |
27 | # leetcode discussion solution
28 | class Solution:
29 | def inorderTraversal(self, root: TreeNode) -> List[int]:
30 | stack = []
31 | trav = []
32 |
33 | curr_node = root
34 | while (curr_node or stack):
35 | while curr_node:
36 | stack.append(curr_node)
37 | curr_node = curr_node.left
38 |
39 | last = stack.pop()
40 | trav.append(last.val)
41 | if last.right:
42 | curr_node = last.right
43 |
44 | return trav
45 | # Status: Accepted
46 | # Algorithm: iteration
47 | # Time Complexity: O(n)
48 | # Runtime: 32ms (top 46.8%)
49 | # Intuition: inorder이므로 left를 넣어주고 pop하면서 right를 넣어준다.
50 | # 나는 처음에 타임아웃이 발생했는데 이미 거쳐간 노드라도 left가 있다고 판단되서 다시 스택에 들어갔기 때문.
51 | # 해결 방법으로는 위처럼 while문을 하나 더 넣거나 visited 변수를 추가해서 set로 스택에 추가하는 방법이 있다.
--------------------------------------------------------------------------------
/02_leetcode/0096 unique binary search tree.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | class Solution:
3 | def numTrees(self, n: int) -> int:
4 | table = {
5 | 0: 1,
6 | 1: 1,
7 | }
8 |
9 | for i in range(2, n+1):
10 | res = 0
11 | for j in range(i):
12 | res += table[j] * table[i-j-1]
13 |
14 | table[i] = res
15 |
16 | return table[n]
17 | # Status: Accepted
18 | # Algorithm: Dynamic Programming(Tabulation)
19 | # Time Complexity: O(n^2)
20 | # Runtime: 24ms (top 4.4%)
21 | # 잘했어!
--------------------------------------------------------------------------------
/02_leetcode/0111 minimum depth of binary tree.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def minDepth(self, root: TreeNode) -> int:
11 | if root is None: return 0
12 | from collections import deque
13 |
14 | q = deque([root, 1])
15 | while q:
16 | node, curr_depth = q.popleft()
17 | if node:
18 | if node.left is None and node.right is None:
19 | return curr_depth
20 | q.append((node.left, curr_depth+1))
21 | q.append((node.right, curr_depth+1))
22 | # Status: Accepted
23 | # Algorithm: BFS
24 | # Time Complexity: O(n)
25 | # Runtime: 48ms (top 37.6%)
26 |
--------------------------------------------------------------------------------
/02_leetcode/0113 path sum2.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 | class Solution:
8 | def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
9 | if root is None: return None
10 | stack = [(root, root.val, [root.val])]
11 | res = []
12 | while stack:
13 | last = stack.pop()
14 | left, right = last[0].left, last[0].right
15 | if last[1] == sum and left is None and right is None:
16 | res.append(last[2])
17 | if left:
18 | stack.append((left, last[1] + left.val, last[2] + [left.val]))
19 | if right:
20 | stack.append((right, last[1] + right.val, last[2] + [right.val]))
21 |
22 | return res
23 |
24 | # Status: Accepted
25 | # Algorithm: DFS
26 | # Time Complexity: O(n)
27 | # Runtime: 44ms (top 13.0%)
28 | # Intuition: dfs로 내려가다가 leaf node이고 총합이 sum과 같으면 result 리스트에 추가.
29 | # leaf node나 path 개수가 아닌 path를 return 해야 하기 때문에 스택에 path 변수 추가
--------------------------------------------------------------------------------
/02_leetcode/0116 populating next right pointers in each node.py:
--------------------------------------------------------------------------------
1 | """
2 | # Definition for a Node.
3 | class Node:
4 | def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
5 | self.val = val
6 | self.left = left
7 | self.right = right
8 | self.next = next
9 | """
10 |
11 | # my solution
12 | class Solution:
13 | def connect(self, root: 'Node') -> 'Node':
14 | if root is None:
15 | return None
16 |
17 | from collections import deque
18 | q = deque()
19 | q.append(root)
20 |
21 | idx = 0
22 | while q:
23 | for _ in range(pow(2, idx)):
24 | first = q.popleft()
25 | if first is None:
26 | return root
27 | first.next = q[0] if q else None
28 | q.append(first.left)
29 | q.append(first.right)
30 | first.next = None
31 | idx += 1
32 |
33 | return root
34 | # Status: Accepted
35 | # Algorithm: BFS
36 | # Time Complexity: O(n)
37 | # Runtime: 64ms (top 14.1%)
38 | # Intuition: 포화이진트리이므로 큐를 쌓으면 2의 지수 개수만큼의 요소들이 같은 레벨임을 이용
39 | # root이 None인 경우를 예외처리 해줬으므로 q.popleft()가 None일 경우 트리가 끝난 것.
40 |
41 | # 혼자서 '아 될 것 같은데-' 하면서 몇십분을 씨름하다가 풀었다. 너무 뿌듯해
42 |
43 |
44 | # leetcode discussion solution
45 | class Solution:
46 | def connect(self, root: 'Node') -> 'Node':
47 | if root and root.left and root.right:
48 | root.left.next = root.right
49 | if root.next:
50 | root.right.next = root.next.left
51 | self.connect(root.left)
52 | self.connect(root.right)
53 |
54 | return root
55 | # Status: Accepted
56 | # Algorithm: Recursion
57 | # Time Complexity: O(n)
58 | # Runtime: 68ms (top 27.9%)
59 | # 같은 O(n)이더라도 훨씬 간결하고 이해가 쉬운 코드. 완벽한 부분문제를 찾았다.
--------------------------------------------------------------------------------
/02_leetcode/0121 best time to buy and sell stock.py:
--------------------------------------------------------------------------------
1 | # 브루트포스
2 | class Solution:
3 | def maxProfit(self, prices: List[int]) -> int:
4 | max_profit = 0
5 | for i, price in enumerate(prices):
6 | for selling_price in prices[i+1:]:
7 | max_profit = max(max_profit, selling_price - price)
8 |
9 | return max_profit
10 | # 타임아웃
11 |
12 |
13 | # 책 풀이
14 | class Solution:
15 | def maxProfit(self, prices: List[int]) -> int:
16 | max_profit = 0
17 | min_price = sys.maxsize
18 |
19 | for price in prices:
20 | min_price = min(min_price, price)
21 | max_profit = max(max_profit, price - min_price)
22 |
23 | return max_profit
24 | # 이렇게 간결한 코드라니 너무 좋다.ㅜㅜ
25 | # 나는 아직 컴퓨터식 사고가 잘 안되나보다. 한번에 답을 찾아낼 수 있는 수학적 방식으로만 접근한다. max로 계속 따져주면 될 것을..
26 | # 나는 처음에 min_price를 max(prices)로 설정했는데 prices가 빈리스트[]로 들어올 경우 에러가 난다.
27 | # sys.maxsize 라는 개념을 배웠땅. 혹은 float('inf')로 해줄 수도 있다고 한다
28 | # 책에 따르면 파이썬은 무한대의 값을 지정할 수 있는 숫자형을 사용하기 때문에 sys.maxsize가 큰 의미가 없다고 한다
29 | # 하지만 코딩테스트는 모든 언어에 대응하는 테스트케이스만 나오므로 이렇게 하는거라고 함.
30 | # 물론 문제에서 인풋 범위가 제시되어있으면 그 값을 쓰면 된다.
31 | # runtime, memory usage 모두 적당하다
--------------------------------------------------------------------------------
/02_leetcode/0125 valid palindrome.py:
--------------------------------------------------------------------------------
1 | # 슬라이싱 사용
2 | class Solution:
3 | def isPalindrome(self, s: str) -> bool:
4 | s = s.lower()
5 | s = re.sub('[^a-z0-9]', '', s)
6 |
7 | return s == s[::-1]
8 | # 슬라이싱은 내부적으로 굉장히 빠르게 동작한다고 함. 속도를 따질 때 좋은 선택
9 | # reverse(), 혹은 reversed() + join() 보다 다섯배이상 빠르고 포문보다는 열배이상
10 |
11 |
12 | # 리스트로 변환 방식
13 | class Solution:
14 | def isPalindrome(self, s: str) -> bool:
15 | lst = []
16 | for char in s:
17 | if char.isalnum():
18 | lst.append(char.lower())
19 | # isalnum 함수는 영문자, 숫자 여부를 판별해줌.
20 | # 위에서 정규식으로 돌린걸 하나씩 점검해주고 있는 것
21 |
22 | while len(lst) > 1:
23 | if lst.pop(0) != lst.pop()
24 | # pop은 해당 요소를 return 하면서 동시에 리스트에서 삭제해버리기 때문에
25 | # 인덱싱해서 비교 + 그 요소를 삭제 를 따로 구현할 필요없이 한번에 가능하다.
26 | return False
27 |
28 | return True
29 | # 리스트가 빈 경우 바로 True로 return
30 | # 문자열을 리스트로 맵핑하고 있는데 데이터 구조를 다루는 측면에선 좋지만
31 | # 연산 과정에서 시간소모가 크다. 지금도 포문을 써서 일일히 확인하고 있음
32 |
33 |
34 | # 데크 자료형을 이용하는 방법도 있는데 데크를 내가 아직 안배웠으니 그건 나중에~
--------------------------------------------------------------------------------
/02_leetcode/0200 number of islands.py:
--------------------------------------------------------------------------------
1 | # 문제가 너무 귀엽다! 1로 이루어진 섬이라니~.~
2 |
3 | class Solution:
4 | def dfs(self, i, j, grid):
5 | if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] != '1':
6 | return
7 |
8 | grid[i][j] = -1
9 | # 한번 본 곳은 다시 탐색하지 않기 위해 value 변경
10 |
11 | self.dfs(i+1 ,j, grid)
12 | self.dfs(i-1, j, grid)
13 | self.dfs(i, j+1, grid)
14 | self.dfs(i, j-1, grid)
15 |
16 |
17 | def numIslands(self, grid: List[List[str]]) -> int:
18 | num_islands = 0
19 | for i in range(len(grid)):
20 | for j in range(len(grid[0])):
21 | if grid[i][j] == '1':
22 | self.dfs(i, j, grid)
23 | num_islands += 1
24 |
25 | return num_islands
26 | # 책의 풀이를 읽고 내가 다시 짜본 코드다. 문제보고 접근 방식을 전혀 모르겠어서 약간 띠용했달까
27 | # 비선형구조 문제를 처음 풀어서 그런 것 같다. dfs를 이런 데에 쓸 수 있구나
28 | # 책의 풀이에서는 중첩함수를 써서 조금 더 간단하게 구현했다.
29 |
30 |
31 | # 책 풀이 - dfs
32 | class Solution:
33 | def numIslands(self, grid: List[List[str]]) -> int:
34 | def dfs(i,j):
35 | if i < 0 or i >= len(grid) or \
36 | j < 0 or j >= len(grid[0]) or \
37 | grid[i][j] != '1':
38 | return
39 |
40 | grid[i][j] = 0
41 | dfs(i + 1, j)
42 | dfs(i - 1, j)
43 | dfs(i, j + 1)
44 | dfs(i, j - 1)
45 |
46 | count = 0
47 | for i in range(len(grid)):
48 | for j in range(len(grid[0])):
49 | if grid[i][j] == '1':
50 | dfs(i, j)
51 | count += 1
52 |
53 | return count
54 | # 중첩함수로 dfs를 numIslands 안에 구현하면 grid를 따로 파라미터로 넣어주지 않아도 된다.
55 |
--------------------------------------------------------------------------------
/02_leetcode/0206 reverse linked list.py:
--------------------------------------------------------------------------------
1 | # Definition for singly-linked list.
2 | # class ListNode:
3 | # def __init__(self, val=0, next=None):
4 | # self.val = val
5 | # self.next = next
6 |
7 | # 책 풀이 - 재귀 구조 사용
8 | class Solution:
9 | def reverseList(self, head: ListNode) -> ListNode:
10 | def reverse(node: ListNode, prev: ListNode =None):
11 | if not node:
12 | return prev
13 |
14 | next_node, node.next = node.next, prev
15 | # 다중 할당을 사용한 부분. 두 줄로 나눠 작성했다면 제대로 작동하지 않았을거다.
16 | # next_node는 계속 뒤로 넘어가야하니 기존의 node.next를 할당해주어야 하고
17 | # 동시에 node.next는 prev로 변경해주어야 한다.
18 | return reverse(next_node, node)
19 |
20 | return reverse(head)
21 | # 연결리스트는 재귀구조 및 다중할당과 함께 사용되는 경우가 많은 것 같다.
22 | # 내용은 간단하고 직관적이어서 이해하기는 어렵지않지만 이 코드를 혼자서도 짤 수 있어야 한다.
23 |
24 |
25 | # 책 풀이 - 반복 구조 사용
26 | class Solution:
27 | def reverseList(self, head: ListNode) -> ListNode:
28 | node, prev = head, None
29 |
30 | while node:
31 | next_node, node.next = node.next, prev
32 | node, prev = next_node, node
33 |
34 | return prev
35 | # 사실 개념은 똑같다. 구조를 어떤 것을 사용하냐의 차이일 뿐.
36 | # 다만 이경우, 나는 return prev가 헷갈렸다.
37 | # 직접 노트에 연산 과정을 풀어써보고 나서야 그렇구나- 했다.
38 | # 두 방식의 속도는 거의 비슷하며, memory usage는 반복구조가 좀 더 낮다.
39 | # 나는 반복 구조가 더 잘 읽히는 것 같다.
40 |
41 |
42 | # -----------------------------------------------------------------------------------------
43 | # 0813(연결리스트 한번 훑기 완료한 날) 책 없이 나 혼자 다시 풀이 도전
44 | class Solution:
45 | def reverseList(self, head: ListNode) -> ListNode:
46 | if not head:
47 | return None
48 |
49 | root = ListNode(None)
50 | root.next = head
51 |
52 | while head.next:
53 | nxt = head.next
54 | root.next, nxt.next, head.next = nxt, root.next, head.next.next
55 |
56 | return root.next
57 | # 세상에세상에~~~ 너무 행복해~~~~ 너무 잘돌아간다(runtime, memory 똑같음!)~~~
58 | # 코드는 조금 지저분할지 몰라도 내가 제대로 이해하고 있다는 거잖아! >_< 뿌듯
59 | # 이제 조금씩조금씩 다듬어주면서 어떻게 더 간결하고 명확하게 짤 수 있을까 생각해보자
60 |
61 |
62 |
--------------------------------------------------------------------------------
/02_leetcode/0225 implement stack using queues.py:
--------------------------------------------------------------------------------
1 | class MyStack:
2 |
3 | def __init__(self):
4 | from collections import deque
5 | self.queue = deque()
6 |
7 | def push(self, x: int) -> None:
8 | self.queue.append(x)
9 | for _ in range(len(self.queue) - 1):
10 | self.queue.append(self.queue.popleft())
11 | # 새로 추가하는 애 전까지 반복하면서 [0]을 맨뒤로 append한다.
12 | # 순서를 재정렬하는 것
13 |
14 | def pop(self) -> int:
15 | return self.queue.popleft()
16 |
17 | def top(self) -> int:
18 | return self.queue[0]
19 |
20 | def empty(self) -> bool:
21 | return not self.queue
22 |
23 | # Your MyStack object will be instantiated and called as such:
24 | # obj = MyStack()
25 | # obj.push(x)
26 | # param_2 = obj.pop()
27 | # param_3 = obj.top()
28 | # param_4 = obj.empty()
29 |
30 | # 파이썬에는 큐가 따로 없으니 데크를 이용
31 | # 하지만 큐의 기능, 즉 .append() .leftpop() [0]등으로만 구현해야함 (pop(), [-1]쓰면 반칙)
32 |
33 | # 나는 push는 그냥 append로 구현하고 top을 어떻게 해야할까 고민했는데 그게 잘 안되더라
34 | # [0]만 접근할 수 있었기 때문에 복사본을 만들어서 [0]을 계속 빼는방법 밖에 생각하지 못했음
35 |
36 | # 책풀이는 반대로 push에 이것저것을 적용했다.
37 | # 새로 push하는 요소를 [0]자리에 위치하게 하면 pop은 popleft로, top은 [0]로 간단히 해결할 수 있다.
38 |
--------------------------------------------------------------------------------
/02_leetcode/0232 implement queue using stacks.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class MyQueue:
3 | def __init__(self):
4 | self.stack = []
5 |
6 | def push(self, x: int) -> None:
7 | self.stack.append(x)
8 |
9 | def pop(self) -> int:
10 | reloc_stack = []
11 | for _ in range(len(self.stack)):
12 | reloc_stack.append(self.stack.pop())
13 | # 스택에서 스택으로 옮기면서 순서를 반대로 뒤집어준다.
14 | front = reloc_stack.pop()
15 |
16 | for _ in range(len(reloc_stack)):
17 | self.stack.append(reloc_stack.pop())
18 | # 원하는 요소를 뺐으니 다시 뒤집어준다.
19 |
20 | return front
21 |
22 | def peek(self) -> int:
23 | reloc_stack = []
24 | for _ in range(len(self.stack)):
25 | reloc_stack.append(self.stack.pop())
26 | front = reloc_stack[-1]
27 |
28 | for _ in range(len(reloc_stack)):
29 | self.stack.append(reloc_stack.pop())
30 |
31 | return front
32 |
33 | def empty(self) -> bool:
34 | return not self.stack
35 |
36 |
37 | # Your MyQueue object will be instantiated and called as such:
38 | # obj = MyQueue()
39 | # obj.push(x)
40 | # param_2 = obj.pop()
41 | # param_3 = obj.peek()
42 | # param_4 = obj.empty()
43 |
44 | # 파이썬에는 스택 자료형이 없으니 리스트를 사용하자
45 | # 사용할 수 있는 기능은 pop(), [-1], append() 정도
46 |
47 | # 0225 문제와 짝꿍이다
48 | # push 함수에서 제일 먼저 들어온 요소가 제일 뒤에 있을 수 있게 재정렬해주면 된다
49 | # 라고 생각했는데 코드를 짜보니 그렇지가 않다.
50 | # 큐를 쓸 때는 앞에서 빼서 뒤에 붙여주는게 가능했는데 스택은 빼고 더하고를 다 뒤에서만 할 수 있으므로
51 | # 225번 문제처럼 풀 수 없다.
52 | # 이 문제는 오히려 처음 내 방식, 즉 push는 그냥 append 해주고 pop에서 처리를 해주는 식으로 풀어야한다.
53 |
54 | # 맞긴 했는데 pop과 peek에서 새 스택으로 옮겼다가 다시 원상복구해주는게 좀 비효율적인 것 같고
55 | # 한줄 빼고는 같은 코드를 두번 쓰는 것도 낭비다.
56 |
57 |
58 |
59 | # 책 풀이
60 | class MyQueue:
61 | def __init__(self):
62 | self.input = []
63 | self.output = []
64 |
65 | def push(self, x: int) -> None:
66 | self.input.append(x)
67 |
68 | def pop(self) -> int:
69 | self.peek()
70 | return self.output.pop()
71 |
72 | def peek(self) -> int:
73 | if not self.output:
74 | while self.input:
75 | self.output.append(self.input.pop())
76 | return self.output[-1]
77 |
78 | def empty(self) -> bool:
79 | return self.input == [] and self.output == []
80 | # 와 너무 똑똑하다. myqueue에 추가한 데이터들을 input과 output에 흩어져 있는 형태겠지만
81 | # 이 문제에서 원하는게 큐 전체를 print하는 것도 아니고,
82 | # 4개의 기능들만 수행할 수 있으면 안에서 어떤 상태로 저장하고 있든 상관이 없다.
83 | # 나는 항상 self.stack에 완전한 큐의 형태를 구현하고 있어야한다고 생각해서 reloc에서 다시 옮겨주고 함수를 끝냈다.
84 | # 이 코드는 input과 output을 필요할 때 마다 자유자재로 쓴다.
85 | # append는 input에, pop은 output에서.
86 | # 심지어 input에 들어올 때 마다 output으로 옮겨주는게 아니라 output이 다 떨어질 때만 옮기는 것도 너무 현명하다ㅠㅠ
87 | # 요소들의 순서가 복잡하게 얽히는 것을 고민할 필요가 없고 연산시간도 분할상환분석으로 O(1)이 된다.
88 | # 하여간 너무 똑똑한 방법!!
--------------------------------------------------------------------------------
/02_leetcode/0238 product of array except self.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def productExceptSelf(self, nums: List[int]) -> List[int]:
4 | result = []
5 | for i, num in enumerate(nums):
6 | product = 1
7 | for ele in nums[:i] + nums[i+1:]:
8 | product *= ele
9 | result.append(product)
10 |
11 | return result
12 |
13 | # 문제에서 내건 조건은 division 을 사용하지 말고, O(n) 에 해결하라는 것
14 | # 전체곱구한뒤 포문 돌리면서 나누는 방식은 아웃, 내가 한 포문 두번도 타임아웃ㅜㅜ
15 | # 선택사항으로 constant space complexity 도 있다. (아웃풋 제외)
16 |
17 | # 책 풀이
18 | class Solution:
19 | def productExceptSelf(self, nums: List[int]) -> List[int]:
20 | left = [1]
21 | right = [1]
22 | result = []
23 |
24 | # i의 왼쪽값들을 곱한 리스트
25 | for i in range(len(nums)-1):
26 | left.append(nums[i] * left[-1])
27 |
28 | # i의 오른쪽값들을 곱한 리스트
29 | for i in range(len(nums)-1, 0, -1):
30 | right.append(nums[i] * right[-1])
31 |
32 | # 두 리스트를 곱해줌(right는 인덱스 거꾸로)
33 | for i in range(len(nums)):
34 | result.append(left[i] * right[len(nums)-i-1])
35 |
36 | return result
37 | # wow 이 아이디어 누가 처음 생각해내신거죠?? 말도안됨 너무 똑똑해
38 | # 정확히 말하면 책의 코드와는 조금 다르다. 책의 해설을 읽고 그 아이디어로 내가 짠 코드로,
39 | # output 값 말고도 left와 right 리스트가 있기 때문에 공간복잡도가 O(n)이다.
40 | # output 리스트를 계속해서 업데이트 해나갈 수 있으면 공간복잡도(1)을 만족할 수 있다. 한번 해보자
41 |
42 |
43 | class Solution:
44 | def productExceptSelf(self, nums: List[int]) -> List[int]:
45 | output = [1]
46 |
47 | # 똑같이 i의 왼쪽값들을 곱한 리스트지만 여기에 바로 업데이트 작업을 해줄거임
48 | for i in range(len(nums)-1):
49 | output.append(nums[i] * output[-1])
50 |
51 | # right는 리스트없이 바로 더할거라서 곱한 값을 계속 업데이트하면서 지니고 가는 vairable이 필요
52 | right_product = 1
53 | for i in range(len(nums)-1, 0, -1):
54 | right_product *= nums[i]
55 | output[i-1] *= right_product
56 |
57 | return output
58 | # 시간복잡도, 공간복잡도 모두 개선됐다.
--------------------------------------------------------------------------------
/02_leetcode/0310 minimum height trees.py:
--------------------------------------------------------------------------------
1 | class Solution:
2 | def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]:
3 |
--------------------------------------------------------------------------------
/02_leetcode/0316 remove duplicate letters.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def removeDuplicateLetters(self, s: str) -> str:
4 | count = collections.Counter(s)
5 | # 알파벳별 횟수 카운트
6 |
7 | stack = []
8 | for char in s:
9 | count[char] -= 1
10 | if stack and char < stack[-1]:
11 | while stack and count[stack[-1]] > 0:
12 | stack.pop()
13 |
14 | stack.append(char)
15 |
16 | return ''.join(stack)
17 | # 내가 한 건 여기까지가 한계인데 해결하지 못하는 케이스들이 있다ㅜㅜ
18 | # 이미 해당 char이 stack에 잘 담겨있는 경우인데 그 조건을 코드 어디다가 넣어야하는지 감을 못잡는 것 같다.
19 |
20 |
21 | # 책 풀이
22 | class Solution:
23 | def removeDuplicateLetters(self, s: str) -> str:
24 | counter = collections.Counter(s)
25 |
26 | stack = []
27 | for char in s:
28 | counter[char] -= 1
29 | if char in stack:
30 | # 내가 놓친 부분
31 | continue
32 | while stack and char < stack[-1] and counter[stack[-1]] > 0:
33 | stack.pop()
34 | stack.append(char)
35 |
36 | return ''.join(stack)
37 | # 그냥 가장 먼저 char in stack을 체크해주면 되는구나. stack 안에서의 위치와는 상관없이
38 | # 코드를 제대로 짰다면 이미 잘 되어 있으리라고 추측하고 그냥 저렇게 간단히 확인해주는 과정을 추가하면 된다.
39 | # 그리고 나는 내 머리속의 논리를 그대로 옮겨서 if 문 안에 while문이 들어가 있는데
40 | # 그것도 while 문으로 합쳐서 한줄로 줄일 수 있다.
41 | # 꼭 stack을 써야한다는 조건은 없지만 만약 스택을 활용해서 하고 싶다면
42 | # 사실 char in stack은 올바르지 않다. 스택이라는 추상자료형의 기능에는 탐색연산이 없기 때문
43 | # 꼭 스택을 정석대로 쓰고 싶다!하면 stack과는 별개로 seen이라는 세트를 하나 만들어서 해결할 수 있다.
44 | # 아직 부족하지만 혼자서 짠 것도 어느정도 솔루션의 흐름을 쫓아가고 있는 것 같아 기분이 좋다.ㅎㅎ
45 |
46 |
47 | # 책 풀이 - 정석 stack
48 | class Solution:
49 | def removeDuplicateLetters(self, s: str) -> str:
50 | counter = collections.Counter(s)
51 |
52 | stack = []
53 | seen = set()
54 | for char in s:
55 | counter[char] -= 1
56 | if char in seen:
57 | continue
58 | while stack and char < stack[-1] and counter[stack[-1]] > 0:
59 | seen.remove(stack.pop())
60 | stack.append(char)
61 | seen.add(char)
62 |
63 | return ''.join(stack)
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/02_leetcode/0328 odd even linked list.py:
--------------------------------------------------------------------------------
1 | # Definition for singly-linked list.
2 | # class ListNode:
3 | # def __init__(self, val=0, next=None):
4 | # self.val = val
5 | # self.next = next
6 |
7 | # 시간복잡도 O(n), 공간복잡도 O(1)에 풀이하라는 조건
8 |
9 | # 책 풀이 - 반복구조 이용
10 | class Solution:
11 | def oddEvenList(self, head: ListNode) -> ListNode:
12 | # 예외처리
13 | if not head:
14 | return None
15 |
16 | odd = head
17 | even = head.next
18 | even_head = head.next
19 | # 마지막에 홀수리스트의 끝과 이어줄 때 짝수리스트의 첫 노드가 필요하므로 even_head를 별도로 생성
20 |
21 | while even and even.next:
22 | odd.next, even.next = odd.next.next, even.next.next
23 | odd, even = odd.next, even.next
24 |
25 | odd.next = even_head
26 | return head
27 | # 처음에 든 생각은 이거 홀수리스트랑 짝수리스트를 따로 만들어 나가는데 그럼 공간복잡도 O(1)이 아닌거 아냐?
28 | # 음 근데 생각해보면 O(1)이라는게 output말고 다른 변수가 없게하라- 이게 아니라
29 | # n(여기서는 노드 개수)에 상관없이 일정하게 사용하는 것을 말하기 때문에
30 | # odd, even, even_head는 n의 크기에 관계없이 항상 일정하게 사용하고 있으므로 만족한다.
31 | # 그리고 홀수/짝수리스트도 기존의 n을 반 쪼개서 할당한 것이므로 공간차지가 늘어나지 않는다.
32 | # 맨 처음부터 뒤로 쭉 지나가면서 홀수/짝수리스트를 동시에 생성하므로 시간복잡도도 O(n)이다.
33 | # 음 코드 짜는건 그렇게 어렵지 않았다. 근데 홀수/짝수를 따로 만들어 나가야겠다는 아이디어를 떠올리지 못하니
34 | # 링크가 유실되면 다음 애를 찾을 수가 없으니까 이 링크를 어디다가 임시로 연결해놔야하지? 하면서 점점 복잡해졌다.
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/02_leetcode/0344 reverse string.py:
--------------------------------------------------------------------------------
1 | # 투포인터 사용
2 | class Solution:
3 | def reverseString(self, s: List[str]) -> None:
4 | """
5 | Do not return anything, modify s in-place instead.
6 | """
7 | left, right = 0, len(s) - 1
8 | while left < right:
9 | s[left], s[right] = s[right], s[left]
10 | left += 1
11 | right -= 1
12 |
13 | class Solution:
14 | def reverseString(self, s: List[str]) -> None:
15 | right = len(s) - 1
16 | for left in range(len(s)//2):
17 | s[left], s[right] = s[right], s[left]
18 | right -= 1
19 | # 책에 나와있는 방법은 위의 것인데 나는 왠지 while 문을 별로 안좋아하는 듯 하다.
20 | # 오류가 날 때까지 기다리는 느낌이랄까.. 시스템이 불안정한 느낌
21 | # 그래서 포문으로도 한번 짜보았다. 잘 돌아간다ㅎㅎ
22 | # 걸리는 시간은 212ms로 동일하다.
23 |
24 |
25 | # 짱짱인 파이썬 기본 함수를 쓰는 방법
26 | class Solution:
27 | def reverseString(self, s: List[str]) -> None:
28 | s.reverse()
29 | # 문자열에는 슬라이싱이 있다면 리스트에는 reverse!
30 | # 새로운 변수를 만들어내지 않고 s를 변경한다. a = s.reverse() 랑 다르게
31 | # 앗 리스트도 슬라이싱이 된다고 한다. 근데 플랫폼에 따라 제대로 작동하지 않을 수도 있다고 하니 이건 생각하지 말자
32 | # 리버스함수는 200ms
--------------------------------------------------------------------------------
/02_leetcode/0347 top k frequent elements.py:
--------------------------------------------------------------------------------
1 | # 시간복잡도가 O(n lg(n)) 이내일 것을 요구한다.
2 | # 알파벳 별 출현 빈도수 딕셔너리를 생성하는 건 O(n)이므로
3 | # 결국 dict를 어떻게 정렬할거냐는 문제.
4 | # k는 최대 n이므로 그냥 for문으로 탐색하면 O(n^2)다.
5 |
6 | # 내 풀이
7 | class Solution:
8 | def topKFrequent(self, nums: List[int], k: int) -> List[int]:
9 | freq = collections.defaultdict(int)
10 | for num in nums:
11 | freq[num] += 1
12 |
13 | heap = []
14 | for key, value in freq.items():
15 | heapq.heappush(heap, (-value, key))
16 |
17 | sorted_list = []
18 | for _ in range(k):
19 | sorted_list.append(heapq.heappop(heap)[1])
20 |
21 | return sorted_list
22 | # 힙정렬을 사용했다.(시간복잡도 O(n lg(n))) 우선순위와 내가 원하는 값이 따로 있으니까 이게 적합할 것 같아서~.~
23 | # runtime 상위20프로 memory usage 상위5프로! 띠용 너무 잘했다.
24 | # 책풀이도 힙정렬을 사용했다.
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/02_leetcode/0404 sum of left leaves.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def sumOfLeftLeaves(self, root: TreeNode) -> int:
11 | if root is None:
12 | return 0
13 |
14 | from collections import deque
15 | q = deque([root])
16 | tot_sum = 0
17 | while q:
18 | node = q.popleft()
19 | if node:
20 | if node.left and node.left.left is None and node.left.right is None:
21 | tot_sum += node.left.val
22 | elif node.left:
23 | q.append(node.left)
24 | if node.right:
25 | q.append(node.right)
26 |
27 | return tot_sum
28 | # Status: Accepted
29 | # Algorithm: BFS
30 | # Time Complexity: O(n)
31 | # Runtime: 32ms (top 24.6%)
32 |
33 |
--------------------------------------------------------------------------------
/02_leetcode/0509 fibonacci number.py:
--------------------------------------------------------------------------------
1 | class Solution:
2 | def fib(self, N: int) -> int:
3 | table = {
4 | 0: 0,
5 | 1: 1
6 | }
7 |
8 | for i in range(2, N + 1):
9 | table[i] = table[i - 1] + table[i - 2]
10 |
11 | return table[N]
12 | # Status: Accepted
13 | # Algorithm: Tabulation
14 | # Time Complexity: O(N)
15 | # Runtime: 24ms (top 3.9%)
16 |
--------------------------------------------------------------------------------
/02_leetcode/0561 array partition.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def arrayPairSum(self, nums: List[int]) -> int:
4 | nums.sort()
5 | result = sum([x for x in nums[::2]])
6 |
7 | return result
8 | # runtime 상위 10퍼센트~~ 근데 list를 한번 더 만들기 때문에 공간복잡도는 좋지않다.
9 | # 리스트 없이 포문으로 해보자
10 | # 아! sum(nums[::2]) 로 하면 될 것을 괜히 한번 꼬아 생각했다.
11 | # 책을 보면 아예 한줄로 코드를 짤 수도 있다. return sum(sorted(nums)[::2])
12 |
13 |
14 | # 내 풀이2
15 | class Solution:
16 | def arrayPairSum(self, nums: List[int]) -> int:
17 | nums.sort()
18 | result = 0
19 |
20 | for i in range(0, len(nums), 2):
21 | result += nums[i]
22 |
23 | return result
24 | # 시간복잡도는 살짝 안좋아지고 대신 공간복잡도가 조금 개선됨
25 |
26 |
--------------------------------------------------------------------------------
/02_leetcode/0617 merge two binary trees.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
11 | if t1 and t2:
12 | node = TreeNode(t1.val + t2.val)
13 | node.left = self.mergeTrees(t1.left, t2.left)
14 | node.right = self.mergeTrees(t1.right, t2.right)
15 | return node
16 |
17 | else:
18 | return t1 or t2
19 | # Status: Accepted
20 | # Algorithm: Recursion
21 | # Time Complexity: O(n)?
22 | # Runtime: 80ms (top 1%)
--------------------------------------------------------------------------------
/02_leetcode/0622 design circular queue.py:
--------------------------------------------------------------------------------
1 | class MyCircularQueue:
2 |
3 | def __init__(self, k: int):
4 | self.q = [None] * k
5 | self.maxlen = k
6 | self.p1 = 0
7 | # 맨 첫요소 인덱스
8 | self.p2 = 0
9 | # 맨 마지막 요소 다음 인덱스
10 |
11 | def enQueue(self, value: int) -> bool:
12 | """
13 | Insert an element into the circular queue. Return true if the operation is successful.
14 | """
15 | if self.q[self.p2] is None:
16 | self.q[self.p2] = value
17 | self.p2 = (self.p2 + 1) % self.maxlen
18 | return True
19 | else:
20 | return False
21 |
22 | def deQueue(self) -> bool:
23 | """
24 | Delete an element from the circular queue. Return true if the operation is successful.
25 | """
26 | if self.q[self.p1] is None:
27 | return False
28 | else:
29 | self.q[self.p1] = None
30 | self.p1 = (self.p1 + 1) % self.maxlen
31 | return True
32 |
33 | def Front(self) -> int:
34 | """
35 | Get the front item from the queue.
36 | """
37 | if self.q[self.p1] is None:
38 | return -1
39 | else:
40 | return self.q[self.p1]
41 |
42 | def Rear(self) -> int:
43 | """
44 | Get the last item from the queue.
45 | """
46 | if self.q[self.p2 - 1] is None:
47 | return -1
48 | else:
49 | return self.q[self.p2 - 1]
50 |
51 | def isEmpty(self) -> bool:
52 | """
53 | Checks whether the circular queue is empty or not.
54 | """
55 | if self.p1 == self.p2 and self.q[self.p1] is None:
56 | return True
57 | else:
58 | return False
59 |
60 | def isFull(self) -> bool:
61 | """
62 | Checks whether the circular queue is full or not.
63 | """
64 | if self.p1 == self.p2 and self.q[self.p1] is not None:
65 | return True
66 | else:
67 | return False
68 |
69 | # Your MyCircularQueue object will be instantiated and called as such:
70 | # obj = MyCircularQueue(k)
71 | # param_1 = obj.enQueue(value)
72 | # param_2 = obj.deQueue()
73 | # param_3 = obj.Front()
74 | # param_4 = obj.Rear()
75 | # param_5 = obj.isEmpty()
76 | # param_6 = obj.isFull()
77 |
78 | # 원형큐가 생소했지만 구현 자체는 어렵진 않았다
--------------------------------------------------------------------------------
/02_leetcode/0653 two sum4 input is a BST.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def findTarget(self, root: TreeNode, k: int) -> bool:
11 | if root is None:
12 | return False
13 |
14 | self.BST = []
15 | def inorder(node):
16 | if node.left:
17 | inorder(node.left)
18 | self.BST.append(node.val)
19 | if node.right:
20 | inorder(node.right)
21 |
22 | inorder(root)
23 | start = 0
24 | end = len(self.BST) - 1
25 | while start < end:
26 | two_sum = self.BST[start] + self.BST[end]
27 | if two_sum == k:
28 | return True
29 | if two_sum < k:
30 | start += 1
31 | if two_sum > k:
32 | end -= 1
33 |
34 | return False
35 | # Status: Accepted
36 | # Algorithm: BST inorder traversal + two pointer
37 | # Time Complexity: O(n)
38 | # Runtime: 92ms (top 46.2%)
39 |
40 |
41 | # leetcode discussion solution
42 | class Solution:
43 | def findTarget(self, root: TreeNode, k: int) -> bool:
44 | if root is None:
45 | return False
46 |
47 | from collectinos import deque
48 | q = deque(root)
49 | seen = set()
50 |
51 | while q:
52 | node = q.leftpop()
53 | if node.val in seen:
54 | return True
55 | seen.add(k - node.val)
56 | if node.left: q.append(node.left)
57 | if node.right: q.append(node.right)
58 |
59 | return False
60 | # Status: Accepted
61 | # Algorithm: BFS search
62 | # Time Complexity: O(n)
63 | # Runtime: 72ms (top 5.1%)
64 |
65 |
--------------------------------------------------------------------------------
/02_leetcode/0671 second minimum node in a binary tree.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # leetcode discussion solution
9 | class Solution:
10 | def findSecondMinimumValue(self, root: TreeNode) -> int:
11 | self.sec_min = sys.maxsize
12 |
13 | def traverse(node):
14 | if node:
15 | if node.val > root.val:
16 | self.sec_min = min(self.sec_min, node.val)
17 | traverse(node.left)
18 | traverse(node.right)
19 |
20 | traverse(root)
21 | return -1 if self.sec_min == sys.maxsize else self.sec_min
22 | # Status: Accepted
23 | # Algorithm:
24 | # Time Complexity: O(n)
25 | # Runtime: 32ms (top 35.6%)
26 | # 문제 자체가 좀 별로인 것 같다. 비추수가 많은 문제는 이유가 있는듯..
27 | # 효율적으로 풀려고 엄청 애썼는데 결국 n개를 traverse할 수 밖에 없는 예외 예제들이 뜨더라.
--------------------------------------------------------------------------------
/02_leetcode/0700 search in a binary search tree.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def searchBST(self, root: TreeNode, val: int) -> TreeNode:
11 | if root:
12 | if root.val == val:
13 | return root
14 | elif root.val < val:
15 | return self.searchBST(root.right, val)
16 | else:
17 | return self.searchBST(root.left, val)
18 |
19 | return None
20 | # Status: Accepted
21 | # Algorithm: Recursion
22 | # Time Complexity: O(lg(n))
23 | # Runtime: 108ms (top 71.5%)
24 |
25 | class Solution:
26 | def searchBST(self, root: TreeNode, val: int) -> TreeNode:
27 | iterator = root
28 | while iterator:
29 | if iterator.val == val:
30 | return iterator
31 | elif iterator.val < val:
32 | iterator = iterator.right
33 | else:
34 | iterator = iterator.left
35 |
36 | return None
37 | # Status: Accepted
38 | # Algorithm: Iteration
39 | # Time Complexity: O(lg(n))
40 | # Runtime: 80ms (top 26.8%)
--------------------------------------------------------------------------------
/02_leetcode/0739 daily temperatures.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def dailyTemperatures(self, T: List[int]) -> List[int]:
4 | stack = [(0, 0)]
5 | # 첫번째 예외처리를 해주기 보다는 그냥 없어질 값을 넣어줬다
6 | result = {}
7 | for i, temp in enumerate(T):
8 | while temp > stack[-1][1]:
9 | result[stack[-1][1]] = (i - stack.pop()[0])
10 | stack.append((i, temp))
11 | # 새로 들어오는 값이 stack의 마지막 값보다 큰 경우,
12 | # 마지막 값은 빼주고 인덱스 차이를 result에 기록
13 | # 새로 넣을 값보다 큰 값을 만날 때까지 반복
14 | stack.append((i, temp))
15 |
16 | output = []
17 | for key, value in sorted(result.keys()):
18 | output.append(value)
19 | # 인덱스 순으로 정렬해서 인덱스는 버리고 값만 기록
20 | return output
21 | # 잘 짠거 같은데 에러가 난다.. 어려워 흑흑
22 | # 일단 stack의 첫번째 값으로 넣어준 애는 첫 for문에서 빠질 것이며, result에 0, 0 으로 기록된다.
23 | # 첫번째 요소가 stack에서 빠질 때 result[0]의 값이 업데이트 되므로 자연스럽게 사라진다.
24 |
25 |
26 | # 0830 다시도전
27 | class Solution:
28 | def dailyTemperatures(self, T: List[int]) -> List[int]:
29 | stack = []
30 | result = {}
31 | for i, temp in enumerate(T):
32 | while stack and temp > stack[-1][1]:
33 | to_pop = stack.pop()
34 | result[to_pop[0]] = i - to_pop[0]
35 | stack.append([i, temp])
36 |
37 | while stack:
38 | to_pop = stack.pop()
39 | result[to_pop[0]] = 0
40 |
41 | output = []
42 | for key in sorted(result.keys()):
43 | output.append(result[key])
44 | # 인덱스 순으로 정렬해서 인덱스는 버리고 값만 기록
45 | return output
46 | # 위에 내가 썼던 코드가 어디가 틀렸는지 눈에 보여서 수정해줬다 기쁘당
47 | # memory usage가 좀 높다. stack은 필수적인 부분이라서 괜찮은데 output과 result가 굳이 두번이나 기록될 필요는 없어보인다.
48 |
49 |
50 | # 책 풀이
51 | class Solution:
52 | def dailyTemperatures(self, T: List[int]) -> List[int]:
53 | answer = [0] * len(T)
54 | stack = []
55 | for i, cur in enumerate(T):
56 | while stack and cur > T[stack[-1]]:
57 | last = stack.pop()
58 | answer[last] = i = last
59 | stack.append(i)
60 |
61 | return answer
62 | # 오! 딱 내가 부족한 부분을 깔끔히 해결한 코드다.
63 | # 우선 stack에 기온을 저장하지 않고 매번 접근 연산으로 불러온다.
64 | # 접근 연산은 O(1)이고 answer에 값을 업데이트 해줄 때는 온도가 필요하지 않으므로 이게 더 좋은 방식인 것 같다.
65 | # 그리고 나와 다르게 결과값을 저장하는 변수가 한개다.
66 | # 저렇게 0으로 디폴트 된 리스트에 인덱스로 접근해서 업데이트 해주면 나중에 다시 정렬할 일도,
67 | # dict를 순환하면서 value를 빼올 일도, stack에 남은 값들(더 따뜻한 날이 없어서 0으로 기록해줘야하는 날들)을 처리해줄 필요도 없다.
68 | # 시간도 아끼고 코드도 짧고 공간도 절약하는 참 좋은 방법이다.
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/02_leetcode/0743 network delay time.py:
--------------------------------------------------------------------------------
1 | # 방향, 양수가중치 그래프 -> 다익스트라를 써보자
2 | # 노드 갯수는 1이상 100이하
3 | # 엣지 개수는 1이상 6000이하
4 | # 한 엣지의 최대 가중치 값은 100
5 |
6 | class Solution:
7 | def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
8 | completed = [0] * (N + 1)
9 | distance = [sys.maxsize] * (N + 1)
10 |
11 | from collections import defaultdict
12 | edges = defaultdict(list)
13 |
14 | for edge in times:
15 | edges[edge[0]].append((edge[1], edge[2]))
16 |
17 | for _ in range(100):
18 | completed[K] = 1
19 | for edge in edges[K]:
20 | target, weight = edge
21 | distance[target] = min(distance[K] + weight, distance[target])
22 | next_k = -1
23 | for edge in edges[K]:
24 | if completed[edge[0]] != 1:
25 | if (0 <= next_k and distance[edge[0]] < distance[next_k]) or next_k == -1:
26 | next_k = edge[0]
27 | if next_k == -1:
28 | break
29 | K = next_k
30 |
31 | if sum(completed) != len(completed) - 1:
32 | return -1
33 | else:
34 | return max(distance)
35 | # Status: Wrong Answer
36 | # Algorithm: Dijkstra
37 | # Time Complexity:
38 | # Runtime:
39 | # Intuition: 다익스트라를 처음 써보는데 이것저것 보수하다보니 너무 지저분해졌다. 다시 한번 해보자-!
40 |
41 |
42 | # my solution
43 | class Solution:
44 | def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
45 | completed = [0] * (N + 1)
46 | dist = [sys.maxsize] * (N + 1)
47 |
48 | from collections import defaultdict
49 | edges = defaultdict(list)
50 | for edge in times:
51 | edges[edge[0]].append((edge[1], edge[2]))
52 |
53 | dist[K] = 0
54 | while True:
55 | for target, weight in edges[K]:
56 | dist[target] = min(dist[target], dist[K] + weight)
57 | completed[K] = 1
58 | next_k = -1
59 | for target in range(1, N + 1):
60 | if completed[target] != 1 and dist[target] != sys.maxsize:
61 | if (0 <= next_k and dist[target] < dist[next_k]) or next_k < 0:
62 | next_k = target
63 | if next_k < 0:
64 | break
65 | K = next_k
66 |
67 | if 0 in completed[1:]:
68 | return -1
69 | return max(dist[1:])
70 | # Status: Accepted
71 | # Algorithm: Dijkstra
72 | # Time Complexity: O(N^2 + E) (E: num of edges)
73 | # Runtime: 488ms (top 15.8%)
74 | # Intuitions: 위의 코드를 깔끔하게 정리하고 디버깅한 코드. next_k 설정하는 포문에서 생각해줘야할 조건이 꽤 있었다.
75 | # 릿코드 솔루션을 보니 다익스트라에 힙을 사용해서 O(E logE)로 줄일 수 있던데 내일 해보자
76 |
--------------------------------------------------------------------------------
/02_leetcode/0771 jewels and stones.py:
--------------------------------------------------------------------------------
1 | # 내 풀이
2 | class Solution:
3 | def numJewelsInStones(self, J: str, S: str) -> int:
4 | dict = {}
5 | for char in J:
6 | dict[char] = 0
7 |
8 | for stone in S:
9 | if stone in dict.keys():
10 | dict[stone] += 1
11 |
12 | return sum(dict.values())
13 | # 굿굿 runtime- 상위50프로, memory usage- 상위10프로
14 |
15 |
16 | # 내 풀이2
17 | class Solution:
18 | def numJewelsInStones(self, J: str, S: str) -> int:
19 | counter = collections.Counter(S)
20 |
21 | sum = 0
22 | for char in J:
23 | if char in counter.keys():
24 | sum += counter[char]
25 |
26 | return sum
27 | # 방법은 똑같고 counter를 이용해서 코드수를 줄일 수 있음 메모리를 조금 더 쓴다.
28 | # 책의 풀이도 내 것(위의 두개)과 똑같다!! ㅎㅎ기분좋당 맨 마지막 풀이4는 따로 써둔다.
29 |
30 |
31 | class Solution:
32 | def numJewelsInStones(self, J: str, S: str) -> int:
33 | return sum(s in J for s in S)
34 | # 리스트 컴프리헨션 대괄호를 넣어서 생각하면 좀 더 이해가 쉽다.
35 | # [s for s in S]는 character를 한 개씩 리스트의 요소로 만든다.
36 | # [s in J for s in S]는 character가 J에 들었는지 불린값으로 요소가 기록된다.
37 | # 그럼 요걸 sum해주면 True는 1로 계산되니까 총 갯수를 세준다! 넘모 똑똑한 방법
--------------------------------------------------------------------------------
/02_leetcode/0819 most common word.py:
--------------------------------------------------------------------------------
1 | # 내 답안
2 | class Solution:
3 | def mostCommonWord(self, paragraph: str, banned: List[str]) -> str:
4 | # 언제 str에서 list로 변환할건지. puctuation 제거 전? 후?
5 | # str 타입은 immutable 이므로 펑츄에이션 제거 + lower 하면서 바로 list로 만들어주는 것이 좋겠다.
6 |
7 | # 클리닝작업
8 | cleaned_paragraph = re.sub(r'[^\w]', ' ', paragraph)
9 | # 토큰화 (단어들 리스트로 생성)
10 | words = [word for word in cleaned_paragraph.lower().split() if word not in banned]
11 |
12 | # 빈도수 딕셔너리 생성
13 | counts = {}
14 | for word in words:
15 | if word not in counts.keys():
16 | counts[word] = 1
17 | else:
18 | counts[word] += 1
19 |
20 | # 빈도수로 정렬
21 | sorted_counts = sorted(counts.items(), key=lambda x: x[1], reverse=True)
22 |
23 | return sorted_counts[0][0]
24 | # 첫번째 튜플의 0번째 인덱스 출력
25 | # 람다펑션이 굉장히 유용하구나.
26 | # sort()는 원본 리스트를 변경하고 아무것도 return하지 않는다 (None)
27 | # sorted()는 원본에 영향을 주지 않고 새로운 객체를 반환하며 모든 iterable 에 동작한다.
28 | # 문자열도 사용가능하긴 한데 결과값이 문자열이 아닌 글자당 리스트로 나오는 것에 유의
29 | # sort는 list에 속한 함수, 즉 메소드이므로 .sort()로 사용하고 sorted는 sorted(iterable)로 사용한다.
30 | # 메소드와 함수의 차이, 좀 더 공부하기
31 | # runtime은 상위 30프로, 메모리사용량은 상위 10프로! 넘모 뿌듯ㅜㅜ
32 |
33 |
34 |
35 | # 교재의 방식: Counter 객체 사용
36 | class Solution:
37 | def mostCommonWord(self, paragraph: str, banned: List[str]) -> str:
38 | words = [word for word in re.sub(r'[^\w]', ' ', paragraph)
39 | .lower().split()
40 | if word not in banned]
41 |
42 | counts = collections.Counter(words)
43 | return counts.most_common(1)[0][0]
44 | # Counter는 파이썬에서 제공하는, 딕셔너리와 관련된 특수한 형태의 자료형이라고 한다.
45 | # a = [1, 2, 3, 4, 5, 5, 5, 6, 6] 일 때,
46 | # b = collections.Counter(a)
47 | # b를 출력하면 Counter({5: 3, 6: 2, 1: 1, 2: 1, 3: 1, 4: 1}) 로 딕셔너리를 만들어준다.
48 | # most_common 메소드를 이용해서 빈도수가 높은 애들을 리스트로 받아볼 수 있다.
49 | # 즉, b.most_common(2) 은 [(5, 3), (6, 2)]를 return 한다
50 |
51 |
--------------------------------------------------------------------------------
/02_leetcode/0841 keys and rooms.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | class Solution:
3 | def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
4 | from collections import defaultdict
5 | status = defaultdict(int)
6 | self.visited = set()
7 |
8 | def bfs(idx):
9 | if status[idx] == 0:
10 | self.visited.add(idx)
11 | status[idx] = 1
12 | for neighbor in rooms[idx]:
13 | bfs(neighbor)
14 |
15 | bfs(0)
16 | return len(self.visited) == len(rooms)
17 | # Status: Accepted
18 | # Algorithm: BFS
19 | # Time Complexity: O(N + E) (N: num of rooms, E: num of keys(edges))
20 | # Runtime: 60ms (top 2.5%)
21 | # Intuition: 0에서부터 탐색을 진행하며 방문한 방들은 visited에 추가
22 | # 탐색을 완료한 후 방문한 방 개수와 전체 방 개수가 같으면 True
23 | # 사실 set이 아니라 list로 했어도 같았겠지만 의미를 명확히 하기 위해 set 사용
--------------------------------------------------------------------------------
/02_leetcode/0937 reorder log files.py:
--------------------------------------------------------------------------------
1 | class Solution:
2 | def reorderLogFiles(self, logs: List[str]) -> List[str]:
3 | nums, letters = [], []
4 | for log in logs:
5 | if log.split()[1].isdigit():
6 | nums.append(log)
7 | else:
8 | letters.append(log)
9 | # 현재 숫자든 레터든 전부 문자열 타입(str)으로 들어와있다.
10 | # isdigit은 해당 문자열이 숫자로 변환 가능한지 확인해주는 함수
11 | # 마찬가지로 isalpha도 사용가능하다. True/False로 결과값이 나온다.
12 | # 역시 있을거라고 추측하는 함수는 파이썬에 다 있다.
13 | # 둘다 str 타입에 사용하는 함수임 헷갈리지 말기~
14 |
15 | # nums는 더이상 건드릴 것 없이 정렬한 letters 뒤에 붙여주면 된다.
16 |
17 | letters.sort(key=(lambda x: (x.split()[1:], x.split()[0])))
18 | # 람다는 a = lambda x: (x*2) + 3
19 | # a(5) = 13처럼 함수 선언없이 간단히 한줄로 펑션을 define한다.
20 | # sort의 key perameter에 원래 letters의 요소들(x)이 차례로 들어갈텐데,
21 | # 이 때 람다펑션이 발동해서 x의 두번째 요소를 키의 argument로 넣어준다.
22 | # 뒤에 x.split()[0]은 앞의 key가 동일한 경우 0번째, 즉 아이덴티파이어를 비교하라는 뜻
23 | # 나 혼자서는 요 부분 못짰을거다.
24 |
25 | ordered_logs = letters + nums
26 | return ordered_logs
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/02_leetcode/0938 range sum of bst.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # 릿코드 솔루션1 - 재귀구조
9 | class Solution:
10 | def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int:
11 | def dfs(node):
12 | if node:
13 | if L <= node.val <= R:
14 | self.ans += node.val
15 | if L < node.val:
16 | dfs(node.left)
17 | if R > node.val:
18 | dfs(node.right)
19 |
20 | self.ans = 0
21 | dfs(root)
22 | return self.ans
23 |
24 |
25 | # 릿코드 솔루션2
26 | class Solution:
27 | def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int:
28 | ans = 0
29 | stack = [root]
30 | while stack:
31 | node = stack.pop()
32 | if node:
33 | if L <= node.val <= R:
34 | ans += node.val
35 | if L < node.val:
36 | stack.append(node.left)
37 | if node.val < R:
38 | stack.append(node.right)
39 |
40 | return ans
41 |
42 | # 이제 트리,그래프 등 비선형구조 문제를 풀어야하는데 처음 접하다보니 감이 잘 안잡혀서 공부한 문제
43 | # recursive나 iterative나 방식은 똑같다.
44 | # 이 노드가 조건을 만족하면 총합변수에 더하고, L보다 크면 왼쪽노드를 다시 조사, R보다 작으면 오른쪽 노드를 다시 조사
45 | # 이 세개가 elif가 아닌 if로, 조건만 만족하면 한 노드에 대해 세 연산이 다 수행될 수 있다.
46 | # 나는 L따로 R따로 하면서 어떻게 똑같은 값을 두번 더하지 않을 수 있을까 고민했다(node.visited를 만들어줘야하나 했음)
47 | # 그리고 dfs라는게 트리에서도 이렇게 자연스럽게 쓰이는 거구나.
48 | # 이론을 배울 때 그래프에서 배웠는데 dfs는 굉장히 다양한 문제에서 쓰일 수 있는 알고리즘인 것 같다.
--------------------------------------------------------------------------------
/02_leetcode/0968 binary tree cameras.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def minCameraCover(self, root: TreeNode) -> int:
11 | count = [0, 0]
12 |
13 | def search(node, idx):
14 | if node:
15 | count[idx] += 1
16 | if node.left:
17 | search(node.left, 1 - idx)
18 | if node.right:
19 | search(node.right, 1 - idx)
20 |
21 | search(root, 0)
22 |
23 | if count[0] == 0 and count[1] == 0:
24 | return None
25 | elif count[0] == 0:
26 | return count[1]
27 | elif count[1] == 0:
28 | return count[0]
29 | else:
30 | return min(count)
31 | # Status: Wrong Answer
32 | # Algorithm: Recursion
33 | # Time Complexity: O(n)
34 | # Runtime:
35 | # Intuition: 아이디어 자체가 틀렸다.
36 | # 홀수레벨 노드의 합과 짝수레벨 노드의 합 중 작은 값을 return 했는데 반례가 있음
37 | # [0,0,null,null,0,0,null,null,0,0]
38 |
39 | # 코드만 놓고 보자면 위에 recursion은 잘 짰고 밑에 0인 요소를 제외하고 min 값을 도출해낼 때는 더 간결하게 쓸 수 있을 것 같은데 어떻게 하지?
40 |
41 | # leetcode solution을 봤는데 아직 hard는 도전하지 않는걸로..
--------------------------------------------------------------------------------
/02_leetcode/0979 distribute coins in binary tree.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 | class Solution:
8 | def distributeCoins(self, root: TreeNode) -> int:
9 |
10 |
11 | # 해설봐도 이해가 잘 안됨...:( 나중에 다시 보는걸로
12 |
--------------------------------------------------------------------------------
/02_leetcode/0997 find the town judge.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | class Solution:
3 | def findJudge(self, N: int, trust: List[List[int]]) -> int:
4 | if N == 1 and len(trust) == 0:
5 | return 1
6 |
7 | def check(num):
8 | for pair in trust:
9 | if pair[0] == num:
10 | return False
11 | return True
12 |
13 | from collections import defaultdict
14 | trusted = defaultdict(int)
15 | for pair in trust:
16 | trusted[pair[1]] += 1
17 |
18 | candidates = []
19 | for num in trusted:
20 | if trusted[num] == N - 1:
21 | candidates.append(num)
22 |
23 | for num in candidates:
24 | result = check(num)
25 | if result == True:
26 | return num
27 |
28 | return -1
29 | # Status: Accepted
30 | # Algorithm:
31 | # Time Complexity: O(n^2)
32 | # Runtime: 776ms (top 10.2%)
33 | # Intuition: town judge가 있다면 trust안의 list에서 두번째 자리에는 N-1번 등장할 것이고, 첫번째 자리에는 한번도 등장하지 않을 것이다.
34 | # trust를 살펴보면서 각 번호의 사람을 믿고 있는 사람이 몇명인지 센다(trusted 딕셔너리)
35 | # 그 중에 N-1 값을 가지고 있는 사람들을 candidates 리스트로 모은다.
36 | # 후보자들에 대해서 한번 더 포문을 돌리면서 한번이라도 첫번째 자리에 등장하면 제끼고 한번도 등장하지 않으면 바로 그 사람을 return 한다.
37 | # Note: 단계가 많고, 예외처리도 따로 해줘야 하고, 새로운 함수도 만들었고, 포문도 여러번 돌려서 굉장히 안좋은 코드라고 생각하며 짰는데 시간/공간 복잡도 모두 잘 나와서 얼떨떨하다.
38 | # 더 깔끔하게 할 수 있을 것 같은데. trusted를 만들면서 pair[0]에 등장하면 바로 뺴버리는 걸 동시에 할 수 있는지 해보자.
39 |
40 |
41 | # my solution 2
42 | class Solution:
43 | def findJudge(self, N: int, trust: List[List[int]]) -> int:
44 | if N == 1 and len(trust) == 0:
45 | return 1
46 |
47 | trusted = defaultdict(int)
48 | for pair in trust:
49 | trusted[pair[1]] += 1
50 |
51 | for pair in trust:
52 | if pair[0] in trusted:
53 | del trusted[pair[0]]
54 |
55 | for num, freq in trusted.items():
56 | if freq == N - 1:
57 | return num
58 |
59 | return -1
60 | # Status: Accepted
61 | # Algorithm:
62 | # Time Complexity: O(n^2)
63 | # Runtime: 736ms (top 0.04%)
64 | # 오예!
65 |
--------------------------------------------------------------------------------
/02_leetcode/1008 construct binary search tree from preorder traversal.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # leetcode discussion solution
9 | class Solution:
10 | def bstFromPreorder(self, preorder: List[int]) -> TreeNode:
11 | if preorder is None: return None
12 | root = TreeNode(preorder[0])
13 | stack = [root]
14 | for num in preorder[1:]:
15 | if num < stack[-1].val:
16 | stack[-1].left = TreeNode(num)
17 | stack.append(stack[-1].left)
18 | else:
19 | while stack and stack[-1].val < num:
20 | last = stack.pop()
21 | last.right = TreeNode(num)
22 | stack.append(last.right)
23 |
24 | return root
25 | # Status: Accepted
26 | # Algorithm: stack
27 | # Time Complexity: O(n)
28 | # Runtime: 32ms (top 5.4%)
29 | # 문제 자체를 이해 못해서 끙끙대다가 보고 공부했다.
30 | # preorder traversal 같은 경우는 리스트로 주어지는 걸 보니 노드가 아니라 그냥 숫자값이 담긴 리스트로 보면 되겠다.
31 | # else 부분에서 헷갈렸는데 preorder이기 때문에 계속 pop 해주는게 가능하다. 흠 어렵당
--------------------------------------------------------------------------------
/02_leetcode/1022 sum of root to leaf binary numbers.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 | class Solution:
8 | def sumRootToLeaf(self, root: TreeNode) -> int:
9 | stack = [(root, 0)]
10 | tot_sum = 0
11 | while stack:
12 | node, curr_num = stack.pop()
13 | if node:
14 | curr_num = (curr_num << 1) | node.val
15 | if node.left is None and node.right is None:
16 | tot_sum += curr_num
17 | else:
18 | stack.append((node.left, curr_num))
19 | stack.append((node.right, curr_num))
20 |
21 | return tot_sum
22 | # 릿코드 솔루션을 이해한 뒤에 다시 짜본 코드. 이진수를 다뤄본 적이 없어 비트연산자 등을 처음 써봤다.
23 | # Status: Accepted
24 | # Algorithm: DFS(with stack)
25 | # Time Complexity: O(n)
26 | # Runtime: 60ms (top 70.3%)
27 |
--------------------------------------------------------------------------------
/02_leetcode/1161 maximum level sum of a binary tree.py:
--------------------------------------------------------------------------------
1 | # Definition for a binary tree node.
2 | # class TreeNode:
3 | # def __init__(self, val=0, left=None, right=None):
4 | # self.val = val
5 | # self.left = left
6 | # self.right = right
7 |
8 | # my solution
9 | class Solution:
10 | def maxLevelSum(self, root: TreeNode) -> int:
11 | from collections import deque
12 | from collections import defaultdict
13 |
14 | q = deque([(root, 1)])
15 | level_sum = defaultdict(int)
16 |
17 | while q:
18 | node, level = q.popleft()
19 | if node:
20 | level_sum[level] += node.val
21 | q.append((node.left, level + 1))
22 | q.append((node.right, level + 1))
23 |
24 | tot = - sys.maxsize
25 | for i in range(len(level_sum), 0, -1):
26 | if level_sum[i] >= tot:
27 | tot = level_sum[i]
28 | res = i
29 |
30 | return res
31 | # Status: Accepted
32 | # Algorithm: BFS
33 | # Time Complexity: O(n)
34 | # Runtime: 360ms (top 43.6%)
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/02_leetcode/1306 jump game3.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | class Solution:
3 | def canReach(self, arr: List[int], start: int) -> bool:
4 | from collections import defaultdict
5 | edges = defaultdict(list)
6 | l = len(arr)
7 | targets = []
8 | for i, value in enumerate(arr):
9 | if i + value < l:
10 | edges[i].append(i + value)
11 | if -l <= i - value < 0:
12 | edges[i].append(l + i - value)
13 | else:
14 | edges[i].append(i - value)
15 | if value == 0:
16 | targets.append(i)
17 |
18 | visited = [0] * l
19 |
20 | def bfs(idx, edges, visited):
21 | if visited[idx] == 0:
22 | visited[idx] = 1
23 | for node in edges[idx]:
24 | bfs(node, edges, visited)
25 |
26 | bfs(start, edges, visited)
27 | if len(targets) == 0:
28 | return False
29 |
30 | for t in targets:
31 | if visited[t] == 1:
32 | return True
33 |
34 | return False
35 |
36 | # Status: Wrong Answer
37 | # Algorithm: BFS
38 | # Time Complexity: O(N + E) (N: len of arr, E: num of edges)
39 | # Runtime:
40 | # Intuition: 방법은 맞는 것 같은데 한가지 케이스에서 에러가 난다. 흠 뭐가 문제일까?
41 | # 그리고 value 0 인 모든 인덱스에 도달할 수 있는지 여부인 줄 알았는데 단 한개라도 도달할 수 있으면 True 인거였다.
42 | # 그러면 이것보다 더 간단히 만들 수 있을 것 같다.
43 | # 또한 문제의 조건, 모든 요소가 양수인 것을 이용해서 visited 를 만들 필요 없이 방문했던 인덱스의 value는 음수로 만들어주면 간편하다.
44 |
45 |
46 | # leetcode solution
47 | class Solution:
48 | def canReach(self, arr: List[int], start: int) -> bool:
49 | if start < len(arr) and 0 <= arr[start]:
50 | if arr[start] == 0:
51 | return True
52 | self.canReach(arr, start - arr[start])
53 | self.canReach(arr, start + arr[start])
54 | arr[start] = -1
55 | return False
56 | # Status: Accepted
57 | # Algorithm: DFS
58 | # Time Complexity: O(N)
59 | # Runtime: 228ms (top 2.4%)
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/03_codeit/삼송전자 주식 최대이익.py:
--------------------------------------------------------------------------------
1 | # 리스트에 그 날의 주식가격이 기록되어있다. 어느날 사서 어느날 파는게 최대이익을 낼지 계산하는 문제
2 |
3 | # 브루트포스 O(n^2)
4 | def max_profit(stock_list):
5 | max_profit = stock_list[1] - stock_list[0]
6 | for i in range(len(stock_list)):
7 | for j in range(i+1, len(stock_list)):
8 | max_profit = max(max_profit, stock_list[j] - stock_list[i])
9 |
10 | return max_profit
11 |
12 |
13 | # divide and conquer로 가능할까?
14 | def cross_max_profit(stock_list):
15 | mid = len(stock_list) // 2
16 | left_value = min(stock_list[:mid])
17 | right_value = max(stock_list[mid:])
18 |
19 | return right_value - left_value
20 |
21 | def max_profit(stock_list):
22 | if len(stock_list) == 2:
23 | return stock_list[-1] - stock_list[0]
24 |
25 | mid = len(stock_list) // 2
26 | return max(
27 | cross_max_profit(stock_list),
28 | max_profit(stock_list[:mid]),
29 | max_profit(stock_list[mid:]))
30 | # 앞에서 한 방식으로 O(nlgn)을 만들어보려고 했는데 얘는 len =1 인 경우 제대로 작동하지 않는 문제라서 실패
31 |
32 |
33 | # 코드잇 해답
34 | def max_profit(stock_list):
35 | min_num_so_far = min(stock_list[0], stock_list[1])
36 | max_profit_so_far = stock_list[1] - stock_list[0]
37 |
38 | for i in range(2, len(stock_list)):
39 | max_profit_so_far = max(max_profit_so_far, stock_list[i] - min_num_so_far)
40 | min_num_so_far = min(min_num_so_far, stock_list[i])
41 |
42 | return max_profit_so_far
43 | # 와... 이게 이렇게 간단히 된다고? 진짜 대단하다 O(n)
44 |
45 |
46 | # 테스트
47 | print(max_profit([7, 1, 5, 3, 6, 4]))
48 | print(max_profit([7, 6, 4, 3, 1]))
49 | print(max_profit([11, 13, 9, 13, 20, 14, 19, 12, 19, 13]))
50 | print(max_profit([12, 4, 11, 18, 17, 19, 1, 19, 14, 13, 7, 15, 10, 1, 3, 6]))
--------------------------------------------------------------------------------
/03_codeit/중복되는 항목 찾기.py:
--------------------------------------------------------------------------------
1 | # n+1 길이의 리스트에 1부터 n까지 임의의 자연수가 들어있다. 즉 최소 한 개의 수는 두번이상 들어있다.
2 | # 이 때 그 값을 구하는데 만약 조건을 만족하는 수가 여러개라면 그 중에 한개만 return해도 오케이
3 | # ex [1, 4, 1, 2, 2]에서 1 혹은 2를 return
4 |
5 |
6 | # 브루트 포스 O(n^2_
7 | def find_same_number(some_list):
8 | for i in range(len(some_list)):
9 | for j in range(i+1, len(some_list)):
10 | if some_list[i] == some_list[j]:
11 | return some_list[i]
12 |
13 |
14 | # O(n)의 공간을 할당하면서 대신 시간복잡도를 O(n)으로 줄임
15 | def find_same_number(some_list):
16 | nums = []
17 | for num in some_list:
18 | if num in nums:
19 | return num
20 | nums.append(num)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/03_codeit/투자 귀재 규식이.py:
--------------------------------------------------------------------------------
1 | # 리스트를 파라미터로 받으며 리스트는 매일의 이익으로 구성되어 있다.
2 | # 수익 합이 가장 큰 구간을 찾아서 수익값을 리턴
3 |
4 | # 브루트포스 O(n^2)
5 | def sublist_max(profits):
6 | max_profit = profits[0]
7 | for i in range(len(profits)):
8 | for j in range(i, len(profits) + 1):
9 | max_profit = max(max_profit, sum(profits[i:j]))
10 |
11 | return max_profit
12 |
13 | # divide and conquer를 이용해서 시간복잡도를 O(nlgn)으로 줄여보자
14 | def combine(profits, start, end):
15 | point = (start + end) // 2 + 1
16 | max_profit = 0
17 |
18 | for i in range(start, point):
19 | for j in range(point, end):
20 | max_profit = max(max_profit, sum(profits[i:j+1]))
21 |
22 | return max_profit
23 |
24 | def sublist_max(profits, start, end):
25 | if end - start <= 0:
26 | return profits[start]
27 |
28 | mid = (start + end) // 2
29 | return max(combine(profits, start, end),
30 | sublist_max(profits, start, mid),
31 | sublist_max(profits, mid+1, end))
32 | # i랑 j 범위를 반으로 줄였지만 그래봤자 n/2 * n/2 이니 시간복잡도는 O(n^2)다...
33 | # 심지어 재귀를 lg(n)만큼 호출하니 n^2 * lg(n) 됨... 똥망
34 | # sublist_max는 제대로 구현했고 combine이 잘못됨
35 |
36 |
37 | # 제대로된 divide and conquer 구현
38 | def max_crossing_sum(profits, start, end):
39 | mid = (start + end) // 2
40 |
41 | left_max = profits[mid]
42 | for i in range(mid, start - 1, -1):
43 | left_max = max(left_max, sum(profits[i: mid + 1]))
44 |
45 | right_max = profits[mid + 1]
46 | for j in range(mid + 1, end + 1):
47 | right_max = max(right_max, sum(profits[mid + 1: j + 1]))
48 |
49 | return left_max + right_max
50 |
51 |
52 | def sublist_max(profits, start, end):
53 | if end - start <= 0:
54 | return profits[end]
55 |
56 | mid = (start + end) // 2
57 | return max(
58 | max_crossing_sum(profits, start, end),
59 | sublist_max(profits, start, mid),
60 | sublist_max(profits, mid + 1, end))
61 | # 시간복잡도 O(nlgn)
62 |
63 |
64 | # 세상에 O(n)으로도 줄일 수 있다 이게 어떻게 가능하지?
65 | def sublist_max(profits):
66 | prev = profits[0]
67 | output = profits[0]
68 | for i in range(1, len(profits)):
69 | output = max(output, prev)
70 | prev = max(prev + profits[i], profits[i])
71 |
72 | return output
73 | # 진짜.. 말도 안돼ㅜㅜ
74 |
75 | print(sublist_max([7, -3, 4, -8]))
76 | print(sublist_max([-2, -3, 4, -1, -2, 1, 5, -3, -1]))
--------------------------------------------------------------------------------
/04_프로그래머스/2018카카오_다트게임.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | import re
3 |
4 | def solution(dartResult):
5 | p = re.compile('(\d+)([SDT])([*#]*)')
6 | darts = p.findall(dartResult)
7 |
8 | ans = []
9 | for d in darts:
10 | (n, bonus, option) = d
11 | n = int(n)
12 |
13 | # bonus check
14 | if bonus == 'D':
15 | n = n ** 2
16 | elif bonus == 'T':
17 | n = n ** 3
18 |
19 | # option check
20 | if option == '*':
21 | n *= 2
22 | if ans:
23 | ans[-1] *= 2
24 | elif option == '#':
25 | n *= -1
26 |
27 | ans.append(n)
28 |
29 | return sum(ans)
30 | # Status: Accepted
31 | # Note: 카카오는 언제나 정규표현식을 요하는 문제를 한개씩 내는 듯 하다.
32 | # 정규표현식이 생소한건 아니지만 사용할 일이 별로 없어서 언제나 한번씩 구글링하면서 정독하게 된당
33 | # 내가 쓴건 grouping 과 findall method
34 | # 깔끔하게 잘 짠 것 같다.
35 |
36 |
37 | # 다른사람의 solution
38 | import re
39 |
40 | def solution(dartResult):
41 | bonus = {'S': 1, 'D': 2, 'T': 3}
42 | option = {'': 1, '*': 2, '#': -1}
43 | p = re.compile('(\d+)([SDT])([*#]?)')
44 | darts = p.findall(dartResult)
45 | ans = [None] * len(darts)
46 |
47 | for i in range(len(darts)):
48 | if darts[i][2] == '*' and i > 0:
49 | ans[i-1] *= 2
50 | ans[i] = int(darts[i][0]) ** bonus[darts[i][1]] * option[darts[i][2]]
51 |
52 | return sum(ans)
53 | # 같은 정규표현식인데 일단 [*#]* 보다 [*#]? 이 더 맞는 것 같다. (같은 답을 내놓긴하지만)
54 | # 그리고 bonus와 option을 dict로 만들어서 쓰니 더 간결하다
55 | # 더 줄이려고 하면 ans를 만들지 않고 dart에 업데이트를 할 수도 있는데 난 그것까진 오류날까 살떨려서 못하겠다이
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/04_프로그래머스/2018카카오_비밀지도.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(n, arr1, arr2):
3 | _arr1 = [bin(i)[2:] for i in arr1]
4 | _arr2 = [bin(i)[2:] for i in arr2]
5 | for i in range(n):
6 | _arr1[i] = '0' * (n - len(_arr1[i])) + _arr1[i]
7 | for i in range(n):
8 | _arr2[i] = '0' * (n - len(_arr2[i])) + _arr2[i]
9 |
10 | ans = [''] * n
11 | for i in range(n):
12 | for j in range(n):
13 | ans[i] += ' ' if _arr1[i][j] == '0' and _arr2[i][j] == '0' else '#'
14 |
15 | return ans
16 | # Status: Accepted
17 | # Note: 음 밑에 포문돌리면서 ans 만드는 부분은 좋은데 위에 _arr 만드는 과정을 한 연산으로 해결할 수는 없을까?
18 | # 다른 사람들 코드를 보니 내가 잘 모르는 방식이 많다. 비트연산자를 내가 제대로 공부한 적이 없어서 그런 듯 하다. 내일 따로 시간을 내서 꼼꼼히 공부하자
19 |
20 |
21 | # 다른 사람의 solution
22 | def solution(n, arr1, arr2):
23 | ans = []
24 | for i, j in zip(arr1, arr2):
25 | a12 = str(bin(i|j)[2:])
26 | a12 = a12.rjust(n, '0')
27 | a12 = a12.replace('1', '#')
28 | a12 = a12.replace('0', ' ')
29 | ans.append(a12)
30 |
31 | return ans
32 | # 내 코드와 이 코드 모두 O(n^2)인데 얘가 3배정도 빠르다. n 범위가 좁아서 정확한 평가는 아님
33 | # 몰랐던 메소드는 rjust. 내가 첫번째, 두번째 포문으로 돌렸던 작업을 rjust 하나로 해결할 수 있다.
34 | # rjust(width, fillchar) center과 ljust 도 있다
35 | # replace의 경우는 알고 있었는데 bin(i|j)를 몰라서 활용을 못했다. 평소에 잘 안쓰던 메소드기도 했고.
36 | # 이제 비트논리연산자를 보자.
37 | bin(3)
38 | >>> 0b11
39 | bin(9)
40 | >>> 0b1101
41 | bin(3|9)
42 | >>> 0b1011
43 | # 신기하당. 십진수로는 생각하지 않고 이진수로 바꿔서 각 자리를 계산해주네. 하나라도 1이면 1
44 | # 그럼 & and연산자는 둘 다 1이어야 1을 출력하는 식이구나.
45 | # 음 그리고 bin의 결과값은 str 형식이다. 그럼 위의 코드에서 str 은 없어도 될 것 같은데?
46 | # 테스트 해보니 맞다. 맨 첫줄에 str(bin())할 필요없이 bin()만 해도 결과는 같음
47 | # 비트연산자 공부를 했다! 씬난다. int('1101' ,2) 식으로 int를 이용하는 방법도 다음에 써보자.
48 |
49 | # 공부하고 나서 문제를 다시 보니, 정확히 비트논리연산자를 쓰게 하려는 문제임을 알겠다
--------------------------------------------------------------------------------
/04_프로그래머스/2019카카오_실패율.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(N, stages):
3 | stages = sorted(stages, reverse=True)
4 | failure_rate = {}
5 |
6 | for i in range(1, N + 1):
7 | numerator, denominator = 0, len(stages)
8 | while stages and stages[-1] == i:
9 | stages.pop()
10 | numerator += 1
11 | failure_rate[i] = numerator / denominator if denominator != 0 else 0
12 |
13 | return sorted(failure_rate.keys(), key=lambda x: failure_rate[x], reverse=True)
14 | # Status: Accepted
15 | # Note: time complexity는 O(n)이고 pop을 이용해서 깔끔하게 잘 짠 것 같다. 뿌듯해
16 | # 다른 사람 풀이를 보니 while이 돌 때마다 numerator += 1 을 하는대신 numerator = stages.count(i) 로 할 수 있다.
17 | # 내가 잘쓰지 않는 메소드라 쓸 생각을 못했는데 이것도 괜찮겠다. 다만 그 경우에는 pop을 안쓰니 연산시간이 증가할 듯? -> 오! 해보니 내 방식이 압도적으로 빠르다. 45ms vs 1385ms 다ㅎㅎ
18 |
19 |
--------------------------------------------------------------------------------
/04_프로그래머스/2019카카오_크레인인형뽑기게임.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(board, moves):
3 | stack = []
4 | ans = 0
5 | for m in moves:
6 | # 0이 아닌 지점 찾기
7 | row = 0
8 | while row < len(board) and board[row][m - 1] == 0:
9 | row += 1
10 |
11 | # row == len(board)면 해당 위치에는 집을 인형이 없다는 뜻이므로 pass
12 | if row != len(board):
13 | if stack:
14 | last = stack[-1]
15 | else:
16 | last = None
17 | stack.append(board[row][m - 1])
18 |
19 | # 같은 인형 두개가 연속한 경우 팡팡
20 | if last and stack[-1] == last:
21 | stack.pop()
22 | stack.pop()
23 | ans += 2
24 |
25 | # 집은 인형이 있던 자리는 0으로 변경
26 | board[row][m - 1] = 0
27 |
28 | return ans
29 | # Status: Accepted
30 | # Note: 노트를 달지 않으면 이해하기가 어려울 것 같다. 지금은 달아놔서 각 부분 코드가 뭐하는지 알 수 있지만..
31 | # 코드를 잘 짜게 되면 코멘트 없이도 한눈에 작업 방식/순서가 한 눈에 들어올까?
32 |
33 |
34 | # 다른 사람의 solution
35 | def solution(board, moves):
36 | stack = []
37 | ans = 0
38 |
39 | for i in moves:
40 | for j in range(len(board)):
41 | if board[j][i - 1] != 0:
42 | stack.append(board[j][i - 1])
43 | board[j][i - 1] = 0
44 |
45 | if len(stack) > 1:
46 | if stack[-1] == stack[-2]:
47 | stack.pop()
48 | stack.pop()
49 | ans += 2
50 |
51 | break
52 |
53 | return ans
54 | # Note: 아, 훨씬 잘읽힌다. 내 코드는 row를 찾는부분, 같은 인형 두개가 연속되어 있는지 확인하는 부분 등
55 | # 부분집합들로 구성되어 있는데 얘는 하나의 큰 덩어리 같은 느낌이다. 훨씬 읽기 쉽다.
56 | # 나는 if문, for문 은 중첩되지 않게 만드는게 제일 중요하다고 생각했는데 이 코드를 보면 전혀 그렇지 않다.
57 | # break이 있어서 더 오래걸리는 것도 아니고 무엇보다 읽기가 너무 쉽다.
58 | # 마지막 break의 위치를 찾는 데에 좀 헤맸는데 break은 점점 바깥으로 나가면서 가장 먼저 만나는 반복문을 break 시키는 건가?
59 | # if 문 안에 들어있는게 좀 어색하게 느껴졌다.
--------------------------------------------------------------------------------
/04_프로그래머스/2020카카오_키패드누르기.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(numbers, hand):
3 | left, right = '*', '#'
4 | point = {1: (1, 1), 2: (1, 2), 3: (1, 3),
5 | 4: (2, 1), 5: (2, 2), 6: (2, 3),
6 | 7: (3, 1), 8: (3, 2), 9: (3, 3),
7 | '*': (4, 1), 0: (4, 2), '#': (4, 3)}
8 |
9 | def dist(pt1, pt2):
10 | return abs(pt1[0] - pt2[0]) + abs(pt1[1] - pt2[1])
11 |
12 | ans = ''
13 | for n in numbers:
14 | if n in [1, 4, 7]:
15 | left = n
16 | ans += 'L'
17 | elif n in [3, 6, 9]:
18 | right = n
19 | ans += 'R'
20 | elif n in [2, 5, 8, 0]:
21 | left_dist, right_dist = dist(point[n], point[left]), dist(point[n], point[right])
22 | if left_dist < right_dist:
23 | left = n
24 | ans += 'L'
25 | elif right_dist < left_dist:
26 | right = n
27 | ans += 'R'
28 | else:
29 | if hand == 'right':
30 | right = n
31 | ans += 'R'
32 | else:
33 | left = n
34 | ans += 'L'
35 |
36 | return ans
37 | # Status: Accepted
38 | # Note: 좀 더 깔끔하게 짤 수는 없을까 고민했는데 다른 사람들 풀이를 봐도 비슷비슷하다.
39 | # 내 코드가 제일 잘 읽히는 것 같당ㅎ.ㅎ
--------------------------------------------------------------------------------
/04_프로그래머스/9월코드챌린지_두개뽑아서더하기.py:
--------------------------------------------------------------------------------
1 | def solution(numbers):
2 | ans = set()
3 | for i, n_1 in enumerate(numbers):
4 | for n_2 in numbers[i + 1:]:
5 | ans.add(n_1 + n_2)
6 |
7 | return sorted(list(ans))
8 |
--------------------------------------------------------------------------------
/04_프로그래머스/9월코드챌린지_삼각달팽이.py:
--------------------------------------------------------------------------------
1 | def solution(n):
2 | fin = sum(range(1, n + 1))
3 | mat = [[0] * i for i in range(1, n + 1)]
4 |
5 | m = 1
6 | chg = 0
7 | while True:
8 | i = 0 + chg * 2
9 | j = 0 + chg
10 | while i < n and mat[i][j] == 0:
11 | mat[i][j] = m
12 | i += 1
13 | m += 1
14 |
15 | k = n - 1 - chg
16 | g = 1 + chg
17 | while g < len(mat[k]) and mat[k][g] == 0:
18 | mat[k][g] = m
19 | g += 1
20 | m += 1
21 |
22 | x = n - 2 - chg
23 | y = -1 - chg
24 | while 0 < x and mat[x][y] == 0:
25 | mat[x][y] = m
26 | x -= 1
27 | m += 1
28 |
29 | chg += 1
30 | if fin < m:
31 | break
32 |
33 | ans = []
34 | for lst in mat:
35 | ans += lst
36 | return ans
37 | # Status: Accepted
38 | # Note: 이렇게 의미없는 변수가 많아질 때 이름 설정을 어떻게 해야할지 잘 모르겠다.
39 | # 다른 사람들 코드를 보니 i, j의 두 개로 해결할 수 있다.
40 | # mode 변수를 써서 # 0 : go down, 1 : go right, 2 : go up 이라고 코멘트를 달아놓으니 보는 사람이 이해하기가 쉽다.
41 | # 방법은 맞았지만 좀 더 깔끔하고 처음보는 사람이 이해하기 쉽도록 다시 짜보자.
42 |
43 |
44 | def solution(n):
45 | mat = [[0] * i for i in range(1, n + 1)]
46 | step, cnt, i, j = n, 1, -1, 0
47 | mode = 0 # 0: down, 1: left to right, 2: up
48 | while True:
49 | for _ in range(step):
50 | if mode %3 == 0:
51 | i += 1
52 | elif mode %3 == 1:
53 | j += 1
54 | else:
55 | i -= 1
56 | j -= 1
57 | mat[i][j] = cnt
58 | cnt += 1
59 | mode += 1
60 | step -= 1
61 | if step < 1:
62 | break
63 |
64 | ans = []
65 | for row in mat:
66 | ans += row
67 | return ans
68 | # Status: Accepted
69 | # Note: 마지막 숫자에 도달하면 break 하는 것보다 step 을 사용하는게 훨씬 더 간결하다.
70 |
--------------------------------------------------------------------------------
/04_프로그래머스/9월코드챌린지_풍선터트리기.py:
--------------------------------------------------------------------------------
1 | def solution(a):
2 | prefix_min, suffix_min, n = a[0], a[-1], len(a)
3 | mins = [[None, None] for _ in range(n)]
4 |
5 | for i in range(n - 1):
6 | prefix_min = min(prefix_min, a[i])
7 | suffix_min = min(suffix_min, a[n - 1 - i])
8 | mins[i + 1][0] = prefix_min
9 | mins[n - 2 - i][1] = suffix_min
10 |
11 | ans = 0
12 | for m, num in zip(mins, a):
13 | if None in m or num <= m[0] or num <= m[1]:
14 | ans += 1
15 |
16 | return ans
17 | # Status: Accepted
18 | # Note: 해설을 보고 푼 답안. 챌린지 때는 divide and conquer 로 시도했었다. 그 방법으로는 자꾸 예외가 생겼었음
19 | # 주어진 문제의 조건을 단순화하는 연습이 필요할 것 같다.
20 | # 나는 조금 읽기 힘들어도 prefix 와 suffix 계산을 하나의 포문에 같이 돌렸는데
21 | # 같은 코드가 반복되더라도 더 이해가 잘되게 따로따로 두번 쓰는게 더 좋은 코드일까?
22 | # 같은 O(n)이더라도 prefix, suffix min 을 계산하면서 동시에 체크해주는 방식이 더 빠르겠다. 흠 괜히 더 복잡해지려나?
23 |
24 | def solution(a):
25 | n = len(a)
26 | prefix_min, suffix_min = a[0], a[-1]
27 | mins = [[None, None] for _ in range(n)]
28 |
29 | for i in range(n):
30 | prefix_min = min(prefix_min, a[i])
31 | mins[i][0] = prefix_min
32 |
33 | for i in range(n-1, -1, -1):
34 | suffix_min = min(suffix_min, a[i])
35 | mins[i][1] = suffix_min
36 |
37 | ans = 0
38 | for i in range(n):
39 | if a[i] <= mins[i][0] or a[i] <= mins[i][1]:
40 | ans += 1
41 |
42 | return ans
43 | # Status: Accepted
44 | # Note: 이게 훨씬 읽기 쉽다. 그리고 왠지 모르겠는데 이게 좀 더 빠르다
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_2016년.py:
--------------------------------------------------------------------------------
1 | def solution(a, b):
2 | months = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
3 | weekdays = ['THU', 'FRI', 'SAT', 'SUN', 'MON', 'TUE', 'WED']
4 | days = sum(months[:a]) + b
5 | return weekdays[days%7]
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_x만큼간격이있는n개의숫자.py:
--------------------------------------------------------------------------------
1 | def solution(x, n):
2 | return [x + i * x for i in range(n)]
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_가운데글자가져오기.py:
--------------------------------------------------------------------------------
1 | def solution(s):
2 | mid = len(s) // 2
3 | if len(s) % 2 != 0:
4 | return s[mid]
5 | else:
6 | return s[mid-1:mid+1]
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_같은숫자는싫어.py:
--------------------------------------------------------------------------------
1 | # 너무 힘들어서 자신감 회복하려고 lev1만 골라푸는 중
2 | def solution(arr):
3 | ans = [arr[0]]
4 | for i in range(1, len(arr)):
5 | if arr[i] != arr[i - 1]:
6 | ans.append(arr[i])
7 |
8 | return ans
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_나누어떨어지는숫자배열.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(arr, divisor):
3 | ans = [i for i in arr if i % divisor == 0]
4 | if len(ans) == 0:
5 | return [-1]
6 | return sorted(ans)
7 |
8 |
9 | # or을 사용하면 한줄에 가능
10 | def solution(arr, divisor):
11 | return sorted([i for i in arr if i % divisor == 0]) or [-1]
12 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_두정수사이의합.py:
--------------------------------------------------------------------------------
1 | def solution(a, b):
2 | return sum(range(a, b + 1)) if a <= b else sum(range(b, a + 1))
3 | # 예에 한줄로 깔끔
4 |
5 | # 다른 사람들꺼 보니 이 코드도 좋아보인다.
6 | def solution(a, b):
7 | return sum(range(min(a, b), max(a, b)+1))
8 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_모의고사.py:
--------------------------------------------------------------------------------
1 | def solution(answers):
2 | pat_1 = [1, 2, 3, 4, 5]
3 | pat_2 = [2, 1, 2, 3, 2, 4, 2, 5]
4 | pat_3 = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5]
5 | scores = [0, 0, 0]
6 | ans = []
7 |
8 | for idx, a in enumerate(answers):
9 | if pat_1[idx % len(pat_1)] == a:
10 | scores[0] += 1
11 | if pat_2[idx % len(pat_2)] == a:
12 | scores[1] += 1
13 | if pat_3[idx % len(pat_3)] == a:
14 | scores[2] += 1
15 |
16 | for i, s in enumerate(scores):
17 | if s == max(scores):
18 | ans.append(i + 1)
19 |
20 | return ans
21 | # Status: Accepted
22 | # Note: 내용이 어려운건 아니었는데 도저히 깔끔하게가 안짜져서 다른 사람들 코드를 보고 다시 짰다.
23 | # pattern으로 비교하지 않고 세명의 답을 전부 짜놓으려 한 부분이 좀 지저분해 진 것 같다.(최대 10000개라서 공간복잡도도 너무 커진다)
24 |
25 |
26 | # 예전에 짜다가 떄려친 코드
27 | def solution(answers):
28 | n = len(answers)
29 | anss = [None for _ in range(3)]
30 | anss[0] = [i % 5 + 1 for i in range(n)]
31 | anss[1] = [2, 1, 2, 3, 2, 4, 2, 5] * (n // 8 + 1)
32 | anss[2] = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5] * (n // 10 + 1)
33 |
34 | anss[1] = anss[1][:n]
35 | anss[2] = anss[2][:n]
36 |
37 | scores = [0, 0, 0]
38 | for i in range(3):
39 | score = 0
40 | for j in answers:
41 | if answers[j] == anss[i][j]:
42 | score += 1
43 |
44 | max(scores)]
45 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_문자열내p와y의개수.py:
--------------------------------------------------------------------------------
1 | def solution(s):
2 | s = s.lower()
3 | p, y = 0, 0
4 | for char in s:
5 | if char == 'p':
6 | p += 1
7 | if char == 'y':
8 | y += 1
9 |
10 | return p == y
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_문자열내림차순으로배치하기.py:
--------------------------------------------------------------------------------
1 | def solution(s):
2 | return ''.join(sorted(s, reverse=True))
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_문자열내마음대로정렬하기.py:
--------------------------------------------------------------------------------
1 | def solution(strings, n):
2 | strings.sort(key=lambda x: (x[n], x))
3 | return strings
4 |
5 |
6 | # solution2
7 | def solution(strings, n):
8 | return sorted(strings, key=lambda x: (x[n], x))
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_문자열다루기기본.py:
--------------------------------------------------------------------------------
1 | # my solutioin
2 | import re
3 |
4 | def solution(s):
5 | if len(s) != 4 and len(s) != 6:
6 | return False
7 |
8 | m = re.search(r'\D', s)
9 | return m == None
10 | # Status: Accepted
11 | # Note: 정규표현식을 써봤다. 잘쓰는 사람을 보면 len까지 체크하던데 나도 좀 더 연마해야겠당
12 |
13 |
14 | # 다른 사람의 solution
15 | def solution(s):
16 | return s.isdigit() and len(s) in (4, 6)
17 | # isdigit()을 생각못했당 아주 깔끔한 코드
18 |
19 | # 다른 사람의 solution
20 | def solution(s):
21 | return bool(re.match("^(\d{4}|\d{6})$", s))
22 | # Note: 아 생각보다 어렵지 않네, digit이 4번반복되거나 6번반복되는 경우가 있는지 체크체크하는 방식
23 |
24 |
25 | # isdecimal isdigit isnumeric 은 모두 간단히 말해 숫자인지를 체크해주지만
26 | # isdecimal은 0-9 사이의 열가지만 인정하고
27 | # isdigit 은 3^2(위첨자 형식의 2) 로 써있어도 인정하고
28 | # isnumeric은 1/2(가 한글자로 되어있는 특수문자) 의 경우도 인정한다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_서울에서김서방찾기.py:
--------------------------------------------------------------------------------
1 | def solution(seoul):
2 | answer = ''
3 | for i, name in enumerate(seoul):
4 | if name == 'Kim':
5 | return f'김서방은 {i}에 있다'
6 | return answer
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_소수찾기.py:
--------------------------------------------------------------------------------
1 | '''def solution(n):
2 | ans = 1
3 | for i in range(3, n + 1):
4 | for j in range(2, i):
5 | if i % j == 0:
6 | break
7 | else:
8 | ans += 1
9 |
10 | return ans'''
11 | # 시간복잡도는 그렇다치고, else가 저 자리에 있는데 왜 에러가 안나는거지?
12 | # else 없이 ans += 1 을 하면 j에 관한 포문 결과에 상관없이 결국 모든 i들에 대해 작동하게 돼서 문제가 생긴다.
13 | # 흠, break과 else의 관계에 대해서 좀 찾아봐야겠다.
14 | # 이 게시글이 이해하기 쉽다. https://kongdols-room.tistory.com/42
15 | # if문이 아니라 while문과 for문에도 else를 사용할 수 있구나. 저 else는 for j 에 따라오는 절이며, for문이 종료되는 시점에 else 절의 내용을 실행한다.
16 | # for문에 포함된 절이므로 break으로 for문을 깨고 나오는 경우, else 절은 실행되지 않는다 -> 이걸 이용한거네!
17 | # 어쨌든 이 풀이는 시간초과가 난다. 더 줄일 방법을 생각해보자
18 |
19 |
20 | # 에라토스테네스의 체 이용
21 | import math
22 | def solution(n):
23 | sieve = [True] * (n + 1)
24 |
25 | m = int(math.sqrt(n))
26 | for i in range(2, m + 1):
27 | if sieve[i] is True:
28 | for j in range(i + i, n + 1, i):
29 | sieve[j] = False
30 |
31 | return sum(sieve[2:])
32 | # Status: Accepted
33 | # Note: 좋은 코드다. 구글링하니 에라토스테네스의 체를 이용한 코드가 굉장히 많더라.
34 | # 음 나였으면 우선 [True] 를 이용할 생각을 못했을 것 같다. if sieve[i] is True 한 줄로 간단히 다음 걸러낼 소수를 찾을 수 있다
35 | # j 범위 설정에 i 단위 증가도 좋은 방법이다.
36 | # 일단 sieve[0]은 버리고 sieve[1]은 1은 True지만 소수가 아니므로 버리고 [2]부터 쓰면된다.
37 | # import math가 없어도 n ** 0.5하면 똑같지만 저게 더 의미가 명확한 듯해서 sqrt를 사용했다.
38 | # 그리고 if sieve[i]:, if sieve[i] is True:, if sieve[i] == True: 세가지 전부 통과한다. 일단 False는 None처럼 여겨지는 듯하고 is와 ==는 아직도 헷갈린다.
39 | # 파이참의 가이드는 is True를 권한다.
40 |
41 |
42 | # 다른 사람의 풀이
43 | def solution(n):
44 | num = set(range(2,n+1))
45 |
46 | for i in range(2,int(n ** 0.5)+1):
47 | if i in num:
48 | num -= set(range(2*i,n+1,i))
49 | return len(num)
50 | # 같은 원리지만 이것도 참 간단하다. True/False 값이 없어도 if i in num 으로 체크하면 되네. set을 좀 더 자주 활용해보면 좋겠다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_수박수박수.py:
--------------------------------------------------------------------------------
1 | def solution(n):
2 | count = n // 2
3 | ans = ''
4 | for _ in range(count):
5 | ans += '수박'
6 | if n % 2 == 1:
7 | ans += '수'
8 |
9 | return ans
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_시저암호.py:
--------------------------------------------------------------------------------
1 | # my solution 1
2 | def solution(s, n):
3 | ans = ''
4 | for char in s:
5 | if char == ' ':
6 | ans += char
7 | else:
8 | asc = ord(char)
9 | if 64 < asc < 91:
10 | new_asc = asc + n
11 | if new_asc > 90:
12 | new_asc -= 26
13 | ans += chr(new_asc)
14 | if 96 < asc < 123:
15 | new_asc = asc + n
16 | if new_asc > 122:
17 | new_asc -= 26
18 | ans += chr(new_asc)
19 |
20 | return ans
21 | # Status: Accepted
22 | # Note: 아스키코드를 처음 사용해봤다. 지금 코드가 굉장히 덕지덕지 지저분한데 일단 처음 써보는거니까..
23 | # 물론 아스키코드가 아니더라도 그냥 참조 스트링을 만들어서 더 간단하게 할 수 있을듯 하다.
24 |
25 |
26 | # my solution 2
27 | def solution(s, n):
28 | upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
29 | lower = 'abcdefghijklmnopqrstuvwxyz'
30 | # lower = upper.lower() 로 할수도 있지만 읽기 쉽게
31 |
32 | ans = ''
33 | for char in s:
34 | if char == ' ':
35 | ans += char
36 | elif char in upper:
37 | ans += upper[(upper.index(char) + n) % 26]
38 | elif char in lower:
39 | ans += lower[(lower.index(char) + n) % 26]
40 |
41 | return ans
42 | # Status: Accepted
43 | # Note: 아스키코드 변환보다 좀 더 읽기 쉽지만 인덱스를 구한 뒤 n을 더해서 다시 인덱싱하는게 조금 별로다
44 |
45 |
46 | # my solution 3 (다른 사람의 풀이 참조)
47 | def solution(s, n):
48 | ans = ''
49 | for char in s:
50 | if char.isupper():
51 | ans += chr((ord(char) - ord('A') + n)%26 + ord('A'))
52 | elif char.islower():
53 | ans += chr((ord(char) - ord('a') + n) % 26 + ord('a'))
54 | else:
55 | ans += ' '
56 |
57 | return ans
58 | # Status: Accepted
59 | # Note: 아스키코드를 쓰지만 isupper/islower을 사용하고 범위설정도 더 간결하게 바꿨다.
60 | # 그리고 아스키코드 표를 참조하지 않아도 - ord('A') 를 쓰는 식으로 풀 수 있다.
61 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_약수의합.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(n):
3 | ans = set()
4 | for i in range(1, int(n ** 0.5) + 1):
5 | if n % i == 0:
6 | ans.add(i)
7 | ans.add(n // i)
8 |
9 | return sum(ans)
10 | # Status: Accepted
11 | # Note: 처음에 일부 tc에서 실패가 떴는데 제곱수일 경우에 문제가 생겼었다.
12 | # 예를 들어 9일 경우 1 + 9 + 3 = 13 이어야 하는데 3을 한번 더 더해서 16을 return 했다.
13 | # 그걸 막기위한 방법으로는 ans = 0 해두고 if i != n // i: 조건을 다는 방법이나 ans를 set()으로 설정해서 add 로 걸러내는 방법이 있겠다.
14 | # list comprehension으로 좀 더 깔끔하게 쓸 수 있긴 하지만 그럼 적어도 n/2까지는 탐색을 해야하기 때문에 n이 커질수록 제곱근까지만 체크하는 이 방법이 유리하다.
15 |
16 |
17 | # list comprehesion 사용하기
18 | def solution(n):
19 | return sum([i for i in range(1, n + 1) if n %i == 0])
20 |
21 | def solution(n):
22 | return n + sum([i for i in range(1, (n // 2) + 1) if n %i == 0])
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_예산.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(d, budget):
3 | ans = 0
4 | for i in sorted(d):
5 | if i <= budget:
6 | ans += 1
7 | budget -= i
8 | if budget < i or budget < 0:
9 | break
10 | return ans
11 | # Status: Accepted
12 | # Note: while 문으로 하는게 더 깔끔할까? 한번 더 짜보자
13 |
14 |
15 | def solution(d, budget):
16 | tot = 0
17 | for i, n in enumerate(sorted(d)):
18 | tot += n
19 | if tot > budget:
20 | return i
21 |
22 | return len(d)
23 | # Status: Accepted
24 | # Note: 더 깔끔하긴한데 위의 코드보다 오래 걸리네. 흠 둘다 O(n)이라서 큰 차이는 없을 듯 하다.
25 | # d와 budget의 범위가 크지않아서 정확한 비교는 어렵다.
26 |
27 |
28 | # 다른 사람의 solution
29 | def solution(d, budget):
30 | d, s = sorted(d), sum(d)
31 | while budget < s:
32 | s -= d.pop()
33 |
34 | return len(d)
35 | # 내 두번째 코드와 비슷하지만 더 나은 것 같다. (내꺼는 더해나가는 방식, 이건 총합에서 빼나가는 방식)
36 | # return이 한개로 통일되는게 깔끔하다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_이상한문자만들기.py:
--------------------------------------------------------------------------------
1 | def solution(s):
2 | words = s.split(' ')
3 | ans = []
4 | for w in words:
5 | new_w = ''
6 | for i, char in enumerate(w):
7 | if i % 2 == 0:
8 | new_w += char.upper()
9 | else:
10 | new_w += char.lower()
11 | ans.append(new_w)
12 |
13 | return ' '.join(ans)
14 |
15 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_자릿수더하기.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(n):
3 | return sum([int(i) for i in str(n)])
4 | # Status: Accepted
5 | # Note: 한줄로 간단하긴 하지만 int로 주어진걸 str로 바꿨다가 다시 int로 변환하는게 좀 별로다.
6 |
7 |
8 | # map 사용
9 | def solution(n):
10 | return sum(map(int, str(n)))
11 | # map은 어렵지도 않은데 언제나 생각을 못해낸다. 흠
12 | # map(f, iterable)은 이터러블의 각 요소가 함수 f에 의해 수행된 결과를 묶어서 return
13 |
14 |
15 | # 다른 사람의 solution
16 | def solution(n):
17 | if n < 10:
18 | return n
19 | return (n % 10) + solution(n // 10)
20 | # 이거지! 재귀 너무 멋지당 흑흑
21 | # 포문을 돌릴 수 있는 문제는 재귀를 쓸 수 있다. 상황에 맞게 더 깔끔하게 구현가능한 걸 쓰면 되는 것.
22 | # n의 범위는 최대 9자리까지이므로 재귀를 돌리기에 문제 없다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_자연수뒤집어배열로만들기.py:
--------------------------------------------------------------------------------
1 | def solution(n):
2 | length = len(str(n))
3 | ans = [n % pow(10, i) // pow(10, i-1) for i in range(1, length+1)]
4 | return ans
5 |
6 |
7 | def solution(n):
8 | ans = [int(i) for i in reversed(str(n))]
9 | return ans
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_정수내림차순으로배치하기.py:
--------------------------------------------------------------------------------
1 | def solution(n):
2 | return int(''.join(sorted([i for i in str(n)], reverse=True)))
3 | # Status: Accepted
4 | # Note: 틀린 부분은 없는데 비효율적인 부분은 있다.
5 | # str을 sorted하면 list로 나오고, 또 리스트 컴프리헨션은 list(str(n))으로 해도 된다.
6 | # 즉, 다음처럼 쓰는게 맞다.
7 |
8 | # 한번 개선시키면
9 | def solution(n):
10 | return int(''.join(sorted(list(str(n)), reverse=True)))
11 |
12 | # 한번 더 개선시켜서
13 | def solution(n):
14 | return int(''.join(sorted(str(n), reverse=True)))
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_정수제곱근판별.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(n):
3 | if n ** 0.5 - int(n ** 0.5) == 0:
4 | return int((n ** 0.5 + 1) ** 2)
5 | else:
6 | return -1
7 | # Status: Accepted
8 | # Note: 이프문을 if n ** 0.5 % 1 == 0: 으로 할 수도 있다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_제일작은수제거하기.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(arr):
3 | if len(arr) == 1:
4 | return [-1]
5 |
6 | arr.remove(min(arr))
7 | return arr
8 | # Status: Accepted
9 |
10 |
11 | def solution(arr):
12 | return [i for i in arr if i > min(arr)] or [-1]
13 | # 이게 더 깔끔한 코드라고 생각했는데 위 코드보다 시간이 훨씬 오래걸린다(심지어 타임아웃)
14 | # 위 코드는 min에서 O(n) remove에서 O(n)이므로 O(n)이다.
15 | # 이 코드도 O(n)인 것 같은데? 설마 리스트 컴프리헨션에서 min을 매번 계산하나? 그럼 O(n^2)이겠다.
16 | # 다만 이 코드는 arr안에 min 값의 원소가 여러개일 때 해결할 수 있다.
17 | # 물론 이 문제에서는 제한조건에 i != j 일때 arr[i] != arr[j] 라고 명시했기 때문에 위 코드도 상관없다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_직사각형별찍기.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | a, b = map(int, input().strip().split(' '))
3 | for _ in range(b):
4 | print('*'*a)
5 | # Status: Accepted
6 | # Note: ('*' * a + '\n') * b 는 마지막에 공백한줄이 추가된다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_짝수와홀수.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(num):
3 | return 'Even' if num % 2 == 0 else 'Odd'
4 | # Status: Accepted
5 |
6 | # 똑같은 원리지만 글자수를 더 줄이고 싶다면
7 | def solution(num):
8 | return 'Odd' if num %2 else 'Even'
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_체육복.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(n, lost, reserve):
3 | new_lost = []
4 | for i in lost:
5 | if i in reserve:
6 | reserve.remove(i)
7 | else:
8 | new_lost.append(i)
9 |
10 | lost_num = len(new_lost)
11 | for i in new_lost:
12 | if i - 1 in reserve:
13 | reserve.remove(i - 1)
14 | lost_num -= 1
15 | elif i + 1 in reserve:
16 | reserve.remove(i + 1)
17 | lost_num -= 1
18 |
19 | return n - lost_num
20 | # Status: Accepted
21 | # Note: 되게 수정을 많이 했다.
22 | # 처음에는 new_lost나 lost_num 사용없이 lost.remove(i)를 썼는데 지금 포문 돌리는 중인 리스트에 변형을 가하니까 문제가 생긴듯 하다.
23 | # 그리고 처음에는 한개의 for 문으로 합했었는데 그렇게 하니까 오류가 나는 tc들이 있었다.
24 | # lost와 reserve 둘 다에 들어있는 넘버들을 먼저 다 처리해줘야 제대로 돌아간다.
25 | # 흠 근데 lost.remove 를 썼을 때 new_lost나 lost_num 의 새로운 변수를 안만들어도 돼서 엄청 깔끔했는데.. 다른 사람들 풀이를 한번 보자.
26 |
27 |
28 | # 다른 사람의 solution
29 | def solution(n, lost, reserve):
30 | _lost = [l for l in lost if l not in reserve]
31 | _reserve = [r for r in reserve if r not in lost]
32 |
33 | for r in _reserve:
34 | if r - 1 in _lost:
35 | _lost.remove(r - 1)
36 | elif r + 1 in _lost:
37 | _lost.remove(r + 1)
38 |
39 | return n - len(_lost)
40 | # 너무 깔끔해서 기분이 좋다. 일단 배우고 싶은 부분은 _lost 와 _reserve 를 만드는 코드. 너무 깔쌈해서 기분이 좋다.
41 | # 두번째는 _lost.remove 를 쓸거니 포문을 _lost 가 아닌 _reserve로 돌리는 것.
42 | # 이거 O(n^2)가 보다 작게 만들 수 있나? remove가 깔끔하긴 하지만 효율적인 연산은 아닌데.
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_최대공약수와최소공배수.py:
--------------------------------------------------------------------------------
1 | def solution(n, m):
2 | ans = []
3 | for i in range(min(n, m) + 1, 0, -1):
4 | if n % i == 0 and m % i == 0:
5 | ans.append(i)
6 | break
7 |
8 | for i in range(max(n, m), n * m + 1):
9 | if i % n == 0 and i % m == 0:
10 | ans.append(i)
11 | break
12 |
13 | return ans
14 | # Status: Accepted
15 | # Note: 코드는 간단하고 예외가 없이 항상 답을 찾아내긴 하지만 너무 비효율적인 것 같다.
16 |
17 |
18 | # 구글링 해보니 가장 최대공약수(GCD)와 최소공배수(LCM)에 관한 가장 효율적인 알고리즘이 있다
19 | # 유클리드 호제법 - 증명은 생각보다 어렵지 않으니 까먹으면 한번씩 찾아볼 것
20 | # 반복문 혹은 재귀로 가능한데, 이 알고리즘의 경우 재귀를 쓰면 마법같아 보인다. 너무 간단해
21 | def GCD(n, m):
22 | min_num, max_num = min(n, m), max(n, m)
23 | if max_num % min_num == 0:
24 | return min_num
25 | else:
26 | return GCD(min_num, max_num % min_num)
27 |
28 | # 만약 n < m이라는 조건이 주어진다면 더 간단하다.
29 | def GDC(n, m):
30 | r = m % n
31 | if r == 0:
32 | return n
33 | else:
34 | return GCD(n, r)
35 |
36 | # 또한 최소공배수는 n * m / 최대공약수로 구할 수 있다.
37 | # 이 문제의 경우는 GCD와 LCM을 리스트에 담아서 return 해야하므로 재귀보다는 반복문이 적절할 것 같다.
38 | def solution(n, m):
39 | x, y = max(n, m), min(n, m)
40 | r = x % y
41 | while 0 < r:
42 | r, y = y % r, r
43 |
44 | gcd = y
45 | lcm = n * m // y #int 형식으로 나와야 하기 때문에
46 | return [gcd, lcm]
47 | # Status: Accepted
48 | # Note: 프로그래머스는 정확한 연산시간은 안알려주지만 맨 위의 코드는 tc 중 최대 48ms까지 나온 반면
49 | # 이 코드는 최대 값이 0.1ms 이다!
50 | # 위의 코드는 시간복잡도 O(n*m)이고, 유클리드 호제법을 이용한 이 풀이는 O(log(n+m))이다. 엄청난차이!
51 |
52 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_콜라츠추측.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(num):
3 | for i in range(500):
4 | if num == 1:
5 | return i
6 | if num % 2 == 0:
7 | num = num // 2
8 | else:
9 | num = (num * 3) + 1
10 |
11 | return -1
12 | # Status: Accepted
13 | # Note: 처음에는 while문을 생각했다가 for문이 더 깔끔할 것 같아 바꿈
14 | # 그리고 num == 1 조건을 앞에 따지느냐(i 반환) num 계산 한 뒤에 따지느냐(i + 1 반환) 가 아무 차이 없을거라고 생각했는데,
15 | # 지금보니까 이 방식이 num = 1로 주어진 경우까지 커버할 수 있어서 맞는 방식이다. 차이가 있었네
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_핸드폰번호가리기.py:
--------------------------------------------------------------------------------
1 | def solution(phone_number):
2 | return '*' * (len(phone_number) - 4) + phone_number[-4:]
--------------------------------------------------------------------------------
/04_프로그래머스/lev1_행렬의덧셈.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | import numpy as np
3 |
4 | def solution(arr1, arr2):
5 | _arr1 = np.asarray(arr1)
6 | _arr2 = np.asarray(arr2)
7 |
8 | return np.array(_arr1 + _arr2).tolist()
9 | # Status: Accepted
10 | # Note: 넘파이 없이도 해보자
11 |
12 |
13 | # my solution 2
14 | def solution(arr1, arr2):
15 | n, m = len(arr1), len(arr1[0])
16 |
17 | ans = []
18 | for _ in range(n):
19 | ans.append([0] * m)
20 |
21 | for i in range(n):
22 | for j in range(m):
23 | ans[i][j] = arr1[i][j] + arr2[i][j]
24 |
25 | return ans
26 | # Status: Accepted
27 | # 다른 사람의 풀이를 보니 ans를 만들지 않고 arr1에 더해주는게 더 낫겠다.
28 |
29 | def solution(arr1, arr2):
30 | n, m = len(arr1), len(arr1[0])
31 |
32 | for i in range(n):
33 | for j in range(m):
34 | arr1[i][j] += arr2[i][j]
35 |
36 | return arr1
37 | # Status: Accepted
38 |
39 |
40 | # 다른 사람의 solution
41 | def solution(arr1, arr2):
42 | ans = [[c + d for c, d in zip(a, b)] for a, b in zip(arr1, arr2)]
43 | return ans
44 | # 뜨아 이렇게 깔끔하게 되네.
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_124나라의숫자.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(n):
3 | table = {
4 | '0': '1',
5 | '1': '2',
6 | '2': '4'
7 | }
8 |
9 | temary = ''
10 | i, j = n // 3, n % 3
11 | while 0 < i:
12 | temary += str(j)
13 | i, j = i // 3, i % 3
14 | temary += str(j)
15 |
16 | ans = ''
17 | for s in temary[::-1]:
18 | ans += table[s]
19 |
20 | return int(ans)
21 | # Status: Wrong Answer
22 | # Intuition: 생각한 방법은 숫자가 3개밖에 못쓰이니 결국 3진수랑 다름이 없고 출력할 때만 0 -> 1, 1-> 2, 2 -> 4 로 바꾸면 될거라는 아이디어
23 | # 우선 3진수 구현을 성공적으로 한 건 칭찬해주고 싶당. bin() 이나 int( , ) 등 기존함수가 아니라 직접 구현해본건 처음인데 돌려보니 잘 돌아간다.
24 | # while문이 끝난 뒤에 j를 한번 더 더해야하는 군더더기는 맘에 안들지만 어쨌든 성공
25 | # 근데 애초에 이 방법은 틀린 방법이더라. 직접 구해보면 3, 6, 9 등 자릿수가 넘어가는 부분에서 3진수와 124나라의 숫자는 서로 달라진다.
26 | # 이 나라가 그지같다. 0은 표현안할거여? 0을 1이라고 표현해야하는데 1 -> 1 부터 시작하니 아구가 안맞는당. 다른 방법을 생각해야 할 것 같다.
27 |
28 |
29 | # 번외) 재귀함수로 10진수를 n진수로 변환하기
30 | def convert(number, n):
31 | i, j = divmod(number, n)
32 | if i == 0:
33 | return str(j)
34 | return convert(i, n) + str(j)
35 | # 재귀짱
36 |
37 |
38 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_H-index.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # my solution
3 | def solution(citations):
4 | citations = sorted(citations, reverse=True)
5 | for i in range(len(citations) - 1, -1, -1):
6 | if citations[i] >= i + 1:
7 | return i + 1
8 |
9 | return 0
10 | # Status: Accepted
11 | # Note: 인용 횟수가 전부 0인 경우를 생각해서 마지막에 return 0을 해줘야한다. 포문을 다 돌아도 이프문을 한번도 만족하지 못하는 경우
12 |
13 | # 다른사람의 풀이
14 | def solution(citations):
15 | citations.sort(reverse=True)
16 | return max(map(min, enumerate(citations, start=1)))
17 | # Note: 일단 간결하고 가독성도 나쁘진 않은데 시간복잡도가 너무 크다. 내 풀이랑 비교해서 약 6배정도.
18 | # 여기서 배울 점은 map의 활용방법. int, str도 사실 함수인데 맨날 데이터 타입 변환에만 사용해서 다른 함수를 쓰는걸 떠울리기가 어렵다.
19 | # 그리고 enumerate에 start 키워드는 처음 알았다. 언젠가 활용해보자
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_가장큰수.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | # my solution
4 | def solution(numbers):
5 | ans = ''.join(sorted(map(str, numbers), key=lambda x: x * 3, reverse=True))
6 | return str(int(ans))
7 | # Status: Accepted
8 | # Intuition: sort by str data type
9 | # Note: 처음에는 오름차순 정렬시킨 다음에 두자리수가 등장하는 인덱스, 세자리수가 등장하는 인덱스를 찾는 방식으로 구현하고 있었는데
10 | # 너무 심하게 복잡해져서 이 난이도가 이렇게 어려울리 없는데? 하다가 문득 str 타입인 상태로 정렬시키면 될 것 같은 예감에 테스트를 해봤다.
11 | # 그랬더니 내가 원하는대로 앞자리수부터 비교하여 정렬해주더라! 넘나 기쁨쓰
12 | # 즉 int 로 하면 10, 6, 2 순으로 정렬되고 str 으로 하면 6, 2, 10 이렇게 내가 원하는 식으로 나온다.
13 | # 하나 더 해결해야 하는 문제는 31과 3이 있을 때 31, 3 순으로 정렬이 되어버린다는 것.
14 | # join 했을 때 313보다 331이 더 크기 때문에 이런식으로 정렬이 이루어지면 곤란했다. 그 부분은 key 를 사용해서 해결했다. 숫자의 범위가 세자리 수이기 때문에 *3을 해줬다.
15 | # 마지막에 int 로 변경했다가 다시 str 해주는 건 다른 사람들의 질문글을 참고해서 알아낸건데 처음에는 저 부분없이 제출했더니 딱 한 개의 tc에서만 실패가 떴다.
16 | # 예외 케이스로 모든 숫자가 0일 때 0000이 아니라 0이 출력되어야 하기 때문에 저런 변환 과정이 한번 더 필요했던 것.
17 | # 예외 케이스를 못 떠올리긴 했지만 그래도 풀이 방법이나 *3 을 key로 사용하는 건 내가 해결했으니 왕 뿌듯하당.
18 | # 이게 가장 인기있는 답안이긴 했는데 두번째 답안도 좋아보여서 공부해두면 좋을 것 같다.
19 |
20 |
21 | # 다른사람의 풀이
22 | import functools
23 |
24 | def comparator(a, b):
25 | t1 = a+b
26 | t2 = b+a
27 | return (int(t1) > int(t2)) - (int(t1) < int(t2))
28 |
29 | def solution(numbers):
30 | n = [str(x) for x in numbers]
31 | n = sorted(n, key=functools.cmp_to_key(comparator), reverse=True)
32 | ans = str(int(''.join(n)))
33 | return ans
34 | # 음 다른 부분은 사실 내 풀이와 원리가 같고 key 부분이 관건인데 cmp_to_key가 뭐지?
35 | # 오잉 찾아보니 cmp는 파이썬 2.x 버전에서 람다 대신 사용했던 매개변수라고 한다.
36 | # 람다랑 사실상 같은 원리이고 함수를 따로 구현한뒤 key 파라미터로 넘겨주고 싶을 때 functools.cmp_to_key(함수명)을 쓸 수 있다.
37 | # 그럼 저 함수의 용도는 이해했고 이제 comparator를 보자.
38 | # comparator는 두개의 str을 인자로 받고 있다. 흠 두 개인게 람다와 좀 다른 부분인데,
39 | # 약간 greedy의 느낌인 것 같다. 지금 이 요소가 a 가 되고 비교해야 할 다른 요소가 b가 된다.
40 | # 두 요소를 두가지 순서로 더해본 뒤, 지금 요소가 앞에 있는게 더 크면(즉, t1이 더 크면) 1이 return 되고 아니면 -1이 return 된다.
41 | # 흠 이게 key가 음수면 두 요소의 자리가 바뀌고 양수면 유지되는거 같은데? 그리고 이 key 값은 두 요소를 비교할 때만 사용하고
42 | # 다른 요소와 비교하는 단계로 넘어갈 때는 폐기되고 다시 계산되어지는 것 같다.
43 | # 찾아봐도 cmp_to_key 에 대한 정보가 별로 없당.
44 |
45 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_소수찾기.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # my solution
3 | import itertools
4 |
5 | def solution(numbers):
6 | possible_nums = []
7 | for i in range(1, len(numbers) + 1):
8 | possible_nums += map(''.join, itertools.permutations(numbers, i))
9 |
10 | possible_nums = set(map(int, possible_nums))
11 | target_number = max(possible_nums)
12 |
13 | sieve = [True] * (target_number + 1)
14 | sieve[0], sieve[1] = False, False
15 | for i in range(2, int(target_number ** 0.5) + 1):
16 | for j in range(2*i, target_number + 1, i):
17 | sieve[j] = False
18 |
19 | answer = 0
20 | for candidate in possible_nums:
21 | if sieve[candidate] is True:
22 | print(candidate)
23 | answer += 1
24 |
25 | return answer
26 | # Status: Accepted
27 | # Note: 크게 두 부분으로 나뉘어지는데 가능한 모든 경우의 수 pool을 만드는 것과 그 pool의 요소들이 소수인지 아닌지 판별하는 것.
28 | # 뒷부분은 에라토스테네스의 체를 사용했고 앞부분은 permutation을 다 합한뒤 int형으로 변환, set으로 중복을 걸렀다.
29 | # permutation의 결과물이 붙어나오지 않아서 permutation 을 다시 순회하면서 join으로 붙여야하나 고민했는데 map에 ''.join 도 가능하더라. 왕 편리함!
30 |
31 |
32 | # 다른 사람의 풀이
33 | def solution(numbers):
34 | a = set()
35 | for i in range(len(numbers)):
36 | a |= set(map(int, map("".join, itertools.permutations(numbers, i + 1))))
37 |
38 | a -= set(range(0, 2))
39 | for i in range(2, int(max(a) ** 0.5) + 1):
40 | a -= set(range(2 * i, max(a) + 1, i))
41 |
42 | return len(a)
43 | # Note: 사실상 원리는 같다. 근데 더 간결하고 축약시킨 것.
44 | # 주의해서 볼 점은 |= 부분. set끼리는 += 가 안되는데 |= 와 -=는 가능하다. 그리고 set()안에 바로 range를 쓴 것도 기억해두면 유용하게 쓸 것 같다.
45 | # 특히나 마지막 줄은 리스트 컴프리헨션처럼 j for j in range()로 가능하려나 생각했는데 그냥 저렇게 간단히 쓸 수 있다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_영어끝말잇기.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # my solution
3 | def solution(n, words):
4 | idx = None
5 | for i in range(1, len(words)):
6 | if words[i - 1][-1] != words[i][0] or words[i] in words[:i]:
7 | idx = i + 1
8 | break
9 | else:
10 | return [0, 0]
11 |
12 | ans_1 = idx % n if idx % n != 0 else n
13 | ans_2 = idx // n if idx % n == 0 else idx // n + 1
14 | return [ans_1, ans_2]
15 | # Status: Accepted
16 | # Note: 나는 순서를 생각해서 idx를 i+1로 설정해준 뒤, 조금 복잡하게 계산했는데(그래서 idx 변수도 따로 만들어야 했음) 오히려 i를 그대로 쓰면 밑과 같이 더 간단히 쓸 수 있다.
17 |
18 |
19 | def solution(n, words):
20 | for i in range(1, len(words)):
21 | if words[i-1][-1] != words[i][0] or words[i] in words[:i]:
22 | return [i%n + 1, i//n + 1]
23 | else:
24 | return [0, 0]
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_큰수만들기.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # my solution
3 | def solution(number, k):
4 | i, count = 0, 0
5 | while True:
6 | if int(number[i]) < int(number[i+1]):
7 | number = number[:i] + number[i+1:]
8 | count += 1
9 | i = 0
10 | else:
11 | i += 1
12 | if count == k:
13 | break
14 |
15 | return number
16 | # Status: Timeout
17 | # Note: 가장 큰 문제는 i를 0으로 돌려서 다시 처음부터 체크하게 하는 부분이다. number 가 최대 1000000자리이기 때문에 타임아웃 당하는 테스트케이스가 생긴다
18 | # 그렇다고 -= 1 로 하면 number = number[] + number[] 부분과 상충해서 문제가 생긴다. 다른 방법을 강구해봐야 할 듯하다.
19 |
20 |
21 | # my solution 2
22 | def solution(number, k):
23 | count, i = 0, 0
24 | while count < k and i + 1 < len(number):
25 | if number[i] < number[i + 1]:
26 | number = number[:i] + number[i + 1:]
27 | count += 1
28 | i = max(i - 1, 0)
29 | else:
30 | i += 1
31 |
32 | return number[:count - k] if count < k else number
33 | # Status: Accepted
34 | # Algorithm: Greedy
35 | # Time Complexity: O(n^2)
36 | # Note: 위의 코드와 사실상 같다. i -= 1 부분이 i가 음수로 넘어갈 경우 문제가 생기는 걸 확인하고 max(i - 1, 0)으로 해결해줬다.
37 | # 맨 마지막에 if count < k 부분을 추가 코드없이 return 안에 녹여서 썼다.
38 |
39 |
40 | # 다른 사람들 풀이에서 stack을 활용한 걸 봤다. 스택을 써서 한번 더 도전해보자
41 | def solution(number, k):
42 | stack, count = [number[0]], 0
43 | for i in range(1, len(number)):
44 | while stack and count < k and stack[-1] < number[i]:
45 | stack.pop()
46 | count += 1
47 | stack.append(number[i])
48 | if count == k:
49 | stack += number[i + 1:]
50 | break
51 |
52 | return ''.join(stack)[:len(number) - k]
53 | # Status: Accepted
54 | # Algorithm: Stack
55 | # Time Complexity: O(n)
56 | # Note: 세상에 그리디보다 훨씬 빠르다. while 문 조건이 좀 많아서 지저분하다고 느껴지는데 급하게 짠거라서 좀 더 다듬을 수 있을 것 같다.
57 | # 가장 오래걸리는 tc 기준으로 92ms vs 7000ms
58 |
59 |
60 | # 어디서 시간 복잡도 차이가 많이 나는 걸까? 스택이나 그리디나 둘 다 O(n)의 반복문을 돌리는데..
61 | # append나 pop은 O(1)이니 아마 number = number[:i] + number[i + 1:] 에서 시간을 많이 잡아먹는 것 같다.
62 | # 흠 새로운 메모리를 확보해서 다시 n개의 주소에 레퍼런스를 배정하는 방식이라고 생각하면 이 부분에서 O(n)의 시간 복잡도가 발생하므로 그리디 알고리즘을 이용한 풀이는 O(n^2)가 된다.
63 | # 시간복잡도를 보아하니 맞는 것 같다.
64 |
65 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_타겟넘버.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | class Search:
3 | def __init__(self):
4 | self.ans = 0
5 |
6 | def dfs(self, numbers, target):
7 | if sum(numbers) < abs(target):
8 | # 불가능한 경우의 수일 경우 굳이 끝까지 dfs를 돌지 않게 하기 위함
9 | # 이 부분이 없어도 코드는 돌아지만 인풋 양이 많아질수록 시간 절약의 효과가 있을 것 같다.
10 | return
11 | if len(numbers) == 1:
12 | if numbers[0] == abs(target):
13 | self.ans += 1
14 | return
15 | else:
16 | self.dfs(numbers[1:], target - numbers[0])
17 | self.dfs(numbers[1:], target + numbers[0])
18 |
19 | def solution(numbers, target):
20 | sol = Search()
21 | sol.dfs(numbers, target)
22 | return sol.ans
23 | # Status: Accpeted
24 | # Algorithm: DFS
25 | # Note: 흠 class말고 함수 두개로도 짤 수 있으면 좋을텐데
26 | # 코드 자체는 깔끔하게 잘 짠 것 같다.
27 | # 클래스를 쓴 이유는 ans 때문이었는데 global을 쓰면 클래스 없이도 짤 수 있다.
28 | # 그냥 바깥에 ans = 0 해두고 함수 내부에서 global ans 이렇게 선언해주면 된다.
29 |
30 |
31 | # 다른 사람의 풀이
32 | def solution(numbers, target):
33 | if not numbers and target == 0:
34 | return 1
35 | elif not numbers:
36 | return 0
37 | else:
38 | return solution(numbers[1:], target - numbers[0]) + solution(numbers[1:], target + numbers[0])
39 | # 원리는 내 풀이와 같다. 나는 ans 관리 때문에 클래스까지 쓰게 됐는 그냥 +로 구현해도 가능하다.
40 | # + 등의 사칙연산을 쓰면 대신 내 머리가 좀 더 복잡해진다.
41 |
42 |
43 | # 1123 한번 더
44 | def solution(numbers, target):
45 | if sum(numbers) < target:
46 | return 0
47 | if len(numbers) == 1:
48 | return 1 if numbers[0] == abs(target) else 0
49 | return solution(numbers[1:], target - numbers[0]) + solution(numbers[1:], target + numbers[0])
50 | # Status: Accepted
51 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_피보나치수.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # my solution
3 | def solution(n):
4 | current, prev = 1, 0
5 | for _ in range(n):
6 | current, prev = current + prev, current
7 |
8 | return prev % 1234567
9 | # Status: Accepted
10 | # Note: 왜 lev2에 있지? 응용이 아닌 너무 기본적인 피보나치 문제
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_기능개발.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 |
3 | def solution(progresses, speeds):
4 | complete = deque()
5 | for prog, speed in zip(progresses, speeds):
6 | i = 0
7 | while prog < 100:
8 | prog += speed
9 | i += 1
10 | complete.append(i)
11 |
12 | answer = []
13 | count = 0
14 | while complete:
15 | prev = complete.popleft()
16 | count += 1
17 | while complete and prev >= complete[0]:
18 | complete.popleft()
19 | count += 1
20 | answer.append(count)
21 | count = 0
22 |
23 | return answer
24 | # Status: Accepted
25 | # Algorithm: Queue
26 | # Time Complexity: O(n)
27 | # Note: 일단 complete를 만들 때 while문을 돌릴 필요없다. (군더더기 1)
28 | # 그리고 다 저장할 필요없이 위의 for문과 밑의 while문을 한번에 작업할 수 있다. (군더더기 2)
29 |
30 |
31 | # 다른 좋은 코드
32 | def solution(progresses, speeds):
33 | Q = []
34 | # list(list(int))의 형태. 안의 리스트는 [days_to_comp, count]
35 | for prog, speed in zip(progresses, speeds):
36 | days_to_left = -((prog - 100) // speed)
37 | # 나눗셈의 몫 + 1 을 하고 싶어서 p-100에 -를 취해줌
38 | if len(Q) == 0 or Q[-1][0] < days_to_left:
39 | # 아예 처음이거나, 가장 최근 것보다 더 오래걸리는 경우
40 | Q.append([days_to_left, 1])
41 | else:
42 | Q[-1][1] += 1
43 |
44 | return [q[1] for q in Q]
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_다리를지나는트럭.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | from collections import deque
3 |
4 | def solution(bridge_length, weight, truck_weights):
5 | truck_weights = deque(truck_weights)
6 | q, t = deque(), 1
7 | q.append((truck_weights.popleft(), t))
8 |
9 | while q:
10 | t += 1
11 | if t - q[0][1] == bridge_length:
12 | q.popleft()
13 |
14 | if truck_weights:
15 | if not q or sum([i[0] for i in q]) + truck_weights[0] <= weight:
16 | q.append((truck_weights.popleft(), t))
17 | return t
18 | # Status: Accepted
19 | # Note: 원리는 간단하다. 큐 안에 있는 트럭 무게의 총합과 새로들어올 트럭 무게의 합이 weight 보다 크지 않다면 큐에 추가
20 | # 추가할 때는 (트럭무게, 들어온 시간) 튜플형태로 받아서 현재 경과시간 - 들어온 시각 이 다리 길이가 되면 popleft()
21 | # 큐에 아무것도 안남으면 그 때의 경과시간 t를 return
22 | # 다만 좀 못마땅한 부분은 큐에 원소하나를 넣어주고 시작해야 한다는 군더더기(세번째 줄 코드)
23 | # 시간 효율성을 더 높이고 싶다면 sum을 한번만 계산하고 쁠마로 수정하면서 쓰면 된다. 지금은 시간초과가 안나서 그냥 sum()을 사용.
24 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_더맵게.py:
--------------------------------------------------------------------------------
1 | import heapq
2 |
3 | def solution(scoville, K):
4 | heapq.heapify(scoville)
5 | count = 0
6 | while len(scoville) >= 2:
7 | if scoville[0] >= K:
8 | break
9 |
10 | fir = heapq.heappop(scoville)
11 | sec = heapq.heappop(scoville)
12 | heapq.heappush(scoville, fir + sec * 2)
13 | count += 1
14 |
15 | if scoville[0] < K:
16 | return -1
17 | return count
18 | # Status: Accepted
19 | # Time Complexity: O(n lg(n)
20 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_멀쩡한사각형.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | import math
3 |
4 | def solution(w,h):
5 | m, n = max(w, h), min(w, h)
6 | s_per_line = math.ceil(m / n)
7 | return (w * h) - (s_per_line * n)
8 | # Status: Wrong Answer
9 | # Note: 20이내에서의 수를 입력하면 전부 정답이 나오는데 실패하는 tc가 꽤나 많다.
10 | # 포럼에 보면 같은 상황인 사람들이 몇몇 보이는데 [499, 27] 같은 경우에 답이 틀리게 나온다고 한다.
11 | # 격자를 직접 그려서 세볼 수도 없고.. 이론상으로 문제가 없는 것 같은데 어떤 부분에서 틀린건지 궁금하다.
12 |
13 |
14 | # solution(내가 푼거 아님)
15 | def solution(w, h):
16 | m, n = max(w, h), min(w, h)
17 | r = m % n
18 | while 0 < r:
19 | n, r = r, n % r
20 |
21 | gcd = n
22 | return w * h - (w + h - gcd)
23 | # Status: Accpeted
24 | # Intuition: 가로세로에 상관없이 선을 하나 넘을 때마다 못쓰는 사각형이 한 개 늘어난다는 점이 포인트
25 | # 또한 최대공약수와도 관계가 있다.
26 | # w, h를 최대 공약수로 나눈 w', h'(서로소)의 격자를 생각할 때 (간단하게 2 * 3 을 생각하면 된다) 대각선은 (w' - 1) + (h' - 1) 개의 선을 지나게 된다.
27 | # 따라서 버리는 사각형의 개수는 (w' - 1) + (h' - 1) + 1 = w' + h' - 1 이므로 다시 최대공약수를 곱해주면 전체 사각형에서 버리는 사각형의 개수는 w + h - gcd
28 | # 이걸 서로소로 만든 뒤 계산해야 하는 이유는 서로소가 아닐 경우(=공약수가 있을 경우) 공약수 지점에서 가로선과 세로선의 교차점을 지나가기 때문이다.
29 | # 선을 두 개 지나가는 셈인데도 늘어나는 사각형은 한개가 되니 최대공약수로 나누지 않고 연산하면 오류가 남
30 | # 최대 공약수의 경우 유클리드 호제법으로 구한다: lev1_최대공약수와최소공배수 참고
31 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_스킬트리.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(skill, skill_trees):
3 | ans = [None] * len(skill_trees)
4 |
5 | for idx, s in enumerate(skill_trees):
6 | series = list(skill) # series = skill 도 가능
7 | for char in s:
8 | # 비교할 스킬트리가 남아있지 않다면 작업 중단하고 True 값 지정
9 | if not series:
10 | ans[idx] = True
11 | break
12 |
13 | # 스킬트리의 첫번째에 해당할 경우 스킬트리의 맨 앞을 지우고 계속 진행
14 | if char == series[0]:
15 | series = series[1:]
16 |
17 | # 스킬트리의 첫번째가 아닌 요소와 일치한다면 작업 중단하고 False 값 지정
18 | if char in series[1:]:
19 | ans[idx] = False
20 | break
21 | else:
22 | ans[idx] = True
23 |
24 | return sum(ans)
25 | # Status: Accepted
26 | # Note: for-else 문을 썼다. 저번에 공부한걸 스스로 떠올려서 쓰다니 기특하당ㅎㅎ
27 | # 총 갯수만 return하면 되므로 ans를 [None] * len 대신 그냥 0으로 하면 더 간단해질 것 같다. ans[idx] = False를 생략할 수 있음
28 |
29 |
30 | # 다른사람의 solution
31 | def solution(skill, skill_trees):
32 | ans = 0
33 |
34 | for s in skill_trees:
35 | series = list(skill)
36 |
37 | for char in s:
38 | if char in series:
39 | if char != series.pop(0):
40 | break
41 | else:
42 | ans += 1
43 |
44 | return ans
45 | # Status: Accepted
46 | # Note: 원리는 같다. 근데 내 코드보다 더 나은 부분은,
47 | # 내 코드의 두번째 세번째 이프문을 하나로 통합한 것: if char in series 로 묶었다.
48 | # pop(0)을 사용한 것. 이러면 char == [0] 일 경우에 따로 추가 작업을 해주지 않아도 된다.
49 | # 똑똑하고 간결한 방법.
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_위장.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | from collections import defaultdict
3 |
4 | def solution(clothes):
5 | table = defaultdict(list)
6 | for cloth, name in clothes:
7 | table[name].append(cloth)
8 |
9 | n = len(table)
10 | # 옷 종류의 개수. 최소1개부터 최대n개까지 착용가능
11 |
12 | from itertools import combinations
13 | res = 0
14 | for i in range(1, n + 1):
15 | comb = combinations(table.keys(), i)
16 | for set in comb:
17 | temp = 1
18 | for j in range(i):
19 | temp *= len(table[set[j]])
20 | res += temp
21 |
22 | return res
23 | # 30개의 tc 중 1개가 타임아웃이 난다.
24 |
25 | def solution(clothes):
26 | table = defaultdict(list)
27 | for cloth, name in clothes:
28 | table[name].append(cloth)
29 |
30 | ans = 1
31 | for cloth in table.values():
32 | ans *= (len(cloth) + 1)
33 |
34 | return ans - 1
35 | # Intuition: 안입는 경우도 한가지 경우로 생각해서 value 개수 + 1 을 다 곱해준다.
36 | # 그리고 전부 다 안입은 경우을 제외하기 위해 1을 빼주면 완성
37 |
38 |
39 | # 다른 사람의 코드
40 | import collections
41 | from functools import reduce
42 |
43 | def solution(c):
44 | return reduce(lambda x,y:x*y,[a+1 for a in collections.Counter([x[1] for x in c]).values()])-1
45 | # 우와우 사실상 원리는 내것과 똑같음. 람다와 reduce를 좀 더 연습해야겠다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_전화번호목록.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(phone_book):
3 | if len(phone_book) == 1:
4 | return False
5 | for i, num1 in enumerate(phone_book):
6 | for num2 in phone_book[i+1:]:
7 | if num1 == num2 or num1 == num2[:len(num1)] or num2 == num1[:len(num2)]:
8 | return False
9 | return True
10 | # Status: Accepted
11 | # Algorithm: Brute Force
12 | # Time Complexity: O(n^2)
13 |
14 | # 다른 사람의 코드
15 | def solution(phone_book):
16 | phone_book = sorted(phone_book)
17 | for i, j in zip(phone_book, phone_book[1:]):
18 | if j.startswith(i):
19 | return False
20 | return True
21 | # 이게 더 직관적인데 시간이 훨씬 많이 소요된다 왜지?
22 | # sorted는 아마 O(n lg(n))일테니 startswith 함수가 오래걸리나보다.
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_주식가격.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(prices):
3 | n = len(prices)
4 | answer = [0] * n
5 | stack = [0]
6 |
7 | for i in range(1, n - 1):
8 | while stack and prices[stack[-1]] > prices[i]:
9 | idx = stack.pop()
10 | answer[idx] = i - idx
11 | stack.append(i)
12 |
13 | for idx in stack:
14 | answer[idx] = n - 1 - idx
15 |
16 | return answer
17 | # Status: Accepted
18 | # Algorithm: Stack
19 | # Time Complexity: O(n)
20 |
21 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_카펫.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | def solution(brown, yellow):
3 | # (w - 2) * (h - 2) = yellow
4 | # (w + h - 2) * 2 = brown
5 |
6 | width = 1 + 0.25 * brown + 0.5 * ((0.25 * (brown ** 2) - 2 * brown - 4 * yellow + 4) ** 0.5)
7 | height = 1 + 0.25 * brown - 0.5 * ((0.25 * (brown ** 2) - 2 * brown - 4 * yellow + 4) ** 0.5)
8 | return [int(width), int(height)]
9 | # Status: Accepted
10 | # Intuition: 간단한 연립방정식이므로 근의 공식을 써줬다.
11 | # 이 문제는 완전탐색 문제로 분류되어 있다. 이쪽으로도 생각해보자
12 |
13 | # 완전 탐색으로 푼 코드 (다른 사람의 코드 참고)
14 | def solution(brown, yellow):
15 | for i in range(1, brown):
16 | if brown % i == 0:
17 | if yellow // i + 2 == 0.5 * brown - i:
18 | return [yellow // i + 2, i + 2]
19 | # Status: Accepted
20 | # Note: 위의 두 방정식을 이용한 것으로 사실상 풀이는 같다. 위가 바로 답을 계산한다면 얘는 1부터 완전탐색으로 탐색해나감
21 | # 답 i는 언제나 두 정답 중 더 작은 수이므로 height로 들어간다. (i는 h - 2를 치환한 것(
22 | # 다만 좀 더 생각해봐야할 점은 range 범위인데, 일단 brown까지 잡는 것은 너무 낭비다 (코드에 문제가 없다면 사실 똑같겠지만 문제가 생길경우 너무 시간을 낭비한다.)
23 | # i = h - 2 라고 했을 때 i가 가장 클 수 있는 경우는 width == height 인 경우, 즉 루트 yellow 이다.
24 | # 그래서 범위를 range(1, int(yellow ** 0.5) + 1)로 해주면 가장 효율적이다.
25 |
26 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev2_하샤드수.py:
--------------------------------------------------------------------------------
1 | def solution(x):
2 | n = sum([int(i) for i in str(x)])
3 | return x % n == 0
4 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev3_섬연결하기.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # my solution
3 | import heapq
4 |
5 | def solution(n, costs):
6 | # 인접행렬
7 | adj_mat = [[None] * n for _ in range(n)]
8 | for c in costs:
9 | adj_mat[c[0]][c[1]] = c[2]
10 | adj_mat[c[1]][c[0]] = c[2]
11 |
12 | visited = [False] * n
13 | heap = [(0, costs[0][0])]
14 | answer = 0
15 | # 코스트가 낮은 순으로 우선순위 큐
16 | while heap:
17 | point = heapq.heappop(heap)
18 | if visited[point[1]] is False:
19 | answer += point[0]
20 | visited[point[1]] = True
21 | for i, cost in enumerate(adj_mat[point[1]]):
22 | if visited[i] is False and cost:
23 | heapq.heappush(heap, (cost, i))
24 |
25 | return answer
26 | # Status: Accepted
27 | # Intuition: 우선순위큐를 사용하되, 힙에 추가할 때가 아니라 뺄 때 visited를 체크하는게 포인트 - 추가할 때 visited를 True로 바꾸면 코스트가 더 낮은 항이 아예 힙에 추가되지 않는 경우가 발생함.
28 | # Note: 엄청 많이 헤맨 결과물이라, 코드가 지저분하다.
29 | # 시간을 너무 많이 써서 코드 다듬고 다른사람들 코드 확인하는 건 다음번에~
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/04_프로그래머스/lev3_베스트앨범.py:
--------------------------------------------------------------------------------
1 | # my solution
2 | from collections import defaultdict
3 |
4 | def solution(genres, plays):
5 | n = len(plays)
6 | genres_sum = defaultdict(list)
7 | for i, genre, play in zip(range(n), genres, plays):
8 | genres_sum[genre].append([play, i])
9 |
10 | genres_sum = sorted(genres_sum, key=lambda genre: sum(genres_sum[genre][:][0]), reverse=True)
11 | for plays in genres_sum.values():
12 | plays = sorted(plays, reverse=True)
13 |
14 | ans = []
15 | for plays in genre_sum.values():
16 | ans.append(plays[0][1])
17 | if len(plays) > 1:
18 | ans.append(plays[1][1])
19 |
20 | return ans
21 | # sorted에서 딕셔너리가 리스트로 전환되어버림. 한번 더 도전!
22 |
23 |
24 | def solution(genres, plays):
25 | n = len(plays)
26 | tot = defaultdict(list)
27 |
28 | for genre, play, i in zip(genres, plays, range(n)):
29 | tot[genre].append((plays, i))
30 |
31 | for pairs in tot.values():
32 | pairs.sort(key=lambda pair: (pair[0], pair[1]), reverse=True)
33 | # 딕셔너리 tot에 대해 gerne : [(play, i), (play, i)], gerne: [], gerne: [] ... 정렬
34 | # play가 높은 순, play가 같을 경우 i가 높은 순으로 정렬완료
35 |
36 | ord = defaultdict(int)
37 | for genre, play in zip(genres, plays):
38 | ord[genre] += play
39 |
40 | ge_or = sorted(ord.items(), key=lambda x: x[1], reverse=True)
41 |
42 | ans = []
43 | for genre, _ in ge_or:
44 | ans.append(tot[genre][0][1])
45 | if len(tot[genre]) > 1:
46 | ans.append(tot[genre][1][1])
47 |
48 | return ans
49 | # 에러는 안나지만 제대로 된 값이 안나온다. 어디가 문제일까~
50 | # defaultdict를 사용하는 경우 print가 어려워서 디버깅이 어렵다.
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/04_프로그래머스/lve2_프린터.py:
--------------------------------------------------------------------------------
1 | def solution(priorities, location):
2 | sorted_p = sorted(priorities, reverse=True)
3 |
4 | count = 0
5 | while priorities:
6 | first = priorities.pop(0)
7 | if first != sorted_p[0]:
8 | priorities.append(first)
9 | if location == 0:
10 | location = len(priorities)
11 |
12 | else:
13 | count += 1
14 | sorted_p.pop(0)
15 | if location == 0:
16 | return count
17 | location -= 1
18 | # Status: Accepted
19 | # Time Complexity: O(n)
20 | # Note: 데크를 이용해서 popleft를 사용하면 더 빨라질거다. 근데 리스트로 주어진 priorities를 데크로 변환하는 데 더 오래 걸리려나?
21 | # if location == 0 문을 두 번 쓰는게 좀 못마땅한데 어떻게 한번으로 줄일 수 있을지 잘 모르겠다.
22 | # 그 상위의 if에 따라 location이 0일 때 수행할 동작이 다른데 하나로 합칠 방법이 있을까?
23 |
24 |
25 | # 다른 사람의 풀이
26 | from collections import deque
27 |
28 | def solution(priorities, location):
29 | Q = deque()
30 | for i, p in enumerate(priorities):
31 | Q.append((i, p))
32 |
33 | count = 0
34 | while Q:
35 | cur = Q.popleft()
36 | if any(cur[1] < q[1] for q in Q):
37 | Q.append(cur)
38 | else:
39 | count += 1
40 | if cur[0] == location:
41 | return count
42 | # any를 이용한 깔끔한 방법 + 인덱스까지 같이 저장한 새로운 데크 생성
43 | # 다만 any를 이용하면 요소를 꺼낼 때마다 포문을 돌리므로 나는 sorted 를 하나 두고 비교하는게 더 낫다고 생각함!
--------------------------------------------------------------------------------
/05_백준/11725_트리의부모찾기.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | from collections import deque
3 | import sys
4 | sys.setrecursionlimit(10 ** 9)
5 |
6 | n = int(sys.stdin.readline())
7 | tree = [[] for _ in range(n+1)]
8 | for _ in range(n-1):
9 | a, b = map(int, sys.stdin.readline().split())
10 | tree[a].append(b)
11 | tree[b].append(a)
12 |
13 | visited = [False for _ in range(n+1)]
14 | parent_node = [None for _ in range(n+1)]
15 |
16 | q = deque([1])
17 | visited[1] = True
18 | while q:
19 | curr_node = q.popleft()
20 | for child in tree[curr_node]:
21 | if not visited[child]:
22 | q.append(child)
23 | visited[child] = True
24 | parent_node[child] = curr_node
25 |
26 | for ans in parent_node[2:]:
27 | print(ans)
28 |
29 | # Status: Accepted
30 | # Note: 이거 알고리즘은 BFS로 금방 짰는데 input 처리에서 엄청 애를 먹었다. - 그리고 아직도 의문이 많은 상태
31 | # 처음에는 map(int, input().split()) 으로 했더니 n은 제대로 처리하고 두번째 줄 넣고 엔터치는 순간에 syntax error: unexpected EOF 에러가 났다.
32 | # 도대체 뭐가 무엇인고 하면서 스택오버플로우를 훑다가 raw_input 으로 바꿔보라는 답변이 있어 수정해봤더니 제대로 돌아갔다!
33 | # 근데 여기서 의문은 raw_input 은 python2 에서 사용하던거고 3에 들어서는 없어졌다는데 도대체 왜..?
34 | # 그리고 나는 두번째 줄을 넣는 순간에 포문이 실행되어버려서(n-1번) 그 밑의 줄이 없으니 에러가 나는건가- 추측했는데 복붙으로 한번에 넣어도 에러가 난다. 그럼 이유가 무엇??
35 | # 두번째 의문은 raw_input 으로 파이참에서는 잘 돌아가는 것을 확인했는데 백준에 그대로 제출했더니 런타임 에러가 난다. 뭐가 다르길래..?
36 | # 결국 구글링을 통해서 sys.stdin.readline() 으로 해결했다. 파이썬 내부에서, 그리고 인터프리터 상에서 이 세가지가 어떻게 다르게 동작하는건지 찾아봐야겠다.
37 |
38 |
--------------------------------------------------------------------------------
/05_백준/2225_합분해.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | # my solution
4 | N, K = map(int, input().split())
5 |
6 | def solution(N, K):
7 | if K == 0:
8 | return 0
9 | if K == 1:
10 | return 1
11 | if K == 2:
12 | return N + 1
13 | else:
14 | answer = 0
15 | for n in range(N + 1):
16 | answer += solution(N - n, max(K - 1, 0))
17 |
18 | return answer % 1000000000
19 |
20 | print(solution(20, 2))
21 | print(solution(1, 2))
22 | print(solution(1, 3))
23 | print(solution(0, 3))
24 | print(solution(0, 4))
25 | print(solution(2, 1))
26 | print(solution(2, 2))
27 | print(solution(2, 3))
28 | print(solution(2, 4))
29 | print(solution(2, 5))
30 | print(solution(5, 2))
31 | print(solution(5, 3))
32 | #print(solution(87, 65))
33 | # Status: Timeout
34 | # Algorithm: Recursion
35 | # Note: 백준은 타임아읏을 따로 알려주는지 '틀렸습니다'로 퉁치는지 잘 모르겠넴.
36 | # 위의 tc를 전부 통과하는 걸 보면 코드가 틀린건 아닌거 같은데 87 65 에서 시간이 너무 오래걸린다. 아마 타임아웃인 것 같다.
37 | # 아 백준도 타임아웃이면 타임아웃이라고 뜬다고 한다. 웬만한 반례는 체크했다고 생각했는데 어디서 틀린걸까
38 | # def solution만 하고 펑션콜을 하지않아서 틀린거였다=.= 아예 아무것도 return 하지 않는 코드를 몇 번이나 제출한 것.
39 | # 프로그래머스는 제출을 하면 자체적으로 펑션 콜을 진행해서 평가하기 때문에 습관적으로 이렇게 제출했다.
40 | # 한 플랫폼만 쓰다보니 생긴 문제.... 이 코드는 틀리지는 않았고 시간초과가 뜬다.
41 | # 이제 시간제한을 만족해야 하니 dp로 다시 풀어보자. overlapping subproblem 들을 합쳐합쳐
42 |
43 | # my solution 2
44 | def solution_2(N, K):
45 | if N == 0:
46 | return 1
47 | if K == 0:
48 | return 0
49 |
50 | table = [[None] * (K + 1) for _ in range(N + 1)]
51 | for k in range(K + 1):
52 | table[0][k] = 1
53 | table[1][k] = k
54 | for n in range(N + 1):
55 | table[n][0] = 0
56 | table[n][1] = 1
57 |
58 | for n in range(2, N + 1):
59 | for k in range(2, K + 1):
60 | table[n][k] = table[n - 1][k] + table[n][k - 1]
61 |
62 | return table[N][K] % 1000000000
63 | # Status: Accepted
64 | # Algorithm: Dynamic Programming (tabulation)
65 | # Note: table 크기를 200으로 하면 위의 예외처리 N == 0, K == 0 을 안해줘도 되지만 공간낭비가 심할 것 같아 이렇게 했다.
66 | # 이건 딴 얘긴데 백준은 return이 아니라 출력(print)이네...ㅎㅎㅎ 아까운 내 시간...ㅠㅠ
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/1000 A+B.py:
--------------------------------------------------------------------------------
1 | v1, v2 = input().split()
2 | v1 = int(v1)
3 | v2 = int(v2)
4 | print(v1 + v2)
--------------------------------------------------------------------------------
/99_etc/baekjoon/10171 고양이.py:
--------------------------------------------------------------------------------
1 | print('''\ /\\
2 | ) ( ')
3 | ( / )
4 | \(__)|
5 | ''')
--------------------------------------------------------------------------------
/99_etc/baekjoon/10818 최소,최대.py:
--------------------------------------------------------------------------------
1 | num_of_input = int(input())
2 | num_lst = list(map(int, input().split()))
3 |
4 | biggest_value = num_lst[0]
5 | smallest_value = num_lst[0]
6 |
7 | for i in num_lst:
8 | if biggest_value < i:
9 | biggest_value = i
10 | if smallest_value > i:
11 | smallest_value = i
12 |
13 | print(smallest_value, biggest_value)
14 |
15 | #간단히 이렇게도 가능
16 | print(min(num_lst), max(num_lst))
--------------------------------------------------------------------------------
/99_etc/baekjoon/10869 사칙연산.py:
--------------------------------------------------------------------------------
1 | a, b = map(int, input().split())
2 | print(a + b)
3 | print(a - b)
4 | print(a * b)
5 | print(int(a / b))
6 | print(a % b)
7 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/10871 X보다 작은 수.py:
--------------------------------------------------------------------------------
1 | N, X = map(int, input().split())
2 | A = list(input().split())
3 |
4 | smaller_lst = []
5 | for i in A:
6 | if int(i) < X:
7 | smaller_lst.append(i)
8 |
9 | print(' '.join(smaller_lst))
10 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/1110 더하기 사이클.py:
--------------------------------------------------------------------------------
1 | '''
2 | orig_num = input()
3 |
4 | if len(orig_num) == 1:
5 | orig_num = '0' + orig_num
6 |
7 | calculated_num = 0
8 | num = orig_num
9 | result = 0
10 | cycle = 0
11 | while result != orig_num:
12 | calculated_num = int(num[0]) + int(num[-1])
13 | num = num[-1] + str(calculated_num)[-1]
14 | result = num
15 | if len(result) == 1:
16 | result = '0' + result
17 | cycle += 1
18 |
19 | print(cycle)
20 | '''
21 |
22 | #while True 와 str 사용없이 해보기
23 |
24 | orig_num = int(input())
25 |
26 | cycle = 0
27 | num = orig_num
28 | while True:
29 | a = int(num/10)
30 | b = num%10
31 | c = a + b
32 | num = b*10 + c%10
33 | cycle += 1
34 | if num == orig_num:
35 | print(cycle)
36 | break
37 |
38 |
39 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/14681 quadrant.py:
--------------------------------------------------------------------------------
1 | x = input()
2 | y = input()
3 | x = int(x)
4 | y = int(y)
5 |
6 | if x > 0:
7 | if y > 0:
8 | print(1)
9 | else:
10 | print(4)
11 | else:
12 | if y < 0:
13 | print(3)
14 | else:
15 | print(2)
16 |
17 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/2438 별찍기 -1.py:
--------------------------------------------------------------------------------
1 | a = int(input())
2 | for i in range(a):
3 | print('*' * (i+1))
4 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/2557 Hello World.py:
--------------------------------------------------------------------------------
1 | print('Hello World!')
2 |
3 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/2753 윤년.py:
--------------------------------------------------------------------------------
1 | year = int(input())
2 | if year % 400 == 0:
3 | print(1)
4 | elif year % 4 == 0 and year % 100 != 0:
5 | print(1)
6 | else:
7 | print(0)
8 |
--------------------------------------------------------------------------------
/99_etc/baekjoon/2884 알람시계.py:
--------------------------------------------------------------------------------
1 | hour, minute = input().split()
2 | hour = int(hour)
3 | minute = int(minute)
4 |
5 | minute -= 45
6 |
7 | if minute <0:
8 | minute += 60
9 | hour -= 1
10 |
11 | if hour <0:
12 | hour += 24
13 |
14 | print(hour, minute)
--------------------------------------------------------------------------------
/99_etc/baekjoon/6987 WorldCup.py:
--------------------------------------------------------------------------------
1 | a = list(map(int, input().split()))
2 | b = list(map(int, input().split()))
3 | c = list(map(int, input().split()))
4 | d = list(map(int, input().split()))
5 |
6 | whole_score = [a, b, c, d]
7 | whole_result = []
8 |
9 | for score in whole_score:
10 | sum_of_victory = 0
11 | sum_of_defeat = 0
12 | sum_of_tie = 0
13 | count_of_tie_team = 0
14 | sum_of_each_team_score = 0
15 |
16 | for i in range(6):
17 | sum_of_victory += score[3*i]
18 | sum_of_defeat += score[3*i + 2]
19 | sum_of_tie += score[3*i + 1]
20 | if score[3*i + 1] != 0:
21 | count_of_tie_team += 1
22 |
23 | if sum_of_defeat == sum_of_victory and sum_of_tie % 2 == 0 and sum_of_victory + (sum_of_tie/2) == 15 and count_of_tie_team!= 1:
24 | whole_result.append(1)
25 | else:
26 | whole_result.append(0)
27 |
28 | print(' '.join(map(str, whole_result)))\
29 |
--------------------------------------------------------------------------------
/99_etc/etc/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/99_etc/etc/.idea/etc.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/99_etc/etc/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/99_etc/etc/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/99_etc/etc/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/99_etc/etc/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/99_etc/etc/numpy practice.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | X = np.array([[51, 55],[14, 19], [0, 4]])
4 | print(X)
5 | print(X[0])
6 | print(X[0][1])
7 |
8 | X = X.flatten()
9 | print(X)
10 |
11 | print(X[np.array([0, 2, 4])])
12 | print(X[0], X[2], X[4])
13 | print(np.array([X[0], X[2], X[4]]))
14 | print(X[[0, 2, 4]])
15 |
16 |
17 | print(X > 15)
18 | print(X[X>15])
19 |
20 | list = [51, 55, 14, 19, 0, 4]
21 | print(list > 15)
22 | # 리스트는 이렇게 활용할 수 없음! 넘파이 어레이만 가능 ~.~
--------------------------------------------------------------------------------
/99_etc/hackerrank/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/.idea/hackerrank.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/find a string.py:
--------------------------------------------------------------------------------
1 | '''
2 | 두번째줄 인풋이 첫번째줄 인풋에 몇번 등장하는지 카운트
3 | '''
4 |
5 |
6 | def count_substring(string, sub_string):
7 | count = 0
8 | for i in range(len(string) - len(sub_string)+1):
9 | if string[i:i + len(sub_string)] == sub_string[:]:
10 | count += 1
11 |
12 | return count
13 |
14 |
15 | if __name__ == '__main__':
16 | string = input().strip()
17 | sub_string = input().strip()
18 |
19 | count = count_substring(string, sub_string)
20 | print(count)
21 |
22 | '''
23 | list comprehension 으로 짜보기
24 | print(sum([1 for i in range(len(string) - len(sub_string) +1) if string[i:i+len(sub_string)] == sub_string[:]]))
25 | '''
--------------------------------------------------------------------------------
/99_etc/hackerrank/find the runner-up score.py:
--------------------------------------------------------------------------------
1 | n = int(input())
2 | a = list(map(int, input().split()))
3 |
4 | '''
5 | map(f, iterable)은 iterable의 모든 component를 f 수행해서 돌려준다.
6 | 내가 쓴건 3 5 5 7 을 input으로 받았을 때 스페이스로 구분해서 리스트로 만든 뒤(split) -
7 | 각 컴포넌트에 대해 int로 바꿔주고(int) - 그걸 다시 리스트로 돌려준다(list)
8 | for 문 돌렸을 작업을 한 줄에 표현할 수 있다.
9 | '''
10 |
11 | '''
12 | b = set(a)
13 | a_new = list(b)
14 | a_new.reverse()
15 |
16 | print(a_new[1])
17 | print(a_new)
18 | 돌아가긴 하는데 음수가 들어있으면 제대로 정렬을 못한다. 1 2 -10 7 5 -8 1 이면
19 | [-8, -10, 7, 5, 2, 1] 이 나온다. 다른 방법을 찾아보자
20 | '''
21 | b = set(a)
22 | a_new = list(b)
23 | a_new.sort(reverse=True)
24 |
25 | print(a_new[1])
26 |
27 | #아, sort에 reverse = True 로 했더니 된다! 나이스
28 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/finding the percentage.py:
--------------------------------------------------------------------------------
1 | dict = {}
2 | for _ in range(int(input())):
3 | text = input().split()
4 | name = text[0]
5 | score = [float(a) for a in text[1:]]
6 | dict[name] = score
7 |
8 | quora = input()
9 | print(dict)
10 | print(quora)
11 |
12 | for name in dict.keys():
13 | if name == quora:
14 | result = sum(dict[name]) / len(dict[name])
15 |
16 | print('%.2f'% result)
17 |
18 | # 히히 잘짜서 기분좋다
19 |
20 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/list comprehensions.py:
--------------------------------------------------------------------------------
1 | x = int(input())
2 | y = int(input())
3 | z = int(input())
4 | n = int(input())
5 |
6 | result = [[a, b, c] for a in range(x+1) for b in range(y+1) for c in range(z+1) if a + b + c != n]
7 | print(result)
8 |
9 | '''result = [[a, b, c] for a, b, c in range(x+1), range(y+1), range(z+1) if a + b + c != n]
10 | print(result)
11 | '''
12 | # key, value 처럼 쌍으로 가져오는게 아니라 a 마다 모든 b 반복 식으로 진행되야 하기 때문에 for 문을 각각 써줘야 한다.
--------------------------------------------------------------------------------
/99_etc/hackerrank/merge the tools.py:
--------------------------------------------------------------------------------
1 | text = input()
2 | k = int(input())
3 |
4 | '''lst = [text[i*k:(i+1)*k] for i in range(len(text)//k)]
5 |
6 | for seg in lst:
7 | temp_lst = [i for i in list(seg)]
8 | text_new = ''
9 | for i in temp_lst:
10 | if i not in text_new:
11 | text_new += i
12 | print(text_new)
13 |
14 | 1차 답, 맞긴한데 더 깔끔하게 만들어보자
15 | '''
16 |
17 | lst = [text[i*k:(i+1)*k] for i in range(len(text)//k)]
18 | print(lst)
19 |
20 | # 포럼의 답들이 iter() 함수를 사용하는데 이게 뭔지 이해해야 코드를 줄일 수 있을 듯
21 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/nested lists.py:
--------------------------------------------------------------------------------
1 | lst = []
2 | for _ in range(int(input())):
3 | lst.append([input(), float(input())])
4 |
5 | print(lst)
6 |
7 | second_lowest = sorted(list(set([score for name, score in lst])))[1]
8 | print(second_lowest)
9 | result = [name for name, score in sorted(lst) if score == second_lowest]
10 | print('\n'.join(result))
11 |
12 | # 답지 보고 했다. 어렵다잉 모르는 기능은 없는데!
13 | # 다음 문제부터는 아무리 코드가 지저분해지더라도 일단 내 답을 낸 다음에 답지를 보자
14 |
15 | '''dict = {}
16 | for _ in range(int(input())):
17 | name = input()
18 | score = float(input())
19 | dict[name] = score
20 |
21 |
22 | _ 는 for문 안에서 _를 쓸 일이 없을 때 사용하는 듯 하다.
23 | 5
24 | Harry
25 | 37.21
26 | Berry
27 | 37.21
28 | Tina
29 | 37.2
30 | Akriti
31 | 41
32 | Harsh
33 | 39
34 | 이렇게 input을 받을 건데 range(5) 가 되니까 다섯번 반복하라는 얘기지. i 랑 사실상 똑같지만
35 | 실행 내용에 i 가 등장하지 않을 때 _를 사용하는 것 같다.
36 | 아 근데 저렇게 하면 name 과 score 이 계속 다음 input으로 교체되는 거 아닌가?
37 | 내가 딕셔너리에 key: value 로 넣어줘야하나보다
38 | 그럼 이런식으로 나온다
39 | [['Harry', 37.21], ['Berry', 37.21], ['Tina', 37.2], ['Akriti', 41], ['Harsh', 39]] 아, 딕셔너리가 아니라 리스트로 받는게 더 낫나?
40 | 내가 만든건
41 | {'Harry': 37.21, 'Berry': 37.21, 'Tina': 37.2, 'Akriti': 41, 'Harsh': 39}
42 |
43 |
44 | print(dict)
45 | scores = list(dict.values())
46 | print(scores)
47 | scores.sort() #sort는 원래의 리스트를 변형하기 때문에 new_score = scores.sort() print(new_score) 하면 None 이 나온다.
48 | print(scores)
49 | a = set(scores)
50 | minimum = min(a)
51 | print(min(a))
52 | second = min(a.pop(minimum))
53 | print(second)
54 | 계속 너무 복잡해지기만 하네 ㅜㅜ 다른 방법을 생각해보자'''
55 |
56 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/string split and join.py:
--------------------------------------------------------------------------------
1 | line = input()
2 | lst = line.split()
3 | result = '-'.join(lst)
4 | print(result)
5 |
6 | # print(raw_input().replace(' ','-') 이런 경우 replace가 훨씬 깔끔!
--------------------------------------------------------------------------------
/99_etc/hackerrank/swap case.py:
--------------------------------------------------------------------------------
1 | text = input()
2 |
3 | lst = []
4 | for char in text:
5 | if char == char.lower():
6 | lst.append(char.upper())
7 | else:
8 | lst.append(char.lower())
9 |
10 |
11 | result = ''.join(lst)
12 | print(result)
13 |
14 | #겁나 잘 짰는데 swapcase() 가 있었고요?ㅋㅋㅋ
--------------------------------------------------------------------------------
/99_etc/hackerrank/the minion game.py:
--------------------------------------------------------------------------------
1 | word = input()
2 | vowel = ['A', 'E', 'I', 'O', 'U']
3 |
4 | vowel_count = 0
5 | cons_count = 0
6 | for i in range(len(word)):
7 | if word[i] in vowel:
8 | vowel_count += len(word) - i
9 | else:
10 | cons_count += len(word) - i
11 |
12 | if cons_count > vowel_count:
13 | print('Stuart', cons_count)
14 | elif cons_count < vowel_count:
15 | print('Kevin', vowel_count)
16 | else: print('Draw')
17 |
--------------------------------------------------------------------------------
/99_etc/hackerrank/tuples.py:
--------------------------------------------------------------------------------
1 | num = int(input())
2 | text = input()
3 |
4 | lst = [int(i) for i in text.split()]
5 | tpl = tuple(lst)
6 | print(hash(tpl))
--------------------------------------------------------------------------------
/99_etc/leetcode/best time to buy and sell stock 2.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiukeem/python_algorithm/e25d02c53a088640f34a578e63da0f58c0decbaa/99_etc/leetcode/best time to buy and sell stock 2.py
--------------------------------------------------------------------------------
/99_etc/leetcode/remove duplicates from sorted array.py:
--------------------------------------------------------------------------------
1 | class Solution:
2 | def removeDuplicates(self, nums: List[int]) -> int:
3 | if len(nums) == 0: return 0
4 |
5 | j = 1
6 | for i in range(1, len(nums)):
7 | if nums[i] != nums[i - 1]:
8 | nums[j] = nums[i]
9 | j += 1
10 | return j
11 |
12 | # 네시간걸림..ㅋㅋㅋㅋㅋㅋㅋ 알고리즘이 이런거구나
13 | # 시간,공간복잡도와 in-place, two-pointer 등을 공부했음
14 | # 메모리 distribution은 상위권, 근데 runtime은 하위25프로다
15 | # 릿코드 엄청 좋당
16 |
17 |
18 | class Solution:
19 | def removeDuplicates(self, nums: List[int]) -> int:
20 | if len(nums) == 0: return 0
21 |
22 | a = 0
23 | while a < (len(nums) - 1):
24 | if nums[a] == nums[a + 1]:
25 | del nums[a + 1]
26 | else:
27 | a += 1
28 | return len(nums)
29 |
30 | # del 을 쓰면 runtime이 줄어들고 메모리가 늘어난다.
31 | # 근데 del을 써도 in-place sorting 을 만족하는건가? 이거 공부 좀 해야겠다
--------------------------------------------------------------------------------
/99_etc/programmers/완주하지 못한 선수.py:
--------------------------------------------------------------------------------
1 | participant = input().split()
2 | completion = input().split()
3 |
4 | for name in participant:
5 | if name not in completion:
6 | print(name)
7 | else:
8 | completion.remove(name)
9 |
10 | # for 문의 시간복잡도는 O(n), remove도 모든 요소를 살펴보면서 맞는 걸 찾기때문에 O(n)
11 | # 즉 내 코드의 복잡도는 O(n^2)가 된다.
12 | # 앗, in 혹은 not in 도 O(n)이다 나는 겁나 쓰잘데기 없이 오래걸리는 코드를 짠 것..
13 | # 지금까지 시간복잡도, 공간복잡도라는 개념을 한번도 접해본 적이 없는데 이제 여기에 신경 써서 짜는 연습을 해야할 것 같다.
14 | # 시간복잡도를 O(n)까지 줄이자
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | python-practice
2 |
3 | 00_자료구조 구현
4 | >연결리스트, 배열, 해시테이블, 트리 등의 자료구조를 파이썬으로 구현해본 작업들.
5 | >레퍼런스: blex.me/@baealex/series/ + 코드잇 자료구조 코스
6 |
7 | 01_알고리즘 구현
8 | >정렬, 다이나믹 프로그래밍 등 알고리즘을 파이썬으로 구현해본 작업들.
9 | >레퍼런스: codeit의 알고리즘 강의
10 |
11 | 02_leetcode
12 | >릿코드 문제들을 푼 코드들.
13 | >레퍼런스: '파이썬 알고리즘 인터뷰'
14 |
15 | 03_codeit
16 | >코드잇 알고리즘의 정석 코스 중 마지막 단계인 '문제 해결 능력 기르기'에서 중요하다 싶은 문제들을 따로 기록(대부분은 그냥 페이지 내에서 코드짜고 통과함)
17 |
18 | 04_프로그래머스
19 | >lev3까지 커버할 수 있는 실력을 키우는게 최종 목표이다.
20 |
21 | >lev1 문제들은 난이도는 높지 않지만 깔끔하고 더 효율적으로 짤 수 있는 방법을 다양하게 시도하며 내 코드를 다듬는 문제들로 활용하기 좋다.
22 |
23 | >lev2 문제들은 대부분 30분 - 1시간 내에 풀 수 있고 종종 막히는 문제도 있다.
24 |
--------------------------------------------------------------------------------