자료구조 Set
자료구조에서의 set는 단순히 , 고유한 요소들의 모음을 나타내는 class이다.
Tree 기반 set와 해시 기반 Set가 존재한다.
특징
1. 중복된 요소가 없음.
2. 순서가 없음.
3. 변경 가능 (추가, 제거 가능)
해시 기반 Set
대부분 set는 해시 테이블 기반으로 만들어지며, insert, add 등이 O(1)의 시간 복잡도를 가진다.
Initialize
thisset = set()
# thisset = {}를 하면 dict로 인식하기 때문에 초기화는 set()로 해준다.
thisset = {1,2,3}
# set 초기화
Add and Remove
thisset.add(4)
thisset.remove(3)
Set Operations
# 합집합
union_set = set1 | set2
print(union_set) # 출력: {1, 2, 3, 4, 5}
# 교집합
intersection_set = set1 & set2
print(intersection_set) # 출력: {3}
# 차집합
difference_set = set1 - set2
print(difference_set) # 출력: {1, 2}
Other Methods
- len(set): set의 요소 개수를 반환
- in: 요소가 set에 있는지 확인
- clear(): 모든 요소를 제거
- copy(): set의 복사본을 반환
my_set = {1, 2, 3, 4}
# 요소 개수
print(len(my_set)) # 출력: 4
# 요소 존재 여부 확인
print(2 in my_set) # 출력: True
print(5 in my_set) # 출력: False
# 모든 요소 제거
my_set.clear()
print(my_set) # 출력: set()
# 복사본 생성
my_set = {1, 2, 3}
my_set_copy = my_set.copy()
print(my_set_copy) # 출력: {1, 2, 3}
BST Set
BST Set는 자료구조 set의 한 종류로써 고유한 요소들의 모임이지만, 위에서 언급한 해시 기반 해쉬와는 다르게 BST로 구현을 하게 된다면 값들이 정렬 되게 만든다. 즉, 해시 테이블 기반의 set은 요소들의 순서를 보장하지 않지만, 트리 기반의 set은 항상 요소들이 정렬된 상태를 유지한다.
Tree로 구현시 장점
- 정렬된 데이터 유지: 트리 기반의 set은 요소들이 항상 정렬된 상태를 유지한다. 이는 데이터가 정렬된 상태로 필요할 때 유용하다.
- 범위 쿼리: 트리 기반 set은 특정 범위 내의 요소들을 효율적으로 찾을 수 있다. 예를 들어, 최소값, 최대값 또는 특정 범위 내의 모든 값을 찾는 작업이 효율적이다.
- 순서 기반 작업: 정렬된 데이터가 필요하거나, 순서 기반의 작업을 자주 수행해야 하는 경우 트리 기반의 set이 더 유리하다.
Tree로 구현시 단점
- 성능: 평균적으로 해시 테이블 기반의 set이 요소 추가, 삭제, 검색에서 더 빠르다 (O(1) vs. O(log n)).
- 구현 복잡성: 트리 구조는 구현이 더 복잡하며, 이를 제대로 구현하기 위해 추가적인 노력이 필요하다.
어차피 Set를 사용하는거는 해쉬테이블의 장점을 살릴 때 많이 사용함으로 BST Set를 사용은 많이 안 할 것 같다.
구현
1. sortedcontainers 라이브러리에서 sortedSet 사용.
pip install sortedcontainers
from sortedcontainers import SortedSet
# SortedSet 생성
tree_set = SortedSet()
# 요소 추가
tree_set.add(4)
tree_set.add(1)
tree_set.add(3)
tree_set.add(2)
print(tree_set) # 출력: SortedSet([1, 2, 3, 4])
# 요소 제거
tree_set.remove(3)
print(tree_set) # 출력: SortedSet([1, 2, 4])
# 요소 존재 여부 확인
print(2 in tree_set) # 출력: True
print(5 in tree_set) # 출력: False
# 요소 순회
for elem in tree_set:
print(elem)
# 출력:
# 1
# 2
# 4
print(tree_set)
# 출력:
#SortedSet([1,2,4])
2. 직접 구현
class TreeNode:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
class TreeSet:
def __init__(self):
self.root = None
def add(self, key):
if not self.root:
self.root = TreeNode(key)
else:
self._add(self.root, key)
def _add(self, node, key):
if key < node.key:
if node.left:
self._add(node.left, key)
else:
node.left = TreeNode(key)
elif key > node.key:
if node.right:
self._add(node.right, key)
else:
node.right = TreeNode(key)
def remove(self, key):
self.root = self._remove(self.root, key)
def _remove(self, node, key):
if not node:
return None
if key < node.key:
node.left = self._remove(node.left, key)
elif key > node.key:
node.right = self._remove(node.right, key)
else:
if not node.left:
return node.right
if not node.right:
return node.left
temp_val = self._min_value_node(node.right)
node.key = temp_val.key
node.right = self._remove(node.right, temp_val.key)
return node
def _min_value_node(self, node):
current = node
while current.left:
current = current.left
return current
def contains(self, key):
return self._contains(self.root, key)
def _contains(self, node, key):
if not node:
return False
if key < node.key:
return self._contains(node.left, key)
elif key > node.key:
return self._contains(node.right, key)
else:
return True
def in_order_traversal(self):
elements = []
self._in_order_traversal(self.root, elements)
return elements
def _in_order_traversal(self, node, elements):
if node:
self._in_order_traversal(node.left, elements)
elements.append(node.key)
self._in_order_traversal(node.right, elements)
# 사용 예제
tree_set = TreeSet()
tree_set.add(4)
tree_set.add(1)
tree_set.add(3)
tree_set.add(2)
print(tree_set.in_order_traversal()) # 출력: [1, 2, 3, 4]
tree_set.remove(3)
print(tree_set.in_order_traversal()) # 출력: [1, 2, 4]
print(tree_set.contains(2)) # 출력: True
print(tree_set.contains(5)) # 출력: False
자료구조 Map
자료구조에서 map은 키-값 쌍을 저장하고, 키를 사용하여 값을 효율적으로 검색할 수 있는 데이터 구조이다.
특징
- 키-값 쌍 저장: map은 각 요소가 키와 값으로 구성된 데이터를 저장한다.
- 고유한 키: 각 키는 고유해야 하며, 이를 통해 값을 검색할 수 있다.
- 효율적인 검색: 키를 사용하여 값을 빠르게 검색할 수 있다. O(1) (해시 테이블 기반), O(logn) (트리기반)
- 삽입, 삭제, 검색 연산: 일반적으로 O(1) (해시 테이블 기반) 또는 O(log n) (트리 기반) 시간 복잡도를 가진다.
해시 기반 Map
파이썬의 경우 dict 자료구조를 사용하면 된다. 끝.
BST Map
Map의 한 종류로써 해시 테이블 기반이 아닌 트리로 구현된 map이다. 해시 기반과는 다르게 key값으로 정렬된다. 파이썬에서의 dict는 해시 테이블 기반이므로 트리 기반 해시 테이블은 sortedDict를 사용하거나 직접 구현해야 한다.
구현
1. sortedcontainers 라이브러리에서 sortedDict 사용.
pip install sortedcontainers
from sortedcontainers import SortedDict
# SortedDict 생성
sorted_dict = SortedDict()
# 요소 추가
sorted_dict['c'] = 3
sorted_dict['a'] = 1
sorted_dict['b'] = 2
# SortedDict는 자동으로 키를 정렬된 상태로 유지
print(sorted_dict) # 출력: SortedDict({'a': 1, 'b': 2, 'c': 3})
# 요소 접근
print(sorted_dict['a']) # 출력: 1
# 요소 삭제
del sorted_dict['b']
print(sorted_dict) # 출력: SortedDict({'a': 1, 'c': 3})
# 요소 존재 여부 확인
print('c' in sorted_dict) # 출력: True
print('b' in sorted_dict) # 출력: False
# 키와 값 순회
for key, value in sorted_dict.items():
print(key, value)
# 출력:
# a 1
# c 3
2. 직접구현
class TreeNode:
def __init__(self, key, value):
self.key = key
self.value = value
self.left = None
self.right = None
self.height = 1
class AVLTreeMap:
def __init__(self):
self.root = None
def insert(self, key, value):
if not self.root:
self.root = TreeNode(key, value)
else:
self.root = self._insert(self.root, key, value)
def _insert(self, node, key, value):
if not node:
return TreeNode(key, value)
if key < node.key:
node.left = self._insert(node.left, key, value)
elif key > node.key:
node.right = self._insert(node.right, key, value)
else:
node.value = value
return node
node.height = 1 + max(self._get_height(node.left), self._get_height(node.right))
balance = self._get_balance(node)
if balance > 1 and key < node.left.key:
return self._right_rotate(node)
if balance < -1 and key > node.right.key:
return self._left_rotate(node)
if balance > 1 and key > node.left.key:
node.left = self._left_rotate(node.left)
return self._right_rotate(node)
if balance < -1 and key < node.right.key:
node.right = self._right_rotate(node.right)
return self._left_rotate(node)
return node
def _left_rotate(self, z):
y = z.right
T2 = y.left
y.left = z
z.right = T2
z.height = 1 + max(self._get_height(z.left), self._get_height(z.right))
y.height = 1 + max(self._get_height(y.left), self._get_height(y.right))
return y
def _right_rotate(self, z):
y = z.left
T3 = y.right
y.right = z
z.left = T3
z.height = 1 + max(self._get_height(z.left), self._get_height(z.right))
y.height = 1 + max(self._get_height(y.left), self._get_height(y.right))
return y
def _get_height(self, node):
if not node:
return 0
return node.height
def _get_balance(self, node):
if not node:
return 0
return self._get_height(node.left) - self._get_height(node.right)
def get(self, key):
return self._get(self.root, key)
def _get(self, node, key):
if not node:
return None
if key < node.key:
return self._get(node.left, key)
elif key > node.key:
return self._get(node.right, key)
else:
return node.value
def delete(self, key):
self.root = self._delete(self.root, key)
def _delete(self, node, key):
if not node:
return node
if key < node.key:
node.left = self._delete(node.left, key)
elif key > node.key:
node.right = self._delete(node.right, key)
else:
if not node.left:
return node.right
elif not node.right:
return node.left
temp = self._get_min_value_node(node.right)
node.key = temp.key
node.value = temp.value
node.right = self._delete(node.right, temp.key)
if not node:
return node
node.height = 1 + max(self._get_height(node.left), self._get_height(node.right))
balance = self._get_balance(node)
if balance > 1 and self._get_balance(node.left) >= 0:
return self._right_rotate(node)
if balance > 1 and self._get_balance(node.left) < 0:
node.left = self._left_rotate(node.left)
return self._right_rotate(node)
if balance < -1 and self._get_balance(node.right) <= 0:
return self._left_rotate(node)
if balance < -1 and self._get_balance(node.right) > 0:
node.right = self._right_rotate(node.right)
return self._left_rotate(node)
return node
def _get_min_value_node(self, node):
if node is None or node.left is None:
return node
return self._get_min_value_node(node.left)
# 사용 예제
avl_map = AVLTreeMap()
avl_map.insert("apple", 10)
avl_map.insert("banana", 20)
avl_map.insert("cherry", 30)
print(avl_map.get("apple")) # 출력: 10
print(avl_map.get("banana")) # 출력: 20
print(avl_map.get("grape")) # 출력: None
avl_map.delete("banana")
print(avl_map.get("banana")) # 출력: None
p.s. 파이썬에서의 Map 함수
파이썬에서의 map 함수는 자료구조 map과는 전혀 상관없는 함수이다. 파이썬의 map 함수는 함수형 프로그래밍 도구 중 하나로, 주어진 함수와 하나 이상의 이터러블(예: 리스트)을 인수로 받아, 이터러블의 각 요소에 함수를 적용하여 새로운 이터러블을 생성한다.
import sys
this_list = list(map(int, sys.stdin.readline().rstrip().split(' ')))
this_list = [1,2,3]
print(list(map(lambda x: x+ 1,this_list)))