Hyemi Lee

Hyemi Lee

주니어 개발자의 삽질과 기록

Algorithm, Trie

Trie(트라이)

  • 문자열에서의 검색을 빠르게 해주는 자료구조
  • 정수형 자료형에 대해 이진검색트리를 이용하면 O(logN)의 시간만에 검색가능하다
  • 하지만 최대 길이가 M이라면 O(MlogN)의 시간 복잡도를 가지게 된다
  • 이를 개선 한것이 트라이 !! O(M)

trie

구현

  • Trie.java
package org.study;
import java.util.*;
public class Trie {
	private TrieNode root;
	// -- 생성자 , 루트노드 생성
	public Trie() {
		root = new TrieNode(' ');
	}
	// -- 삽입
	public void insert(String word) {
		// 삽입하려는 문자가 모두 존재할때
		if(search(word) == true) return;
		TrieNode current = root;
		for (char ch : word.toCharArray()) {
			TrieNode child = current.subNode(ch);
			if(child != null) {
				current = child;
			}
			else {
				// ch 문자가 있으면 그 노드 리턴
				current.childList.add(new TrieNode(ch));
				current = current.subNode(ch);
			}
			current.count++;
		}
		current.terminal = true;
	}
	// -- 검색
	public boolean search(String word) {
		TrieNode current = root;
		// 문자열을 문자배열로 쪼개서 비교
		for(char ch : word.toCharArray()) {
			// 해당 문자의 노드가 없으면 false
			if(current.subNode(ch) == null)
				return false;
			// 해당 문자가 존재하면, 현재 노드에 서브 노드 저장해서 단계별로 내려감
			else
				current = current.subNode(ch);
		}
		// a,p,p,l,e 가 있을때,
		// word = apple 이면 true / word = appleb 이면 false
		if(current.terminal == true) return true;
		return false;
	}
	// -- Trie에 저장된 단어 목록 Iterator타입으로 반환
	public Iterator<String> iterator(){
		Set<String> elementsInTrie = new TreeSet<>();
		// 저장됨 데이터를 elementsIntrie에 저장
		recursiveCallPrint(root, "", elementsInTrie);
		Iterator<String> elementsInTrieSet = elementsInTrie.iterator();
 return elementsInTrieSet;
	}
	private void recursiveCallPrint(TrieNode currNode,
			String key, Set<String> elementsInTrie) {
		if(currNode.terminal) {
			elementsInTrie.add(key);
		}
		LinkedList<TrieNode> children = currNode.childList;
 Iterator<TrieNode> childIter = children.iterator();
 String sKey = key;
 while (childIter.hasNext()) {
 TrieNode nextNode = childIter.next();
 // 문자열 합치기 (키+문자)
 String s = sKey + nextNode.nodeChar;
 // 재귀 호출 (다음 노드, 키값, 저장셋)
 recursiveCallPrint(nextNode, s, elementsInTrie);
 }
	}
	public void printWord() {
	 System.out.println("▶Elements in the TRIE are :");
	 Iterator<String> itr = iterator();
	 while (itr.hasNext()) {
	 System.out.println(itr.next());
	 }
	 System.out.println("===================");
	}
}
/*
 * 트라이 노드 정의
 * */
class TrieNode implements Comparable<TrieNode>{
	char nodeChar; 		// 문자저장
	boolean terminal; 	// 리프 노드 여부
	int count; 			// 해당 노드 사용수, 내 노드에 몇개의 가지가 있는지
	LinkedList<TrieNode> childList; // 자식 노드 리스트
	// constructor
	public TrieNode(char c) {
		childList = new LinkedList<>();
		terminal = false;
		nodeChar = c;
		count = 0;
	}
	boolean getTerminal() {
		return terminal;
	}
	// 해당 노드가 가지고 있는 자식 노드들중에서 입력받은 문자가 있으면 그 노드 리턴
	public TrieNode subNode(char nextChar) {
		// 자식 노드들이 있다면
		if(childList != null) {
			for(TrieNode eachChild:childList) {
				if(eachChild.nodeChar == nextChar) {
					return eachChild;
				}
			}
		}
		return null;
	}
	@Override
	public int compareTo(TrieNode other) {
		return Character.compare(this.nodeChar, other.nodeChar);
	}
	@Override
 public String toString() {
 return this.nodeChar+"("+this.terminal+") ";
 }
}
  • TrieTest.java
package org.study;
public class TrieTest {
	public static void main(String[] args) {
 //자료구조에 저장할 데이터
 // String data[] = {"abcd","abcf", "bcd","a", "bcdefg", "defg", "aac", "baz", "foo", "foobar","자바"};
 Trie matcher = new Trie();
	 matcher.insert("abcd");
	 matcher.insert("abcf");
	 matcher.insert("bcd");
 //trie에 있는 데이터 콘솔에 나열.
 matcher.printWord();
 System.out.println(matcher.search("abc"));
	}
}
  • 문제풀이
package org.study;
//https://code0xff.tistory.com/76
//https://kim6394.tistory.com/175
//https://m.blog.naver.com/javaking75/140211950640
import java.io.*;
public class TrieTest_boj5052 {
	private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	private static int i;
	private static String str;
	private static int res;
	static int nextInt() {
		try {
			i = Integer.parseInt(br.readLine().trim());
		}catch (Exception e) {}
		return i;
	}
	static String nextLine() {
		try {
			str = br.readLine().trim();
		}catch (Exception e) {}
		return str;
	}
	public static void main(String[] args) {
		int T = nextInt();	// testcase
		while(T>0) {
			T--;
			int N = nextInt();	// 전화번호 수
			Node root = new Node('r',0);
			for(int n=1; n<=N; n++) {
				Node node = root;
				String str = nextLine();	// 전화번호 한줄읽음
				for (int i=0; i<str.length(); i++) {
					char c = str.charAt(i);
					// 해당 글자로 시작하는 가지가 없으면
					// 해당 글자로 시작하는 가지를 만들어준다
					// c-'0' : char to int
					System.out.println("c ==>"+c);
					if( node.nxt[c-'0'] == null) {
						node.cnt++;	// 이 전글자의 node수 증가
						node.nxt[c-'0'] = new Node(c,0);
						System.out.println(node.cnt+" , c-'0':"+(c-'0'));
					}
					// 해당 글자를 가르킨다.
					node = node.nxt[c-'0'];
					System.out.println("data="+node.data+" cnt="+node.cnt);
				}
			}
			res = 0;
			searchLeaf(root);
			// res 가 전화번호 수가 아니라면
			if(res != N)
				System.out.println("No");
			else
				System.out.println("YES");
		}
	}
	public static void searchLeaf(Node node) {
		if (node.cnt == 0) {
			res++;
			return;
		}
		// node의 개수가 0이 아니라면
		// node자식 최대 개수10
		for (int i=0; i<10; i++) {
			if(node.nxt[i] != null)
				searchLeaf(node.nxt[i]);
		}
	}
}
class Node {
	char data;
	int cnt;
	Node[] nxt = new Node[10];	// 전화번호는 길어야 10자리
	public Node(char data, int cnt) {
		this.data = data;
		this.cnt = cnt;
	}
}

Reference

Share on

Twitter Facebook LinkedIn

You may also enjoy

Redis Stream

2021年04月28日

Stream Stream은 로그 데이터를 처리하게위해 5.0에서 새로 도입된 데이터 타입입니다. 대량의 데이터가 연속적으로 발생할때 처리하기 위해 등장했습니다. 기존 데이터를 수정하지 않고 오직 추가로 발생합니다. 이런 종류의 데이터를 stream or log데이터...

Study, Object, chapter2&3 presentation

2021年04月20日

chapter03. 역할, 책임, 협력 객체지향 설계란, 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동이다.

Spring, chatting 프로그램 만들기, Reactive란?

2020年06月16日

Reactive 막힘없이 흘러다니는 data(event)를 통해 사용자에게 자연스러운 응답을 주고 규모 탄력적으로 리소스를 사용하며 실패에 있어서 유연하게 대처한다 모든 지점에서 블럭 되지 않게 하자 oop와 같은 패러다임 모든 것을 비동기적인 data의 strea...