문자를 입력 받아서 TreeSet에 넣기

냥린이·2022년 2월 24일
0

JAVA

목록 보기
2/3

백준 1991번을 TreeSet으로 풀려고 시도하던 코드를 남기기 위해 작성하는 글이다.
이 문제는 알파벳을 각 노드의 데이터로 삼는 이진 트리를 중위순회, 후위순회, 전위순회로 출력하는 문제이다.
결론부터 말하면 이 문제는 아래의 이유로 TreeSet으로 풀기에 적합하지 않다.

TreeSet의 주요 특징

  • 입력된 노드를 자동으로 정렬하여 tree에 넣음
  • 정렬기준을 바꾸고 싶다면 comparator 지정 필요
  • 노드는 중복될 수 없음

TreeSet은 데이터를 자동으로 정렬해주기 때문에 문제에서 제시하는 트리 모양을 유지하기가 어렵기 때문이다.
결과적으로 문제 풀이에 활용하지는 않았지만, 주어진 입력값을 전처리해서 TreeSet에 넣었던 과정만 따로 글을 쓰려고 한다.

문자 관련된 기능 이것저것 다 다뤄보겠다고 용을 썼는데 까먹지 않게 남겨두려고...

입력

만약 아래와 같이 트리 노드가 여러 줄의 문자열로 입력될 때 TreeSet에 어떻게 넣어줄까?

number of input
a b c
b d .
e f g

노드는 공백 단위로 분류하고 .이 들어오면 무시해야 한다.

String -> StringBuilder -> char[] -> String

도대체 왜 이랬던 걸까? ㅋㅋ 돌리고 돌리고..

  • Scanner의 nextLine()으로 줄 단위로 입력 받아 string에 저장한 후 replaceAll("regex", "replacement") 정규표현식으로 공백과 .을 정리
  • StringBuilder로 가변 문자열을 만들어서 줄 단위 입력을 차곡차곡 append(string)
  • StringBuilder와 같은 크기를 갖는 문자 배열을 만들어서 getChars(FromStart, FromEnd, ToStart, ToEnd)를 통해 문자 값을 복사
  • for문을 돌면서 String.valueOf(char)로 string으로 변환 후 TreeSet에 add
import java.io.*;
import java.util.*;

public class TreeSet{
	public static void main(String[] args) throws IOException{
		
		Scanner sc = new Scanner(System.in);
		StringBuilder sb = new StringBuilder();
		
		int n = sc.nextInt();
		sc.nextLine(); // 줄바꿈 문자 삭제용
		
		for(int i=0; i<n; i++){
			String line = sc.nextLine();
			line = line.replaceAll("\\s", "");
			line = line.replaceAll("\\.", "");
			sb.append(line);	
		}
		char[] charArray = new char[sb.length()];
		sb.getChars(0, sb.length(), charArray, 0);
		
		TreeSet<String> ts = new TreeSet<String>();
		
		for(char c: charArray){
			String s = String.valueOf(c);
			ts.add(s);
		}
		// TreeSet 전체 출력
		System.out.println(ts);

    	// TreeSet 하나씩 출력
		Iterator iter = ts.iterator();	// Iterator 사용
		while(iter.hasNext()) {//값이 있으면 true 없으면 false
			System.out.print(iter.next()+ " ");
		}
	}
}

이때 반드시 잊으면 안 되는 건 Scanner의 nextInt()를 한 후 다른 입력을 받고 싶을 때,
nextLine()을 통해서 줄바꿈 문자를 흡수해줘야 한다.

String

java의 String 클래스가 얼마나 소중한지 깨달을 수 있었다.
만약 char로 모든 걸 처리했어야 했다면 정말 끔찍했을거야..

여러 시도를 해봤지만 BufferedReader와 StringTokenizer의 조합이 가장 좋다.

  • BufferedReader는 정규표현식 처리를 안 해줘서 Scanner에 비해 속도가 빠르다.
  • String의 split은 매번 정규표현식 패턴을 짜야한다는 게 부담스러울 때가 있다. 반면에 StringTokenizer는 " " 안에 구분자를 그대로 넣어줄 수 있다.

여전히 해결이 안 되는 부분도 있다.

  • Scanner의 useDelimiter은 구분 후 hasNextInt() 이런 식으로 접근해야 되서 귀찮다.
  • StringTokenizer도 같은 문제가 있는데, nextToken()으로 접근해야 되서 배열로 파싱할 때 for문을 써야된다는 점이 별로다.

암튼 아래 코드는 string이 항상 3개 나온다는 걸 알고 있었으므로 for 문 없이 직접 작성했다.

import java.io.*;
import java.util.*;

public class BOJ_1991{
	public static void main(String[] args) throws IOException{
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		
		TreeSet<String> ts = new TreeSet<String>();
		
		for(int i=0; i<n; i++){
			StringTokenizer st = new StringTokenizer(br.readLine(), " ");
			String node = st.nextToken();
			String left = st.nextToken();
			String right = st.nextToken();
			if(!(node.equals("."))) ts.add(node);
			if(!(left.equals("."))) ts.add(left);
			if(!(right.equals("."))) ts.add(right);
		}
		// TreeSet 전체 출력
		System.out.println(ts);

    	// TreeSet 하나씩 출력
		Iterator iter = ts.iterator();	// Iterator 사용
		while(iter.hasNext()) {//값이 있으면 true 없으면 false
			System.out.print(iter.next()+ " ");
		}
	}
}

System.out.println보다 BufferedWriter가 성능 향상에 좋다고 한다.

BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( System.out ) );
bw.write( "Hello World!!\n" );
bw.flush();
bw.close();
profile
홀로서기 기록장

0개의 댓글