공구일 2025. 4. 6. 16:22
728x90

🔍자바 : 28278번 . 스택2

 

01. 코드 기획 단계 

- 다양한 스택 명령어를 사용해볼 수 있는 예제입니다.

 

02. 코드 실행 및 오류 잡기

Scanner

import java.util.*;

class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        Stack<Integer> stack = new Stack<>();
        int num = sc.nextInt();
        
        for(int i = 0; i < num; i++){
            int command = sc.nextInt();
            if(command == 1){ //push
                int value = sc.nextInt();
                stack.push(value);
            } else if(command == 2){ //pop 출력
                if(stack.isEmpty()) System.out.println(-1);
                else System.out.println(stack.pop());
            } else if(command == 3){ //개수 출력
                System.out.println(stack.size());
            } else if(command == 4){ //비어있는지 확인
                if(stack.isEmpty()) System.out.println(1);
                else System.out.println(0);
            } else if(command == 5){ //peek 출력
                if(stack.isEmpty()) System.out.println(-1);
                else System.out.println(stack.peek());
            }
        }
    }
}

 

- 코드에서 오류는 발생하지 않았지만 시간이 초과되어 더 빠르게 값을 받기 위해 아래의 방식으로 성능 최적화를 했습니다.

 BufferedReader

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

class Main{
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        
        Stack<Integer> stack = new Stack<>();
        int num = Integer.parseInt(br.readLine());
        
        for(int i = 0; i < num; i++){
            st = new StringTokenizer(br.readLine());
            int command = Integer.parseInt(st.nextToken());
            
            if(command == 1){ //push
                int value = Integer.parseInt(st.nextToken());
                stack.push(value);
            } else if(command == 2){ //pop 출력
                if(stack.isEmpty()) System.out.println(-1);
                else System.out.println(stack.pop());
            } else if(command == 3){ //개수 출력
                System.out.println(stack.size());
            } else if(command == 4){ //비어있는지 확인
                if(stack.isEmpty()) System.out.println(1);
                else System.out.println(0);
            } else if(command == 5){ //peek 출력
                if(stack.isEmpty()) System.out.println(-1);
                else System.out.println(stack.peek());
            }
        }
    }
}

 

03. 정리

- Scanner는 데이터를 읽을 때 내부적으로 정규 표현식을 사용하므로, 구분자를 기준으로 데이터를 파싱하는 과정을 거쳐 추가적인 연산이 필요합니다. 그에 반해 BufferedReader는 버퍼링된 방식으로 데이터를 문자 단위로 처리하기 때문에 Scanner에 비해 더 간단하고 빠릅니다.

 

- 파싱(parsing)과 스트림(stream) : 

• 파싱은 데이터를 분석하고, 그 안에 포함된 정보나 구조를 추출하는 과정을 의미합니다. 데이터나 문자열을 파싱하는 이유는 우리가 사용할 수 있는 형태로 변환하기 위해서입니다. (ex) split(",") -> 구분자를 통해 데이터를 배열에 저장하는 파싱 과정 !

• 스트림은 데이터가 흐르는 경로를 의미합니다. 바이트 스트림(Byte Stream)에는 데이터를 읽어오는 경로인 입력 스트림(Input Stream)과 데이터를 출력해오는 경로인 출력 스트림(Output Stream)이 있으며, 주로 비디오, 이미지와 같은 바이너리 데이터를 처리할 때 유리합니다. 문자 스트림에는 Reader와 Writer이 있으며, 데이터를 문자 단위로 읽고 쓸 때 사용합니다. 자동으로 바이트를 문자로 변환해주므로 텍스트 처리에 유리합니다.

FileInputStream fis = new FileInputStream("example.txt");
int data = fis.read();  // 파일에서 한 바이트씩 읽음
fis.close();
//------------------------------------------------------------
FileReader fr = new FileReader("example.txt");
int data = fr.read();  // 파일에서 문자 단위로 읽음
fr.close();

 

- InputStreamReader는 바이트 스트림을 문자 스트림으로 변환해주는 클래스로, 바이트 스트림인 System.in, FileInputStream등과 함께 사용됩니다.( Scanner는 입력 스트림을 처리하는 클래스로, 바이트 스트림을 문자 스트림으로 변환하는 것이 아닌 바이트 스트림을 처리하여 문자열을 추출하는 기능을 제공합니다. 물론, 내부적으로는 문자 스트림을 사용하여 데이터를 처리하지만, 형식화된 데이터를 읽고 파싱하는데 중점을 둡니다. )

 

- BufferdReader는 문자 단위로 데이터를 읽는 클래스로, 문자를 버퍼링하여 효율적으로 한 번에 많은 문자를 읽어옵니다. 이 때 InputStreamReader을 이용하는 이유가 BufferdReader는 문자 단위로 읽을 수 있는 스트림이 있어야하므로 바이트 스트림 -> 문자 스트림으로 변환 후 그 문자 스트림을 효율적으로 읽기 위해 사용됩니다. 주요 메서드에는 한 줄을 읽는 readLine()과 문자 하나를 읽는 read() 등이 있습니다.

 

- StringTokenizer는 문자열을 특정 구분자를 기준으로 토큰으로 나누는 역할을 합니다. 구분자는 보통 공백, 탭, 줄바꿈을 사용합니다. 메서드 중 nextToken()의 경우 호출할 때 마다 다음 토큰을 가져옵니다. 예를 들어 1 3이 입력됐을 때 st.nextToken()을 처음 쓰면 1이 st.nextToken() 한 번 더 쓰면 3이 반환되는 것입니다. 만약 구분자를 지정하고 싶으면 st = new StringTokenizer(br.readLine(), ",") <- 이런식으로 지정해주면 됩니다.

 

- 자바에서는 체크된 예외(Checked Exception)과 비체크 예외(Unchecked Exception)이 존재합니다.

체크된 예외는 예외를 발생할 가능성이 있는 코드를 사용할 때, 반드시 처리하거나 명시해야합니다. 대표적인 체크된 예외에는 IOException가 있으며 이 예외가 발생할 수 있는 메서드를 사용할 때는 예외처리를 꼭 해줘야합니다.

• 비체크된 예외는 컴파일 시점에서 예외 처리를 강제로 요구하지는 않습니다. 하지만 런타임에서 발생할 수 있고, 예시로는 NullPointer

-Exception나 ArrayIndexOutOfBoundsException 등이 있습니다.

 

- throw IOException는 예외를 명시적으로 발생시키는 코드로, 예외를 처리하지 않았을 때 발생하는 문제를 피하기 위함입니다. BufferedReader를 사용할 때 readLine() 메서드는 IOException을 발생시킬 수 있고 IOException는 위에 설명됐듯이 체크된 예외이므로 반드시 명시해야 컴파일 오류가 발생하지 않습니다.

 

 

- Stack 컬렉션에서 사용되는 주요 메서드

push(값) : 최상위에 값이 들어갑니다.

pop() : 최상위값을 제거하면서 반환합니다.

push() : 최상위값을 제거하지 않고 반환만 합니다.

isEmpty() : 스택이 비어있을 때 true를 반환합니다.

728x90