..
트라이(trie)
트라이는 문자열을 효율적으로 처리하기 위한 트리 자료구조
문자열을 문자 하나씩 쪼개서 노드에 저장, 한 노드의 경로가 문자열 하나를 의미
단어 S를 삽입, 탐색, 삭제를 모조리 O(|S|)에 처리할 수 있다
하지만 메모리를 아주 많이 차지한다는 단점이 있다
트리 구조로 앞에 중복되는 문자들이 있으면 그 뒤부터 새로운 노드로 추가된다
트라이는 문자열 처리에 있어 매우 빠르지만,
공간 복잡도 측면에서는 비용이 크므로 실전에서는 상황에 따라 해시맵, 셋 등과 비교하여 사용해야 한다
코드 리뷰
class TrieNode:
def __init__(self):
self.children = {} # 자식 노드들
self.is_end = False # 단어의 끝을 나타내는 플래그
class Trie:
def __init__(self):
self.root = TrieNode()
# 문자열 삽입
def insert(self, word):
node = self.root
for char in word:
if char not in node.children: # 자식 노드 없으면 새로 생성
node.children[char] = TrieNode()
node = node.children[char]
node.is_end = True
# 문자열 존재 여부 확인 (Fetch)
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end
# 접두사 검색
def startsWith(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True
# 문자열 삭제 (Delete)
def delete(self, word):
def _delete(node, word, depth):
if depth == len(word):
if not node.is_end:
return False # 존재하지 않는 단어
node.is_end = False
return len(node.children) == 0 # 자식 없으면 삭제 가능
char = word[depth]
if char not in node.children:
return False
should_delete = _delete(node.children[char], word, depth + 1)
if should_delete:
del node.children[char]
return not node.is_end and len(node.children) == 0
return False
_delete(self.root, word, 0)
# 트라이 사용해보기
trie = Trie()
# 단어 삽입
trie.insert("apple")
trie.insert("app")
trie.insert("bat")
# 검색 테스트
print(trie.search("app")) # True
print(trie.search("apple")) # True
print(trie.search("bat")) # True
print(trie.search("batman")) # False
# 삭제 후 검색
trie.delete("app")
print(trie.search("app")) # False
print(trie.search("apple")) # True (apple은 남아있음)
trie.delete("apple")
print(trie.search("apple")) # False
단어 삽입 순서
- 노드를 루트로 초기화
문자의 수만큼 반복:- 현재 문자가 자식에 없으면 새 노드 생성
- 노드를 자식으로 이동
- 마지막 노드에 is_end = True 표시
return node.is_end 는 단순히 “경로가 존재한다”가 아니라
“정확히 이 단어가 삽입되어 있는가?” 를 판별하는 핵심이다
startsWith()는 트라이에 존재하는 어떤 단어의 앞부분과 일치하는지를 확인하는 함수
단어가 끝났는지 아닌지는 중요하지 않다
시간 복잡도
연산 | 시간 복잡도 | 설명 |
---|---|---|
삽입 (Insert) | O(|S|) | 문자열 S의 각 문자를 따라가며 노드를 생성 |
탐색 (Search / Fetch) | O(|S|) | 한 문자씩 내려가며 존재 여부 확인 |
삭제 (Delete) | O(|S|) | 리프까지 내려가며 end flag 제거, 필요 시 노드 삭제 |
접두사 검색 (StartsWith) | O(|P|) | 문자열 P의 길이만큼만 탐색 |
|S|는 입력 문자열의 길이
|P|는 접두사(prefix)의 길이