

자동완성 기능과 유사하게 동작하는 트라이와, 실제 데이터베이스에서도 사용되는 B 트리를 알아봅시다람쥐. 🐿️

c, o, m, p, u, t는 동일 노드로 관리됨🤔 왜 굳이 해시 테이블(집합) 말고 트라이를 쓰나요? 문자열들을 집합에 저장해 두면, 탐색에 필요한 시간 복잡도는 아닌가요?
comp로 시작하는 모든 단어를 찾고 싶다면, 집합에선 모든 단어를 일일이 확인해서 comp로 시작해야 하는지 확인해야 합니다.c, o, m, p를 따라간 뒤, 그 아래 하위 트리만 탐색하면 쉽게 단어들을 찾을 수 있습니다.# 각 노드를 나타내는 클래스
class Node:
def __init__(self):
self.children = {}
# {문자: 노드 객체} 꼴
self.end = False # 단어의 마지막 글자인가?
class Trie:
def __init__(self):
self.root = Node()
# 이후 코드에 계속
Node: 각 노드를 나타내는 클래스self.children은 노드의 자식 노드를 저장{'A': 자식노드 객체, 'C': 자식노드 객체...} 꼴self.end는 해당 노드가 문자열의 마지막 문자인지 여부 저장Trie: 트라이를 나타내는 클래스self.root엔 루트 노드를 저장🤔 end 속성은 왜 필요한가요?

dog, doggy가 모두 저장되어 있습니다.g(dog의 마지막 문자)와 y(doggy의 마지막 문자) 모두 end 속성을 True로 지정해서, 두 단어가 모두 저장되어 있음을 나타낼 수 있습니다.end 속성을 True로 설정
# class Trie: 계속
# 문자열 삽입
def insert(self, word):
curr = self.root # 현재 노드
# 문자열의 각 글자가
# 현재 노드의 자식 중에 존재하는지 확인
for char in word:
# 없으면 해당 노드를 생성
if char not in curr.children:
curr.children[char] = Node()
curr = curr.children[char]
curr.end = True

False 반환end 속성값을 반환해 존재 여부 확인# class Trie: 계속
# 찾기 메서드
def search(self, word):
curr = self.root
for char in word:
if char not in curr.children:
return False
curr = curr.children[char]
return curr.end
🤔 위 그림의 트라이에서 comp라는 단어를 삽입하고 싶어요. 그런데 이미 computer가 들어 있는데 가능한가요?
c -> o -> m -> p까지 이동한 다음에, p 노드의 end 속성을 True로 바꿔주면 됩니다.search 메서드로 comp를 찾을 때도, 마지막 노드 p의 end 속성이 True이므로 True를 반환합니다.
condition도 탐색해야 했는데 빠뜨렸군요. 용서해주세요...
starts_with는 검색할 접두어 prefix의 각 문자를 순차적으로 따라가며, 마지막 문자의 노드까지 이동_dfs 메서드를 재귀호출하면서 DFS를 수행prefix 매개변수에 현재 노드의 문자를 추가하며 단어를 구성end에 도달하면, 현재까지 구성한 단어를 words 배열에 추가해 리턴# class Trie: 계속
# 해당 접두어로 시작?
def starts_with(self, prefix):
node = self.root
# 접수어의 마지막 노드까지 이동
for char in prefix:
if char not in node.children:
return [] # 접두어가 없음
node = node.children[char]
# BFS 수행
return self._dfs(prefix, node)
def _dfs(self, prefix, node):
words = []
if node.end: # 단어를 완성했을 때 words에 단어 추가
words.append(prefix)
# 이후 재귀 호출에서 찾은 모든 단어들의 리스트를 words에 extend
for char, next_node in node.children.items(): # 자식 노드 재귀호출
words.extend(self._dfs(prefix + char, next_node))
return words

cat, dog, doggy가 저장된 트라이가 있다고 가정했을 때...cat을 삭제하는 경우cat과 동일한 접두어를 가진 문자열이 없으므로, t -> a -> c 순으로 삭제dog을 삭제하는 경우dog은 doggy의 접두어이므로, g의 end 속성만 False로 바꾸고, 노드는 삭제하지 않음doggy를 삭제하는 경우dog까지 삭제하면 안 되므로, y -> g 순으로 삭제trie = Trie()
trie.insert("condition")
trie.insert("control")
trie.insert("hello")
trie.insert("heart")
print(trie.search("control")) # True
print(trie.search("controller")) # False
print(trie.starts_with("he")) # ["hello", "heart]

왼쪽 부모보다 크고, 오른쪽 부모보다 작은 값을 포함

3 이상의 값 찾기 -> 3부터 8까지 매번 루트 노드부터 탐색할 필요 없음3을 먼저 찾고, 연결 리스트를 통해 나머지 값을 탐색할 수 있음