알고리즘 풀이에 Java를 쓰게 된 이유

요즘 다시 한번 알고리즘을 공부하려 하는 중이다.
알고리즘을 공부할 때 항상 나에게 문제로 다가왔던 것은 언어의 선택인데

실제 내 BOJ 기록을 보면 C++, Python, Java를 고루 사용하여 문제를 풀었다.
하지만 이제 하나의 언어로 정착할 필요성을 느껴 하나의 언어를 정하려고 한다.

알고리즘 문제를 풀 때, 많이 쓰이는 언어는 일반적으로
C, C++, Python, Java 등이 있다.

경험상 Python이 가장 간결하고 좋다.
하지만, 마이너한 기업 중에는 코딩테스트에서 Python을 쓰는 것을 인정하지 않는 기업이 있다.

그리고 나는 웹개발자로서 필드에서 그나마 저 세 언어 중 가장 많이 쓰는 언어는 Java이다.
그래서 Java로 알고리즘을 풀어나가려고 한다.

알고리즘을 풀기 전에 반드시 알아야 할 것 중 하나는 그 언어의 입출력에 대한 부분이다.
Scanner, Println등도 모르면서 알고리즘을 무작정 풀 수는 없는 노릇이고,
시간제한이 있는 문제의 경우에는 입출력에 소비되는 시간을 최소화해야만 한다.

그래서 Java의 입출력을 먼저 정리하려 한다.

참고로, 모든 자료는 백준님의 강의가 출처이며 개인적인 정리목적으로 정리한다.
정말 제대로 배워보고 싶다면 코드 플러스 사이트에서 백준님의 강의를 결제하는 것을 추천한다.
https://code.plus 에 들어가면 강의를 결제할 수 있다.

Java의 출력

  1. System.out.println(...)
    • Java에서 가장 일반적인 출력으로 특징이 있다면, 한 칸 줄넘김을 하는 것이다.
  2. System.out.print(...)
    • println과 흡사하나 줄넘김이 없다.
  3. System.out.printf(...)
    • C언어의 printf와 흡사하게 사용 가능하다.

Java의 입력

Scanner (java.util.Scanner)

  • 입력을 편하게 받을 수 있는 장점이 있다.

  • next자료형을 이용해서 입력을 받을 수 있다.

  • hasNext자료형을 이용해서 입력받을 수 있는 자료형이 있는지 구할 수 있다.

  • Iterator를 Interface로 상속받기 때문에, Iterator에서 사용하는 메소드들을 사용할 수 있다.

    public final class Scanner
    extends Object // 모든 Java의 객체는 Object를 상속
    implements Iterator<String>, Closeable // Interface로 Iterator와 Closeable을 상속
  • 공식 문서 : https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html

  • Scanner의 예제 코드

     // 이 문제는 BOJ 1000번 문제에서 볼 수 있다.
     import java.util.*;
     public class Main { // 일반적인 알고리즘 문제 해답은 Main 클래스에 작성하라는 요구가 많다.
       public static void main(String args[]) {
         Scanner sc = new Scanner(System.in); // System.in을 Argument로 넣어 생성
                                              // System.in은 System의 InputStream을 의미한다.
         int a, b;
         a = sc.nextInt();
         b = sc.nextInt();
         System.out.println(a + b);
       }
     }
  • 정수가 아닌 것이 나올 때까지 입력받는 예제

     public class Main {
       public static void main(String args[]) {
           Scanner sc = new Scanner(System.in);
           int sum = 0;
           while (sc.hasNextInt()) {
               sum += sc.nextInt();
           }
           System.out.println(sum);
           sc.close();
       }
     }
  • Scanner 입력 시에 주의할 점

     public class Main {
       public static void main(String args[]) {
           Scanner sc = new Scanner(System.in);
           int n = sc.nextInt();
           // sc.nextLine();
           String s = sc.nextLine();
           System.out.println(n + "\n" + s);        
           sc.close();
       }
     }
    • 이러한 경우에는, 실제로 s에 문자열을 입력하는게 불가능하다.
      그 이유는 n을 입력하고 엔터를 칠 때 s에는 자연스레 줄바꿈이 들어가기 때문이다.
      그래서 저 주석처리한 nextLine을 해제해줘야 제대로 입력할 수 있다.
      • 결국, 줄바꿈을 고려해야 한다.

BufferedReader (java.io.BufferedReader)

  • Scanner는 매우 편리하지만 느린 경우가 많다.

  • 입력이 많은 경우에는 BufferedReader를 사용하는 것이 빠르다.

  • BufferedReader의 예제 코드

    // https://www.acmicpc.net/problem10824 에서 사용
    import java.io.*;
    public class Main {
     public static void main(String args[]) throws IOException {
       BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
       String[] line = bf.readLine().split(" ");
       String a = line[0] + line[1];
       String b = line[2] + line[3];
       long ans = Long.valueOf(a) + Long.valueOf(b);
       System.out.println(ans);
     }
    }
  • BufferedReader는 read와 readLine이라는 메소드만 제공한다.

    • BufferedReader는 Scanner와 다르게 여러가지 형태의 자료형을 받을 수 없다.
    • 메소드에서 알 수 있듯 오직 문자열만 받을 수 있다.
    • 문자열은 StringTokenizer라는 클래스와 함께 사용하면 편하다.

StringTokenizer

  • 공식 문서 : https://docs.oracle.com/javase/7/docs/api/java/util/StringTokenizer.html

  • 사실 공식문서에도 언급되어있듯 split을 사용해도 무방하다.

  • 공백이나 띄어쓰기 컴마 등으로 구분된 문자열을 토큰화 시킬 때 유용하다.

  • StringTokenizer의 예제 코드

    import java.io.*;
    import java.util.*;
    public class Main {
      public static void main(String args[]) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in))
        String line = bf.readLine();
        StringTokenizer st = new StringTokenizer(line, ","); // 두번째 인자는 Delimeter
        int sum = 0;
        while (st.hasMoreTokens()) {
          sum += Integer.valueOf(st.nextToken()); 
        }
        System.out.println(sum);
      }
    }

StringBuilder

  • StringBuilder는 출력해야 할 것이 많은 경우에 이용한다.

  • 여러번 출력하는 것보다 StringBuilder를 이용해 문자열을 만들고 출력하는 것이 좋다.

  • StringBuilder의 예제 코드

  • StringBuilder를 이용하지 않은 경우

    • 880MS의 속도가 나옴
      // https://www.acmicpc.net/problem/2741
      import java.util.*;
      public class Main {
      public static void main(String args[]) {
      Scanner sc = new Scanner(System.in);
      int n = sc.nextInt();
      for (int i=1; i<=n; i++) {
        System.out.println(i); 
      }
      }
      }
  • StringBuilder를 이용한 경우

    • 156MS의 속도가 나옴
      import java.util.*;
      public class Main {
      public static void main(String args[]) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        StringBuilder sb = new StringBuilder();
        for (int i=1; i<=n; i++) {
          sb.append(i+"\n");
        }
        System.out.print(sb);
      }
      }