안녕하세요 coding-knowjam입니다.
오늘은 검색할 때 O(N)의 시간 복잡도를 가지는 Trie를 구현해보겠습니다.
1. Trie란?
Trie는 일반적인 Tree자료구조와 같은 모양이지만 저장하는 값이 다른 형태입니다.
정수형을 저장하는 Tree가 아래와 같은 모양으로 있다고 가정하겠습니다.
위와 같은 정수형 자료의 이진트리에서는 검색을 수행할 때 O(logN)의 시간 복잡도를 가지게 됩니다.
그러나 같은 이진트리 형태 이어도 문자열을 저장하고 있다면 문자열의 길이가 M일 때, O(M*logN)의 시간 복잡도를 가지게 됩니다.
이러한 문제를 해결하기 위해 Trie를 사용하게 됩니다.
Trie는 문자열 전체를 하나의 노드에 저장하는 게 아니라, 한 단어씩 노드에 저장하는 트리입니다.
Trie는 루트 노드는 비어있고 첫 번째 자식 노드부터 문자열의 첫 단어가 저장됩니다.
현재 위 그림의 Trie에 저장된 문자는 cap, code, kakao, kai입니다.
Trie 자료구조는 문자열의 한 단어씩 자식 노드와 비교해가면서 검색을 진행할 수 있습니다.
예를 들어 cap을 검색한다면 c자식 노드 -> a자식 노드 -> p자식 노드 순으로 검색을 진행할 수 있습니다.
kakao를 검색한다면 k자식 노드 -> a자식 노드 -> k자식 노드 -> a자식 노드 -> o자식 노드
만약에 없는 단어를 검색한다면 어떻게 진행될까요??
coding을 검색한다면 c자식 노드 -> o자식 노드 -> d자식 노드 -> i값을 가지는 자식 노드는 없으므로 검색 불가
이러한 식으로 찾고자 하는 문자를 탐색하므로 문자열의 길이가 M일 때, 탐색 시간 복잡도는 O(M)을 가지게 되므로 매우 효율적이라고 할 수 있습니다.
2. Java코드로 구현
그렇다면 Trie 자료구조를 Java코드로는 어떻게 구현할 수 있을까요?
기본적으로는 Map Interface를 사용해서 구현하게 됩니다.
Map의 키 값에는 문자를 이루는 각각의 단어들이 들어가고, 키와 매핑되는 값에는 자식 노드 클래스가 저장됩니다.
글로만 보면 잘 이해가 안 되실 테니까 코드를 직접 보시기 전에 그림으로 먼저 살펴보겠습니다.
왼쪽은 Trie자료구조이고 오른쪽은 이를 Map을 통해서 구현할 때 key와 value로 매핑하는 코드를 그림으로 표현한 것입니다.
전체적인 코드 구현은 위의 그림과 같이 진행되며 문자가 현재 Trie내부에 존재하는지 체크하기 위한 boolean 변수도 추가적으로 사용해야 합니다.
왜냐하면 예를 들어 bus와 busy를 구분하기 위해서는 s노드가 단어의 끝인지 아닌지를 체크하는 변수가 있어야 bus가 현재 Trie에 존재한다는 것을 알 수 있기 때문입니다.
그럼 실제 코드로 한번 구현해보겠습니다.
우선 가장 먼저 Node를 만들어야겠죠?
static class Node {
// 자식노드
Map<Character, Node> chiledNode = new HashMap<Character, Node>();
// 단어의 끝인지 아닌지 체크
boolean endOfword;
}
Node는 말 그대로 Trie자료구조의 각 노드를 의미합니다.
static을 붙여서 만드는 이유는 마지막에 main메서드 안에서 테스트를 하기 위함입니다.
그다음으로 Node를 사용할 Trie 자료구조 클래스를 만들어보겠습니다.
static class CodingNojam_Trie {
// Trie자료구조를 생성할 떄 rootNode는 기본적으로 생성
Node rootNode = new Node();
// Trie에 문자열 저장
void insert(String str) {
// Trie자료구조는 항상 rootNode부터 시작
Node node = this.rootNode;
// 문자열의 각 단어마다 가져와서 자식노드 중에 있는지 체크
// 없으면 자식노드 새로 생성
for(int i=0; i<str.length(); i++) {
node = node.chiledNode.computeIfAbsent(str.charAt(i), key -> new Node());
}
// 저장 할 문자열의 마지막 단어에 매핑되는 노드에 단어의 끝임을 명시
node.endOfword = true;
}
// Trie에서 문자열 검색
boolean search(String str) {
// Trie자료구조는 항상 rootNode부터 시작
Node node = this.rootNode;
// 문자열의 각 단어마다 노드가 존재하는지 체크
for(int i=0; i<str.length(); i++) {
// 문자열의 각 단어에 매핑된 노드가 존재하면 가져오고 아니면 null
node = node.chiledNode.getOrDefault(str.charAt(i), null);
if(node == null) {
// node가 null이면 현재 Trie에 해당 문자열은 없음
return false;
}
}
// 문자열의 마지막 단어까지 매핑된 노드가 존재한다고해서 무조건 문자열어 존재하는게 아님
// busy를 Trie에 저장했으면, bus의 마지막 s단어에 매핑 된 노드는 존재하지만 Trie에 저장된건 아님
// 그러므로 현재 노드가 단어의 끝인지 아닌지 체크하는 변수로 리턴
return node.endOfword;
}
}
Trie class는 문자열을 저장하기 위한 메서드와 검색을 위한 메서드를 내부에 같이 작성해주었습니다.
사실 이런 부분은 Trie class 내부에 작성하지 않고, 따로 빼서 작성하셔도 상관없습니다.
저는 공부를 하면서 여러 사람들의 구현 코드를 봤었는데 이러한 방식이 저한테 제일 잘 맞는 방법이어서 저는 Trie 클래스 내부에 기능적인 메서드들을 같이 작성해줍니다.
위의 코드들에 대한 설명은 주석에서 하고 있으니 자세한 설명은 따로 드리지 않겠습니다.
혹시나 computeIfAbsent()나 getOrDefault() 같은 메서드가 생소하시다면 제가 이전에 포스팅한 내용에서 이를 다루고 있으니 검색해서 읽어보시면 좋을 것 같습니다. (하나도 어렵지 않습니다!)
글 제목 : [Java] Map Interface의 유용한 메서드를 알아보자! (Java 8 기준)
구현은 다 끝났으니 실제로 main 메서드 안에서 테스트를 해보겠습니다.
public static void main(String[] args) {
// Trie 자료구조 생성
CodingNojam_Trie trie = new CodingNojam_Trie();
// Trie에 문자열 저장
trie.insert("kakao");
trie.insert("busy");
trie.insert("card");
trie.insert("cap");
// Trie에 저장 된 문자열 확인
System.out.println(trie.search("bus")); // false
System.out.println(trie.search("busy")); // true
System.out.println(trie.search("kakao")); // true
System.out.println(trie.search("cap")); // true
}
위의 코드를 보시면 bus라는 문자열은 저장하지 않았기 때문에 false를 리턴해주는 것을 확인할 수 있습니다.
이는 코드의 주석에서도 설명하고 있지만 busy 문자열을 저장하게 되면 s에 매핑되는 노드가 생성될 텐데 노드가 존재한다고 해서 단어가 저장된 것은 아니기 때문에 유의하셔야 합니다.
이를 확인하기 위해 우리는 Node 클래스에서 endOfWord라는 체크 변수를 사용하고 있습니다.
감사합니다.
'Algorithm & Data Structure > 이론' 카테고리의 다른 글
[Algorithm] 유클리드 호제법을 Java로 구현해보자!! (with BOJ 2609) (1) | 2021.07.21 |
---|---|
[Algorithm] 세그먼트 트리(Segment Tree)를 Java로 구현해보자! !(with BOJ-2042 Java로 문제풀이) (0) | 2021.06.13 |
[Algorithm] 다익스트라(Dijkstra) 알고리즘을 Java로 구현해보자! (BOJ-1753 최단경로) (0) | 2021.04.20 |
[Algorithm] DFS (Depth-first Search)를 Java로 구현해보자! (7) | 2021.04.16 |
[Algorithm] BFS(Breadth-first search)를 Java로 구현해보자! (3) | 2021.04.11 |
댓글