백준 1001

YJ·2021년 8월 3일
0
post-thumbnail

- 문제




- 풀이

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String num = sc.nextLine();

        int result = 0;
        int num1 = 0;
        int num2 = 0;
        for (int i = 0; i < num.length(); i++) {
            if (num.charAt(i) - 48 > 0 && num.charAt(i) - 48 < 10) {
                if (num1 == 0) {
                    num1 = num.charAt(i) - 48;
                } else {
                    num2 = num.charAt(i) - 48;
                }
            } else {
                continue;
            }
        }
        result = num1 - num2;
        System.out.println(result);
    }
}

1001번 문제와 1000번 문제의 차이는 A+B, A-B로 덧셈이 뺄셈으로 바뀐 것이다. 1000번 문제와 비교해 봤을 때, 변수 두 개와 if/else문이 추가됐다.


다른 풀이가 궁금해 구글에 백준 1001번 문제를 검색해봤다.

https://st-lab.tistory.com/41



위 블로그의 풀이를 본 후 내가 Scanner에 대해 잘 모르고 있었다는 사실을 깨달았다.

import java.util.Scanner;

public class Main {
    public static void main(String[] args)  {

        Scanner sc = new Scanner(System.in);
        System.out.print("첫번째 정수를 입력하세요 : ");
        int num1 = sc.nextInt();
        System.out.print("두번째 정수를 입력하세요 : ");
        int num2 = sc.nextInt();

        System.out.println(num1 + "과 " + num2 + "의 합은 " + (num1 + num2) + "입니다.");
    }
}

그동안 Scanner를 위와 같은 형식으로 밖에 사용해보지 않았다. 따라서 nextInt()라는 메소드가 공백이 얼마나 들어가던, 엔터키가 얼마나 들어가던 상관 없이 입력받은 정수를 차례로 리턴한다는 사실을 알지 못했던 것이다.


nextInt()를 사용하면 1000번 문제, 1001번 문제는 아래와 같이 4줄의 코드로 작성할 수 있다.




- 풀이 2

import java.util.Scanner;

public class Main {
    public static void main(String[] args)  {

        Scanner sc = new Scanner(System.in);
        int num1 = sc.nextInt();
        int num2 = sc.nextInt();

        System.out.println(num1 - num2);
    }
}

보다시피 입력 받은 정수 사이에 공백이 얼마나 포함되건, 엔터가 얼마나
포함되건 상관 없이 입력한 정수만 변수에 담아 계산이 이루어지고 있다.

뿐만 아니라 위 링크의 블로그를 보면 ScannerBufferedReader 보다 성능이 현저하게 떨어지기 때문에 아래와 같이 짠 코드를 볼 수 있다.




- 풀이 3

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String str = br.readLine();
        StringTokenizer st = new StringTokenizer(str, " ");

        int num1 = Integer.parseInt(st.nextToken());
        int num2 = Integer.parseInt(st.nextToken());

        System.out.println(num1 - num2);
    }
}

블로그에 작성된 정보에서 내가 이해한 부분만 정리해 보면 다음과 같다.

1. System.in 은 InputStream이라는 클래스의 객체이다. 
-> InputStream은 byte 단위로 읽는 역할을 하는 스트림이다.
-> 스트림은 그냥 데이터가 흘러가는 통로이다.

2. InputStreamReader는 바이트 단위로 읽은 데이터를 문자로 변경하는 
역할을 하는 클래스이다. 따라서 매개변수로 InputStream 타입을 받는다.

3. BufferedReader는 데이터을 직렬화 하는 역할의 클래스이다.
-> String으로 만들어준다고 생각하면 된다.

즉, new BufferedReader(new InputStreamReader(System.in)
이라고 쓰인 코드는 System.in을 통해 입력받은 데이터를 byte 단위로
읽어오고, InputStreamReader를 통해 문자로 변환한 뒤, 
BufferedReader를 통해 해당 데이터를 직렬화해서 버퍼에 쌓아놓는
것이다.



실제로 '123 456'이 입력 됐을 때, br.readLine이 실행되는 과정을 생각해봤다.




- 풀이 4

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] str = br.readLine().split(" ");
        int num1 = Integer.parseInt(str[0]);
        int num2 = Integer.parseInt(str[1]);

        System.out.println(num1 - num2);
    }
}

BufferedReader를 사용한 것은 동일하다. StringTokenizer가 아닌 split을 통해 구현한 모습이다.



추가적으로 split은 공백이 하나일 시에만 위의 코드가 정상 작동한다.
물론 공백이 두개라면 두번으로 split할 수 있겠지만 공백이 하나일 수도
두개일 수도 세개일 수도 있다면 구현에 어려움이 생길 것 같다.

반면 Scanner의 nextInt와 StringTokenizer는 공백의 숫자와
상관없이 데이터를 추출해준다.




- 결과 비교

BufferedReader + split (풀이 4)
BufferedReader + StringTokenizer (풀이 3)
Scanner의 nextInt() (풀이 2)
본인이 짠 코드 (풀이 1)



생각보다 Scanner와 nextInt를 사용한 코드와 
내가 짠 코드의 속도의 차이는 안난다. 하지만
1. 조건문 + 반복문 사용으로 코드량이 늘어나면 차이가 날 것 같다.
2. Scanner, nextInt를 사용했을 시 코드량이 현저하게 적다.
3. 가독성도 전자가 좋다고 생각한다.
이런 이유들을 추정해봤을때 Scanner를 사용하는게 더 좋아보인다.

더 나아가 Scanner를 사용했을때와 BufferedReader를
사용했을 경우를 비교해보자. 결과에서 보다시피
Scanner를 사용했을 때 보다 BufferedReader를 사용했을
경우 속도가 훨씬 빠르다. 이유는 Scanner는 많은 정규식 등을
검사하는 과정이 있는 반면 BufferedReader는 그런 과정이
없으며, Buffer에 데이터를 쌓아두었다가 한번에 반환하기 떄문이다.
따라서 BufferedReader를 사용하는게 더 좋아보인다.

더 나아가 StringTokenizer와 split을 비교해보자. 
위 결과에서는 유의미한 차이가 없지만 데이터가 많아지면
많아질 수록 StringTokenizer가 성능상 우위를 점한다고 한다.
따라서 StringTokenizer를 사용하는게 더 좋아보인다.
profile
hi

0개의 댓글