본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로컴퓨터 과학 › 알고리즘 › 코딩 테스트

[BaekJoon-1918] 후위 표기식

목차

후위 표기식

문제

수식은 일반적으로 3가지 표기법으로 표현할 수 있다. 연산자가 피연산자 가운데 위치하는 중위 표기법(일반적으로 우리가 쓰는 방법이다), 연산자가 피연산자 앞에 위치하는 전위 표기법(prefix notation), 연산자가 피연산자 뒤에 위치하는 후위 표기법(postfix notation)이 그것이다. 예를 들어 중위 표기법으로 표현된 a+b는 전위 표기법으로는 +ab이고, 후위 표기법으로는 ab+가 된다.

이 문제에서 우리가 다룰 표기법은 후위 표기법이다. 후위 표기법은 위에서 말한 법과 같이 연산자가 피연산자 뒤에 위치하는 방법이다. 이 방법의 장점은 다음과 같다. 우리가 흔히 쓰는 중위 표기식 같은 경우에는 덧셈과 곱셈의 우선순위에 차이가 있어 왼쪽부터 차례로 계산할 수 없지만 후위 표기식을 사용하면 순서를 적절히 조절하여 순서를 정해줄 수 있다. 또한 같은 방법으로 괄호 등도 필요 없게 된다. 예를 들어 a+bc를 후위 표기식으로 바꾸면 abc+가 된다.

중위 표기식을 후위 표기식으로 바꾸는 방법을 간단히 설명하면 이렇다. 우선 주어진 중위 표기식을 연산자의 우선순위에 따라 괄호로 묶어준다. 그런 다음에 괄호 안의 연산자를 괄호의 오른쪽으로 옮겨주면 된다.

예를 들어 a+bc는 (a+(bc))의 식과 같게 된다. 그 다음에 안에 있는 괄호의 연산자 를 괄호 밖으로 꺼내게 되면 (a+bc)가 된다. 마지막으로 또 +를 괄호의 오른쪽으로 고치면 abc*+가 되게 된다.

다른 예를 들어 그림으로 표현하면 A+B*C-D/E를 완전하게 괄호로 묶고 연산자를 이동시킬 장소를 표시하면 다음과 같이 된다.

이러한 사실을 알고 중위 표기식이 주어졌을 때 후위 표기식으로 고치는 프로그램을 작성하시오

입력

첫째 줄에 중위 표기식이 주어진다. 단 이 수식의 피연산자는 A~Z의 문자로 이루어지며 수식에서 한 번씩만 등장한다. 그리고 -A+B와 같이 -가 가장 앞에 오거나 AB와 같이 *가 생략되는 등의 수식은 주어지지 않는다. 표기식은 알파벳 대문자와 +, -, *, /, (, )로만 이루어져 있으며, 길이는 100을 넘지 않는다.

출력

첫째 줄에 후위 표기식으로 바뀐 식을 출력하시오

예제 입력 1

A*(B+C)

예제 출력 1

ABC+*

풀이

스택을 이용해서 문제를 해결할 수 있다. 이 문제는 괄호가 있거나 연산자의 우선순위가 높을경우 라는 경우의 수가 있다.

아래 코드를 통해서 연산자 우선순위를 지정해준다.

public static int prec(char op) {
        switch (op) {
            case '(': case ')': return 0;
            case '+': case '-': return 1;
            case '*': case '/': return 2;
            default: return -1;
        }
    }

그리고 여는괄호( 를 만나면 스택에 push하고 닫는 괄호 )를 만나면 여는 괄호를 만날 때까지 pop 해준다.

연산자를 언제 출력할까

피연산자 A~Z는 순서가 바뀌지 않으므로 읽자마자 출력한다. 스택에는 아직 출력할 수 없는 연산자와 여는 괄호를 둔다. 새 연산자를 만났을 때 스택 위의 연산자가 우선순위가 같거나 높으면 먼저 출력한다. 같을 때도 꺼내야 A-B-C가 왼쪽부터 계산되는 AB-C-가 된다.

A+B*C-D/E를 따라가면 A를 출력하고 +를 보관한다. B를 출력한 뒤 *는 +보다 우선순위가 높아 그 위에 둔다. C 다음 −를 만나면 *와 +를 차례로 꺼내 ABC*+가 된다. 뒤의 D/E를 처리하면 최종 결과는 ABC*+DE/-다. 여는 괄호는 출력하지 않고, 닫는 괄호를 만나면 여는 괄호 전까지의 연산자를 모두 꺼낸다.

코드의 prec('(')=0은 일반 연산자보다 낮아서 괄호 밖 연산자가 괄호 안으로 섞이지 않게 한다. 입력은 올바른 수식이라고 주어지므로 닫는 괄호에서 여는 괄호를 찾을 수 있다. 각 문자는 스택에 최대 한 번 들어갔다 나와 O(L) 시간, O(L) 추가 공간이 든다.

소스코드

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Stack;

public class Main {


    public static void main(String[] args) throws Exception {
        convertToPostfix();
    }

    public static void convertToPostfix() throws Exception {
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String infix = in.readLine();

        Stack<Character> stack = new Stack<>();
        for (char ch : infix.toCharArray()) {
            switch (ch) {
                case '+':case '-':case '*':case '/':
                    //스택에 있는 연산자가 우선순위가 더 크거나 같으면 출력
                    while(!stack.isEmpty() && prec(stack.peek()) >= prec(ch))
                        out.write(stack.pop());
                    stack.push(ch);
                    break;
                case '(':
                    stack.push(ch);
                    break;
                case ')':
                    char op = stack.pop();
                    //왼쪽괄호를 만날 때까지 계속 출력
                    while (op != '(') {
                        out.write(op);
                        op = stack.pop();
                    }
                    break;
                default: //A~Z
                    out.write(ch);
                    break;
            }
        }
        while(!stack.isEmpty()) {
            out.write(stack.pop());
        }
        out.flush();
        out.close();
        in.close();
        ;
    }

    public static int prec(char op) {
        switch (op) {
            case '(': case ')': return 0;
            case '+': case '-': return 1;
            case '*': case '/': return 2;
            default: return -1;
        }
    }
}

문제 사이트 : https://www.acmicpc.net/problem/1918

같은 카테고리의 글