이번 글에서는 자료구조 트라이(TRIE) 에 대해서 알아보자.
1. 트라이 (TRIE) ??
먼저 '트라이'가 무엇인지에 대해서 부터 알아보자.
트라이는 "문자열을 빠르게 탐색하게 해주는 자료구조" 이다. 즉, '문자열'을 관리하는 방법 중의 하나라고 생각하면
될 것 같다.
문자열을 빠르게 탐색할 수 있다는 장점 이외에도 문자열을 관리하는데 있어서 좋은점을 많이 가진 자료구조이니
이 부분에 대해서는 트라이에 대해서 지금부터 알아본 후에 마지막으로 다시한번 정리해보도록 하자.
2. 트라이의 작동원리
트라이는 주어진 문자열을 이루고 있는 문자를 앞에서 부터 하나씩 노드를 생성해가면서 만들어 진다.
이 과정에서 반복된 재귀 호출이 이루어지는데, 말로는 무슨 말인지 모르겠으니, 이 과정을 구체적으로 알아보자.
먼저, 문자열을 트라이로 생성하는 과정부터 알아보자. 간단하게 순서를 붙여보자면 다음과 같이 진행된다.
( 지금부터 '루트(Root)' 라는 말이 나올 수 있는데, 이는 가장 초기 노드라 생각하면 된다.
즉, 연결되어 있는 어떠한 노드도 없는 초기 노드이다. )
1. 주어진 문자열에서 현재 문자를 가져온다.
2. 현재 문자로 이루어진 노드가 존재한다면, 그 노드로 그 다음 문자열을 탐색하고, 노드가 없다면 그 노드를
새로 할당 받은 후, 해당 노드를 통해 다음 문자열을 탐색한다.
3. 문자열의 마지막이 될 때까지 위의 과정을 반복한다.
위의 과정을 그림으로 한번 알아보자.
문자열, [ ABC , ABCD , BCG , ZYX ,BDE ] 를 순차적으로 삽입하는 과정을 알아보자.
가장 초기, 루트노드에는 아무런 값도, 노드도 존재하지 않는다. 즉, 다음과 같은 상태일 것이다.
그럼, "ABC" 부터 트라이에 삽입해보자.
1. 주어진 문자열에서 현재 문자를 가져온다.
지금 주어진 문자열 'ABC'에서 아직 아무런 문자도 삽입하지 않았으므로, 현재 문자는 가장 앞에 있는 문자인 'A'를
가르키고 있을 것이다.
2. 현재 문자로 이루어진 노드가 존재한다면, 그 노드로 그 다음 문자열을 탐색하고, 노드가 없다면 그 노드를
2. 현재 문자로 이루어진 노드가 존재한다면, 그 노드로 그 다음 문자열을 탐색하고, 노드가 없다면 그 노드를
1 2 3 4 5 6 7 8 9 10 11 | struct TRIE { bool Finish; TRIE *Node[26]; TRIE() { Finish = false; for (int i = 0; i < 26; i++) Node[i] = NULL; } } | cs |
사용하는 변수이다.
line 4) TRIE *Node[26] 이 변수는 우리가 지금까지 계속해서 만들어왔던 트라이의 '노드'이다. 26칸으로 설정한 이유는
알파벳이 총 26개이기 때문이다. 알파벳이 아니라면, 이 배열의 크기도 상황에 맞게 적절하게 바꿔주면 된다.
이번 글에서는 위에서 말한 문자열들을 예시로 들고 있으므로, 26칸으로 설명을 하겠다.
line 5 ~ 9) 기본적인 생성자이다. '노드를 할당한다' 라는 말이 굉장히 많았었는데, 모두 이 생성자를 통해서 생성해줄 것이다
1 2 3 4 5 6 7 8 9 10 11 12 | void Insert(char *Str) { if (*Str == NULL) { Finish = true; return; } int Cur = *Str - 'A'; if (Node[Cur] == NULL) Node[Cur] = new TRIE(); Node[Cur]->Insert(Str + 1); } | cs |
1 2 3 4 5 6 7 8 9 10 11 | bool Find(char *Str) { if (*Str == NULL) { if (Finish == true) return true; return false; } int Cur = *Str - 'A'; if (Node[Cur] == NULL) return false; return Node[Cur]->Find(Str + 1); } | cs |
이 상태에서 "ABC"라는 문자열을 찾는 과정을 알아보자. 위의 코드는 본인이 구현한 Str이라는 문자열을 찾는 부분이다.
만약, 해당 문자열을 찾았다면 true를 반환, 그게 아니라면 false를 반환하도록 만든 코드이다.
코드가 삽입하는 코드와 굉장히 비슷하다.
line 9)
노드가 생성되지 않았다면 바로 false를 반환해버리는 부분이다.
왜냐하면, 찾고자 하는 문자열이 트라이에 삽입되어 있는 문자열이라면, 삽입하는 과정에서 당연히 노드가 생성되어
있을 것이다. 그런데, 문자열을 찾는 도중에 생성되지 않는 노드가 존재한다 ? 이 말은 즉, 트라이에 없는 문자열이라는 것을
의미하기 때문이다.
line 3)
찾고자 하는 문자열의 마지막까지 탐색을 했을 경우이다. 이 경우라고 해서, 무조건 해당 문자열이 존재하는 것이 아니다.
우리가 만들어 놓은 트라이에서 "AB"라는 문자열을 찾는다고 생각해보자.
루트 → 'A'가 있으므로, 노드를 잘 타고 넘어와서, A → B도 있으므로 잘 타고 넘어올 것이다.
그 이후, line 3에 걸릴텐데, 우리가 트라이에 삽입한 문자열은 "ABC"와 "ABCD"이지, "AB"라는 문자열은 없는 문자열이다.
즉, line 3) 까지 왔다고 하더라도, 해당 문자열이 마지막 문자열인지 확인을 해줘야 한다.
따라서 line 5)와 같은 조건문을 설정해 준 것이다.
다시 본론으로 넘어와서 "ABC"를 찾는 경우, 'C'까지 간 이후에, line 3) 에 걸릴 것이다.
그런데, 'C'는 Finish가 true로 설정되어 있다. 즉, 문자열의 마지막이라는 이야기이다. 그래서 "ABC"는 존재하는 문자열
이라고 판단할 수 있는 것이다.
본인은 위와 같이 코드를 한번 구현해 보았다. 이 외에도 어떻게 구현하냐에 따라서 정말 많은 것을 할 수 있는 자료구조 이다 !!
4. 트라이의 좋은 점 ??
위에서도 말했고, 우리가 계속 해왔듯이, '문자열의 탐색'에 있어서 굉장히 유용한 자료구조이다.
또한, 어떤 함수를 어떻게 구현하는지, 어떤 멤버변수를 선언해서 어떻게 관리하는지에 따라서 문자열에 관련된
여러가지 역할을 해내기에 충분히 좋은 자료구조이다.
또 하나는, 트라이에 삽입만 하게 된다면, 자동으로 정렬된 효과를 볼 수 있다는 점도 좋은 점 중 하나라고 생각한다.
어떻게 정렬된 효과를 볼 수 있을까 ??
우리가 삽입과정을 알아본 예시를 보면 [ ABC , ABCD , BCG , ZYX ,BDE ] 이 순서대로 삽입을 했다.
그런데, 순서를 바꿔서 [ ZYX , BCG , ABC , BDE , ABCD ] 순서대로 삽입을 했다고 생각해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | void IsSorting(TRIE *N, char Str[], int Idx) { if (N->Finish == true) cout << Str << endl; for (int i = 0; i < 26; i++) { if (N->Node[i] != NULL) { char C = i + 'A'; Str[Idx] = C; N->IsSorting(N->Node[i], Str, Idx + 1); } } } int main(void) { TRIE *Root = new TRIE(); char *String[5]; String[0] = "ZYX"; String[1] = "BCG"; String[2] = "ABC"; String[3] = "BDE"; String[4] = "ABCD"; for (int i = 0; i < 5; i++) { Root->Insert(String[i]); } for (int i = 0; i < 26; i++) { if (Root->Node[i] != NULL) { char Str[5] = { NULL }; Str[0] = i + 'A'; Root->IsSorting(Root->Node[i], Str, 1); } } } | cs |
'[ Algorithm ] > # Solution - ' 카테고리의 다른 글
[ 펜윅트리(FenwickTree) ] 개념과 구현방법 (C++) (13) | 2020.05.07 |
---|---|
[ 세그먼트 트리(Segment Tree) ] 개념과 구현방법 (C++) (19) | 2020.05.02 |
[ 자료구조 힙 ] 개념과 구현방법 (C++) (3) | 2020.03.07 |
[ 다익스트라 알고리즘과 벨만포드 알고리즘 ] (0) | 2020.03.06 |
[ 벨만포드 알고리즘 ] 개념과 구현방법 (C++) (8) | 2020.03.04 |