본문 바로가기
알고리즘/[EXP] 삼성 SW 역량 테스트 C형

괄호가 있는 중위 표기식 연산하기

by 피로물든딸기 2023. 7. 29.
반응형

삼성 C형 전체 링크

 

참고 

- 스택

- 괄호

- Brainf**k 인터프리터 

- 후위 표기식

- 중위 표기식 연산하기

 

중위 표기법 연산은 아래와 같다.

 

1. 숫자를 만나면 현재 숫자(cur)를 저장해둔다.

2. +, - 연산자가 나오면 temp에 현재의 숫자를 곱한다. (temp는 1로 초기화)

3. 그리고 result에 temp를 더한다.

4. *, / 가 나오면 temp에 값을 누적한다.

5. 이후 +, - 연산자가 나오면 2 ~ 3을 실행한다.

6. 모두 종료 후 남은 연산 실행

 

이때 괄호를 만나면 닫힌 괄호까지를 먼저 계산하고 temp로 변경한다.

그리고 괄호 연산이 끝난 경우다음 연산을 위해 cur = 1로 초기화 해야 한다.

괄호 내에서는 다시 중위 표기식이 될 것이기 때문에 위의 방법을 그대로 적용하여 재귀 함수로 만든다.

 

괄호가 있는 경우의 규칙을 적용하여 아래의 중위 표기식을 연산해보자.

정수 연산을 피하기 위해 나누기 / 연산은 하지 않는다. (중위 표기식 설명에서 operator는 항상 * 가 되므로 생략)

 

4 + ( 2 + ( 6 * 4 - 7 * 4 ) + 5 ) - 3

= 4 + ( 2 + ( -4 ) + 5) - 3

= 4 + ( 3 ) - 3

= 4

 

 

계산을 해본 결과 4가 나온다.

규칙대로 연산해서 4가 나오는지 확인해보자.

 

현재 숫자 cur = 0, 임시 저장 변수 temp = 1, 최종 결과 result는 0으로 초기화한다.

 

현재 숫자를 4로 갱신한다.

 

+ 를 만났으므로, temp에 현재 숫자를 곱하고 result에 누적한다.

그리고 + 연산은 temp를 1로 초기화한다.

 

첫 번째 괄호를 만났다.

이 괄호와 짝이 되는 괄호까지를 다시 연산해서 temp로 변경해야 한다.

 

이 경우 새로운 중위 표현식을 계산하는 함수가 호출되므로,

해당 함수 내의 스택에 cur, temp, result가 초기화된다.

 

2를 만났으므로 cur을 2로 바꾼다.

 

+ 를 만났으므로 temp에 현재 숫자를 곱하고, result에 누적, temp는 1로 초기화한다. 

 

두 번째 괄호를 만났다.

이 괄호와 짝이 되는 괄호까지를 다시 연산해서 temp로 변경해야 한다.

 

이 경우 새로운 중위 표현식을 계산하는 함수가 호출되므로,

해당 함수 내의 스택에 cur, temp, result가 초기화된다.

 

현재 숫자를 6으로 변경한다.

 

* 연산을 만났으므로 temp에 cur을 곱한다.

 

현재 숫자를 4로 변경한다.

 

- 연산을 만났으므로 temp에 cur을 곱하고 result에 누적한다.

그리고 - 연산은 temp를 -1로 초기화한다.

 

현재 숫자를 7로 갱신한다.

 

* 연산을 만났으므로 temp에 cur을 곱한다.

 

현재 숫자를 4로 갱신한다. 

 

두 번째 괄호가 종료되었으므로 남아있는 연산을 처리한다.

두 번째 괄호의 중위 표기식은 -4가 된다.

 

첫 번째 괄호로 다시 돌아가면 아래와 같이 괄호가 통째로 temp = -4로 교체된다.

 

괄호 연산이 종료되면 cur을 1로 초기화해야 한다.

 

cur을 1로 초기화했기 때문에 다음 연산자를 만났을 때 기존의 규칙을 그대로 적용해서 result를 갱신할 수 있다. 

cur을 1로 초기화하지 않으면 이전에 있던 cur = 2가 곱해져서 엉뚱한 값이 계산되기 때문이다.

 

현재 숫자를 5로 바꾼다.

 

첫 번째 괄호가 종료되었다.

남아있는 연산을 하면 3이 된다.

 

결국 (두 번째가 포함된) 첫 번째 괄호의 연산 결과는 3이므로 식이 아래와 같이 변경된다.

temp = 3으로 교체된다.

 

괄호 연산이 종료되었으므로 cur을 1로 바꾼다.

 

- 를 만났으므로 temp에 cur을 곱하고 result에 누적한다.

- 연산이므로 temp = -1로 초기화한다.

 

현재 숫자를 3으로 변경한다.

 

for 문이 종료되었으므로 남아있는 연산을 처리한다.

 

4 + ( 2 + ( 6 * 4 - 7 * 4 ) + 5 ) - 3 = 4 가 나오게 되었다.

 

위와 같은 방법으로 다음 예시들도 똑같이 계산해보면 아래와 같이 답이 나온다.

4 + ( 2 + ( 6 * 4 - 7 * 4 ) + 5 ) - 3 = 4
1 + ( 5 - 7 - 1 ) * ( 3 - 7 + ( 3 - 4 + 1 ) ) - 5 = 8
4 * 1 * 7 + ( 6 * 6 - 7 * 7 + 3 * 5 - 4 * 3 * 7 - 4 - 9 - 2 - 9 ) = -78
6 * 6 - 2 - 9 * 7 * 3 - 6 + 6 - 7 + 9 + ( 8 * 4 - 6 + 8 - 7 * 9 ) = -182
8 - 3 + 8 - 6 - 4 + 2 - 4 * 1 + 4 - 2 * 5 - 6 * 2 * 7 * 8 - 3 = -680
4 + ( 1 + 2 * 7 - 7 - 5 + ( 7 - 2 + 3 - 5 - 4 + 6 * 4 - 8 - 7 * 4 ) ) = -6
7 + 5 * 5 * 2 * 8 - 5 * 6 * 7 + ( 9 - 7 - 3 * 4 - 6 + 2 * 8 + ( 8 ) ) = 205
2 * 8 + 3 + 5 + 4 * 7 - 2 - 8 * 5 - 3 - 7 - 9 * 8 - 8 + ( 7 - 4 ) = -77
3 - 9 + 6 + 6 + 4 - 2 - 3 - 3 - 6 + ( 2 + ( 6 - 2 + 4 + 4 - 3 * 6 ) ) = -8
8 + 5 + 3 + ( 3 - 1 * 8 + ( 3 * 9 * 7 - 4 * 7 * 1 + 5 - 1 + ( 5 - 8 ) ) ) = 173

구현

 

구현의 편의상 다음과 같은 중위 표기법이 주어진다고 가정한다.

 

1. 중위 표기법에서 나눗셈 연산이 들어오지 않는다.

2. 모든 숫자는 1 ~ 9 사이로만 주어진다. 

ㄴ 10 이상의 숫자라면 string을 parsing하여 숫자로 만드는 과정을 추가한다.

 

정답을 검증하기 위해 수식과 답을 아래와 같이 저장한다.

char formula[10][100]
	= {
	"4 + ( 2 + ( 6 * 4 - 7 * 4 ) + 5 ) - 3", // = 4
	"1 + ( 5 - 7 - 1 ) * ( 3 - 7 + ( 3 - 4 + 1 ) ) - 5", // = 8
	"4 * 1 * 7 + ( 6 * 6 - 7 * 7 + 3 * 5 - 4 * 3 * 7 - 4 - 9 - 2 - 9 )", // = -78
	"6 * 6 - 2 - 9 * 7 * 3 - 6 + 6 - 7 + 9 + ( 8 * 4 - 6 + 8 - 7 * 9 )", // = -182
	"8 - 3 + 8 - 6 - 4 + 2 - 4 * 1 + 4 - 2 * 5 - 6 * 2 * 7 * 8 - 3", // = -680
	"4 + ( 1 + 2 * 7 - 7 - 5 + ( 7 - 2 + 3 - 5 - 4 + 6 * 4 - 8 - 7 * 4 ) )", // = -6
	"7 + 5 * 5 * 2 * 8 - 5 * 6 * 7 + ( 9 - 7 - 3 * 4 - 6 + 2 * 8 + ( 8 ) )", // = 205
	"2 * 8 + 3 + 5 + 4 * 7 - 2 - 8 * 5 - 3 - 7 - 9 * 8 - 8 + ( 7 - 4 )", // = -77
	"3 - 9 + 6 + 6 + 4 - 2 - 3 - 3 - 6 + ( 2 + ( 6 - 2 + 4 + 4 - 3 * 6 ) )", // = -8
	"8 + 5 + 3 + ( 3 - 1 * 8 + ( 3 * 9 * 7 - 4 * 7 * 1 + 5 - 1 + ( 5 - 8 ) ) )", // = 173
};

int answer[] = { 4, 8, -78, -182, -680, -6, 205, -77, -8, 173 };

 

연산을 편하게 하기 위해 '0' ~ '9'는 0 ~ 9로 바꾼다.

그리고 열린 괄호 ( 를 만나면 스택에 추가하고 닫힌 괄호 ) 를 만나면 pair에 기록하고 스택에서 제거한다. 

(참고: Brainf**k 인터프리터)

for (int i = 0; testFormula[i]; i++)
{
	if ('0' <= testFormula[i] && testFormula[i] <= '9') testFormula[i] -= '0';
	else if (testFormula[i] == '(') stack[sp++] = i;
	else if (testFormula[i] == ')')
	{
		pair[i] = stack[sp - 1];
		pair[stack[sp - 1]] = i;
		sp--;
	}
}

 

formula에 대한 정보를 모두 저장하였다면 중위 표기법을 계산한다. 

int ret = calculateInfix(testFormula, 0, len);
char ox = (ret == answer[tc]) ? 'o' : 'x';

printf("[tc :%02d]\ncal: %d\nanswer : %d (%c)\n\n", tc + 1, ret, answer[tc], ox);

 

위에서 설명한 규칙을 그대로 적용하면 아래와 같이 구현할 수 있다.

int calculateInfix(char formula[], int start, int end)
{
	int result = 0;
	int temp = 1;
	int cur = 0;

	for (int i = start; i < end; i++)
	{
		if (formula[i] == ' ') continue;

		// 1. 숫자를 만나면 현재 숫자(cur)를 저장해둔다.
		if (0 <= formula[i] && formula[i] <= 9) 
		{
			cur = formula[i];
		}
		// 괄호를 만나면 닫힌 괄호까지 계산해서 temp로 변경, cur은 1로 초기화
		else if (formula[i] == '(') 
		{
			int bracket = calculateInfix(formula, i + 1, pair[i]);

			temp *= bracket;

			i = pair[i];

			cur = 1;
		}
		// temp에 누적, result에 누적, temp 초기화
		else if (formula[i] == '+' || formula[i] == '-')
		{
			temp *= cur;

			result = result + temp;

			temp = (formula[i] == '+') ? 1 : -1;
		}
		// temp에 누적
		else if (formula[i] == '*')
		{
			temp *= cur;
		}
	}

	temp *= cur;

	result = result + temp;

	return result;
}

 

 

전체 코드는 다음과 같다.

#include <stdio.h>

int stack[100];
int sp;
int pair[100];

int calculateInfix(char formula[], int start, int end)
{
	int result = 0;
	int temp = 1;
	int cur = 0;

	for (int i = start; i < end; i++)
	{
		if (formula[i] == ' ') continue;

		// 1. 숫자를 만나면 현재 숫자(cur)를 저장해둔다.
		if (0 <= formula[i] && formula[i] <= 9) 
		{
			cur = formula[i];
		}
		// 괄호를 만나면 닫힌 괄호까지 계산해서 temp로 변경, cur은 1로 초기화
		else if (formula[i] == '(') 
		{
			int bracket = calculateInfix(formula, i + 1, pair[i]);

			temp *= bracket;

			i = pair[i];

			cur = 1;
		}
		// temp에 누적, result에 누적, temp 초기화
		else if (formula[i] == '+' || formula[i] == '-')
		{
			temp *= cur;

			result = result + temp;

			temp = (formula[i] == '+') ? 1 : -1;
		}
		// temp에 누적
		else if (formula[i] == '*')
		{
			temp *= cur;
		}
	}

	temp *= cur;

	result = result + temp;

	return result;
}

int main(void)
{
	char formula[10][100]
		= {
		"4 + ( 2 + ( 6 * 4 - 7 * 4 ) + 5 ) - 3", // = 4
		"1 + ( 5 - 7 - 1 ) * ( 3 - 7 + ( 3 - 4 + 1 ) ) - 5", // = 8
		"4 * 1 * 7 + ( 6 * 6 - 7 * 7 + 3 * 5 - 4 * 3 * 7 - 4 - 9 - 2 - 9 )", // = -78
		"6 * 6 - 2 - 9 * 7 * 3 - 6 + 6 - 7 + 9 + ( 8 * 4 - 6 + 8 - 7 * 9 )", // = -182
		"8 - 3 + 8 - 6 - 4 + 2 - 4 * 1 + 4 - 2 * 5 - 6 * 2 * 7 * 8 - 3", // = -680
		"4 + ( 1 + 2 * 7 - 7 - 5 + ( 7 - 2 + 3 - 5 - 4 + 6 * 4 - 8 - 7 * 4 ) )", // = -6
		"7 + 5 * 5 * 2 * 8 - 5 * 6 * 7 + ( 9 - 7 - 3 * 4 - 6 + 2 * 8 + ( 8 ) )", // = 205
		"2 * 8 + 3 + 5 + 4 * 7 - 2 - 8 * 5 - 3 - 7 - 9 * 8 - 8 + ( 7 - 4 )", // = -77
		"3 - 9 + 6 + 6 + 4 - 2 - 3 - 3 - 6 + ( 2 + ( 6 - 2 + 4 + 4 - 3 * 6 ) )", // = -8
		"8 + 5 + 3 + ( 3 - 1 * 8 + ( 3 * 9 * 7 - 4 * 7 * 1 + 5 - 1 + ( 5 - 8 ) ) )", // = 173
	};

	int answer[] = { 4, 8, -78, -182, -680, -6, 205, -77, -8, 173 };

	for (int tc = 0; tc < 10; tc++)
	{
		int len;
		char* testFormula = formula[tc];
		for (len = 0; testFormula[len]; len++);

		for (int i = 0; testFormula[i]; i++)
		{
			if ('0' <= testFormula[i] && testFormula[i] <= '9') testFormula[i] -= '0';
			else if (testFormula[i] == '(') stack[sp++] = i;
			else if (testFormula[i] == ')')
			{
				pair[i] = stack[sp - 1];
				pair[stack[sp - 1]] = i;
				sp--;
			}
		}

		int ret = calculateInfix(testFormula, 0, len);
		char ox = (ret == answer[tc]) ? 'o' : 'x';

		printf("[tc :%02d]\ncal: %d\nanswer : %d (%c)\n\n", tc + 1, ret, answer[tc], ox);
	}

	return 0;
}
반응형

댓글