1. 수식트리
: 산술식을 트리형태로 표현
: 컴파일러가 수식을 처리하는 방식
: 단말노드 => 피연산자, 비단말노드 => 연산자
2. 수식 트리 계산
중위 표기법 수식 -> 후위 표기법 수식 -> 수식 트리 -> 수식 트리 계산
- 중위 표기법 수식 -> 후위 표기법 수식 : 스택 이용
- 후위 표기법 수식 -> 수식 트리 : 스택 이용
- 수식 트리 -> 수식 트리 계산 : 후위순회 이용
2-1. 후위 표기법 수식
: 스택 이용
https://growingupis.tistory.com/32?category=1186559
스택 응용(수식 계산)
1. 수식표기법 (1) 중위표기법(infix notation) : 연산자를 피연산자의 가운데 표기하는 방법 => 우리가 사용하는 방식 ex) A+B (2) 후위표기법(postfix notation) : 연산자를 피연산자 뒤에 표기하는 방법 => 컴
growingupis.tistory.com
2-2. 수식 트리 구성
: 스택을 이용하여 후위 표기법 수식을 수식 트리로 구성
- 피연산자는 스택에 push
- 연산자를 만나면 스택에서 피연산자 두개를 pop하여 트리 구성(트리도 피연산자 취급)
- 생성된 트리는 다시 스택에 push(부모 노드의 포인터만 스택에 push하여 저장
2-3. 수식트리 이용하여 수식 계산
: 스택 이용하여 수식트리 생성하였기에 수식은 하위에서부터 연산되어야함
: 재귀호출을 이용
4. c언어로 구현
"total.h" 헤더파일
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
typedef char element;
"stack_node.h" 헤더파일
#include "total.h"
#define tSTACK_SIZE 100
#include <memory.h>
#define max(a,b) ((a>b)?(a):(b))
typedef struct _TREE_NODE_ {
element data;
struct _TREE_NODE_* left;
struct _TREE_NODE_* right;
}TREE_NODE;
TREE_NODE* root;
TREE_NODE* t_stack[tSTACK_SIZE];
int t_top;
void INIT();
int ISEMPTY();
int ISFULL();
void PUSH(TREE_NODE* item);
TREE_NODE* POP();
TREE_NODE* PEEK();
TREE_NODE* MakeNode(element item);
TREE_NODE* Merge(element item, TREE_NODE* left, TREE_NODE* right);
void LVR(TREE_NODE * node);
int HEIGHT(TREE_NODE* node);
int CountNode(TREE_NODE* node);
"stack.h" 헤더파일
#include "total.h"
#define STACK_SIZE 100
typedef struct _STACK_ {
element stack[STACK_SIZE];
int top;
}STACK;
int IsFull(STACK* st);
int IsEmpty(STACK* st);
void push(STACK* st, element item);
int peek(STACK* st);
int pop(STACK* st);
void init(STACK* st);
stack_node.c
#include "stack_node.h"
void INIT() {
memset(t_stack, 0, tSTACK_SIZE);
t_top = -1;
}
int ISEMPTY() {
return t_top == -1;
}
int ISFULL() {
return t_top == tSTACK_SIZE - 1;
}
void PUSH(TREE_NODE* item) {
if (ISFULL(t_stack)) {
printf("스택이 꽉찼습니다\n");
return;
}
else {
t_top++;
t_stack[t_top] = item;
}
}
TREE_NODE* POP() {
if (ISEMPTY(t_stack)) {
printf("스택이 비어있습니다\n");
return;
}
else {
TREE_NODE *popnode = t_stack[t_top];
t_top--;
return popnode;
}
}
TREE_NODE* PEEK() {
if (ISEMPTY(t_stack)) {
printf("스택이 비어있습니다\n");
return;
}
else {
return t_stack[t_top];
}
}
//후위표기에서 숫자인 경우
TREE_NODE * MakeNode(element item) {
TREE_NODE* newNode = (TREE_NODE*)calloc(1, sizeof(TREE_NODE));
assert(newNode != NULL);
newNode->data = item;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
//후위표기에서 연산자일때 숫자가아닌 트리를 연결하는 경우
TREE_NODE* Merge(element item, TREE_NODE* left, TREE_NODE* right) {
TREE_NODE* root = (TREE_NODE*)calloc(1, sizeof(TREE_NODE));
assert(root != NULL);
root->data = item;
root->left = left;
root->right = right;
}
void LVR(TREE_NODE * node) {
if (node == NULL) {
return;
}
LVR(node->left);
printf("%c ", node->data);
LVR(node->right);
}
int HEIGHT(TREE_NODE* node)
{
if (node == NULL) {
return 0;
}
else {
return 1 + max(HEIGHT(node->left), HEIGHT(node->right));
}
}
int CountNode(TREE_NODE* node)
{
if (node == NULL) {
return 0;
}
else {
return 1 + CountNode(node->left)+CountNode(node->right);
}
}
stack.c
#include "stack.h"
void init(STACK* st) {
st->top = -1;
memset(st->stack, 0, sizeof(st->stack));
}
int IsFull(STACK* st) {
return st->top == STACK_SIZE - 1;
}
int IsEmpty(STACK* st) {
return st->top == -1;
}
void push(STACK* st, element item) {
if (IsFull(st)) {
printf("스택이 꽉차있습니다\n");
return;
}
else {
st->top++;
st->stack[st->top] = item;
}
}
int peek(STACK* st) {
if (IsEmpty(st)) {
printf("스택이 비어있습니다\n");
return;
}
else {
return st->stack[st->top];
}
}
int pop(STACK* st) {
if (IsEmpty(st)) {
printf("스택이 비어있습니다\n");
return;
}
else {
element data = st->stack[st->top];
st->top--;
return data;
}
}
main.c
#include "stack.h"
#include "stack_node.h"
#include <stdbool.h>
#include <ctype.h>
#define STRING_SIZE 50
int IsTest(STACK *st, char * exp, int len) {
//괄호검사
for (int i = 0; i < len; i++) {
if (exp[i] == '(') {
push(st, exp[i]);
}
else if (exp[i] == ')') {
element data = pop(st);
}
}
if (!IsEmpty(st)) {
return false;
}
else {
return true;
}
}
int precedence(element item) {
switch (item) {
case '(':
case ')':
return 0;
case '+':
case '-':
return 1;
case '*':
case '/':
case '%':
return 2;
}
}
void infix_to_postfix(STACK* st, char* exp, char* res, int len) {
//중위표기를 후위표기로 (일반화)
int k = 0;
for (int i = 0; i < len; i++) {
if (exp[i] == '(') {
push(st, exp[i]);
}
else if (exp[i] == ')') {
while (!IsEmpty(st)) {
element popdata = pop(st);
if (popdata == '(') {
break;
}
else {
res[k++] = popdata;
res[k++] = ' ';
}
}
}
else if (exp[i] >= '0' && exp[i] <= '9') {
res[k++] = exp[i];
res[k++] = ' ';
}
else if (exp[i] == '+' || exp[i] == '-' || exp[i] == '*' || exp[i] == '/' || exp[i] == '%') {
while (!IsEmpty(st)) {
element peekdata = peek(st);
if (precedence(peekdata)<precedence(exp[i])) {
break;
}
else {
res[k++] = pop(st);
res[k++] = ' ';
}
}
push(st, exp[i]);
}
}
while (!IsEmpty(st)) {
res[k++] = pop(st);
res[k++] = ' ';
}
res[k] = '\0';
}
// 4 2 + 4 / 3 7 7 5 * / + -
TREE_NODE * MakeEvaluateTree(char* res, int len) {
TREE_NODE* left;
TREE_NODE* right;
for (int i = 0; i < len; i++) {
if (isdigit(res[i])) {
//숫자이면 숫자노드 생성한다
TREE_NODE * node =MakeNode(res[i]);
PUSH(node);
}
else if (res[i] == ' ') {
//공백이면 넘어간다
continue;
}
else {
//만약 4%2계산해야한다면 나중에 후위순회할거이기에 4 2 순서로 트리 생성되어야함
right = POP();
left = POP();
TREE_NODE * newNode=Merge(res[i], left, right);
PUSH(newNode);
}
}
return POP();
}
int Evaluate(TREE_NODE * node) {
if (node->left == NULL && node->right == NULL) {
return (node->data)-'0';
}
int op1 = Evaluate(node->left);
int op2 = Evaluate(node->right);
switch (node->data) {
case '+':
return op1 + op2;
break;
case '-':
return op1 - op2;
break;
case '/':
return op1 / op2;
break;
case '%':
return op1 % op2;
break;
case '*':
return op1 * op2;
break;
}
}
int main() {
char expression[] = "(((4+2)/4)-(3+(7/(7*5))))";
STACK* st;
int len = strlen(expression);
st = (STACK*)calloc(1, sizeof(STACK));
assert(st != NULL);
st->top = -1;
if(IsTest(st,expression,len)) {
printf("1. 괄호검사 통과\n\n");
init(st);
char res[STRING_SIZE] = { 0 };
infix_to_postfix(st, expression, res, len);
printf("2. 중위표기 -> 후위표기 결과 : %s\n\n", res);
printf("3. 수식트리 생성\n\n ");
INIT();
root=MakeEvaluateTree(res, strlen(res));
printf("-> 이때, LVR로 트리 출력 :");
LVR(root);
printf("\n\n");
printf("4. 수식계산 결과 : %d\n\n", Evaluate(root));
printf("5. 노드의 갯수 : %d\n\n", CountNode(root));
printf("6. 트리의 높이 : %d\n\n", HEIGHT(root));
}
else {
printf("괄호검사를 통과하지 못하였으므로 수식계산을 수행할 수 없습니다\n");
return;
}
getchar();
return 0;
}
cf> 공부하면서 알게된 것
: 트리의 높이를 코드로 구현하면서 node가 NULL인 경우엔 return 0;이 아닌 그냥 return;로 했을때 실제 트리높이+1의 값이 나와서 디버깅하면서 살펴보니 return;을 할 경우에는 max 함수를 구현하기 위해서는 값이 들어가있어야하는데 아무것도 반환하지 않으므로 값을 찾아내기 위해 한 번 더 HEIGHT 함수를 수행하게 되어서 +1이 되어 최종 결과도 실제 트리높이 +1이 된다. 따라서 return 0;을 꼭 해주자! ( 이와 마찬가지로 트리의 총 노드 갯수 구하는 코드를 구현하는 것도 동일)
int HEIGHT(TREE_NODE* node) {
if (node == NULL) {
return 0;
} else {
return 1 + max(HEIGHT(node->left), HEIGHT(node->right)); }
}
ver2)
stack.h
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #include <stdbool.h> #define TRUE 1 #define FALSE 0 typedef struct _STACK_NODE_ { int data; struct _STACK_NODE_* link; }STACK_NODE; typedef struct _STACK_HEAD_ { STACK_NODE* head; }STACK; STACK* makeStack(); void push(STACK * st, int item); int pop(STACK* st); int peek(STACK* st); int isEmpty(STACK* st); bool bracketTest(char* express); void infix_to_postfix(char* infix, char* post);
stack.c
#include "stack.h" STACK* makeStack() { STACK* st = (STACK*)calloc(1, sizeof(STACK)); assert(st != NULL); return st; } void push(STACK* st, int item) { STACK_NODE* newNode = (STACK_NODE*)calloc(1, sizeof(STACK_NODE)); assert(newNode != NULL); newNode->data = item; if (isEmpty(st)) { st->head = newNode; } else { newNode->link = st->head; st->head = newNode; } } int pop(STACK* st) { if (isEmpty(st)) { fprintf(stderr, "Stack is empty"); } else { int data = st->head->data; STACK_NODE* delNode = st->head; st->head = st->head->link; free(delNode); delNode = NULL; return data; } } int peek(STACK* st) { if (isEmpty(st)) { fprintf(stderr, "Stack is empty"); } else { return st->head->data; } } int isEmpty(STACK* st) { return st->head == NULL; } bool bracketTest(char* express) { STACK* st = makeStack(); int len = strlen(express); for (int i = 0; i < len; i++) { if (express[i] == '(') { push(st, express[i]); } else if (express[i] == ')') { if (isEmpty(st)) { return false; } else { pop(st); } } } if (isEmpty(st)) { return true; } else { return false; } } int precedence(char item) { switch (item) { case '(': case ')': return 0; case '+': case '-': return 1; case '*': case '/': case '%': return 2; } } void infix_to_postfix(char* infix, char* postfix) { STACK* st = (STACK*)calloc(1, sizeof(STACK)); assert(st != NULL); int len = strlen(infix); int k = 0; for (int i = 0; i < len; i++) { char data = infix[i]; if (data >= '0' && data <= '9') { int j = i; while (infix[j] >= '0' && infix[j] <= '9') { postfix[k++] = infix[j++]; } postfix[k++] = ' '; i = j - 1; } else if (data == '(') { push(st, data); } else if (data == ')') { while (!isEmpty(st)) { char item = pop(st); if (item == '(') { break; } else { postfix[k++] = item; postfix[k++] = ' '; } } } else if (data == '+' || data == '-' || data == '%' || data == '/' || data == '*') { while (!isEmpty(st)) { char peekdata = peek(st); if (precedence(peekdata) < precedence(data)) { break; } else { postfix[k++] = pop(st); postfix[k++] = ' '; } } push(st, data); } } while (!isEmpty(st)) { postfix[k++] = pop(st); postfix[k++] = ' '; } postfix[k] = '\0'; }
main.c
#define _CRT_SECURE_NO_WARNINGS #include "stack.h" #define MAX_STRING_SIZE 100 #define MAX_TREE_STACK_SIZE 100 typedef struct _TREE_NODE_ { int t_data; struct _TREE_NODE_* left; struct _TREE_NODE_* right; bool isdigit; }TREE_NODE; TREE_NODE* t_stack[MAX_TREE_STACK_SIZE] = { 0, }; int t_top = -1; int t_isEmpty(); int t_isFull(); void t_push(TREE_NODE* item); TREE_NODE* t_pop(); TREE_NODE* t_peek(); TREE_NODE* makeEvalTree(char* exp); TREE_NODE* makeNode(int item); void LVR(); int Eval(TREE_NODE* root); #include <memory.h> #define TRUE 1 #define FALSE 0 //#define TRAVERSE_DEBUG typedef int NodeData; typedef struct _TREENODE_ { NodeData data; struct _TREENODE_* left; struct _TREENODE_* right; }TREENODE; typedef struct _NODEMAP_ { int flag; NodeData data; }NODEMAP; int getMax(int a, int b) { return ((a > b) ? a : b); } int countNode(TREENODE* node) { if (node == NULL) return 0; return 1 + countNode(node->left) + countNode(node->right); } int getHeight(TREENODE* node) { if (node == NULL) { return 0; } else { return 1 + max(getHeight(node->left), getHeight(node->right)); } } void printGivenLevel(TREENODE* root, int level, NODEMAP* map, int pos) { if (root == NULL) { return; } if (level == 1) { map[pos].data = root->data; map[pos].flag = TRUE; } else if (level > 1) { printGivenLevel(root->left, level - 1, map, pos * 2); printGivenLevel(root->right, level - 1, map, pos * 2 + 1); } } void showTree(TREENODE* node) { int height = 0; int numOfNodes = 0; NODEMAP* treeArray = NULL; char** square = NULL; static int pos = 0; int cnt_i; int cnt_j; int position; int start, term, index; height = getHeight(node); if (height != 0) { numOfNodes = (1 << height); } treeArray = (NODEMAP*)calloc(numOfNodes, sizeof(NODEMAP)); assert(treeArray != NULL); square = (char**)calloc(height, sizeof(char*)); for (cnt_i = 0; cnt_i < height; cnt_i++) { square[cnt_i] = (char*)calloc(numOfNodes, sizeof(char)); memset(square[cnt_i], '.', numOfNodes); } pos = 1; for (cnt_i = 1; cnt_i <= height; cnt_i++) { printGivenLevel(node, cnt_i, treeArray, pos); } for (cnt_i = 1; cnt_i <= numOfNodes; cnt_i++) { treeArray[cnt_i - 1] = treeArray[cnt_i]; } pos = 0; start = (1 << (height - 1)) - 1; term = (1 << (height)); index = (1 << (height - 2)); for (cnt_i = 0; cnt_i < height; cnt_i++) { for (cnt_j = start; cnt_j < numOfNodes; cnt_j += term) { if (treeArray[pos].flag == TRUE) { if (treeArray[pos].data >= 0 && treeArray[pos].data <= 9) { square[cnt_i][cnt_j] = (treeArray[pos].data + '0'); } else { square[cnt_i][cnt_j] = (treeArray[pos].data); } } else { square[cnt_i][cnt_j] = '.'; } pos += 1; } term >>= 1; start -= index; index >>= 1; } printf("\n"); for (cnt_i = 0; cnt_i < height; cnt_i++) { for (cnt_j = 0; cnt_j < numOfNodes; cnt_j++) { printf("%c", square[cnt_i][cnt_j]); } printf("\n"); } if (treeArray != NULL) { free(treeArray); } if (square != NULL) { for (cnt_i = 0; cnt_i < height; cnt_i++) { if (square[cnt_i] != NULL) { free(square[cnt_i]); } } free(square); } } int main() { char infix_expr[] = "7+4*2-1"; //(((4+2)/4)-(3+(7/(7*5)))) if (bracketTest(infix_expr)) { printf("1. 괄호검사 통과\n\n"); char postfix_expr[MAX_STRING_SIZE] = { 0 }; infix_to_postfix(infix_expr, postfix_expr); printf("2. 중위표기 -> 후위표기 결과 : %s -> %s\n\n", infix_expr,postfix_expr); printf("3. 수식트리 생성\n\n "); TREE_NODE * root = makeEvalTree(postfix_expr); showTree(root); printf("\n"); LVR(root); printf("\n\n"); printf("4. 수식계산 결과 : %d\n\n", Eval(root)); printf("5. 노드의 갯수 : %d\n\n", countNode(root)); printf("6. 트리의 높이 : %d\n\n",getHeight(root)); } else { printf("괄호검사를 통과하지 못하였으므로 수식계산을 수행할 수 없습니다\n"); return; } getchar(); return 0; } int t_isEmpty() { return t_top == -1; } int t_isFull() { return t_top == MAX_TREE_STACK_SIZE - 1; } void t_push(TREE_NODE* item) { if (t_isFull()) { fprintf(stderr, "Stack is full"); } else { t_stack[++t_top] = item; } } TREE_NODE* t_pop() { if (t_isEmpty()) { fprintf(stderr, "Stack is empty"); } else { return t_stack[t_top--]; } } TREE_NODE* t_peek() { if (t_isEmpty()) { fprintf(stderr, "Stack is empty"); } else { return t_stack[t_top]; } } TREE_NODE* makeNode(int item) { TREE_NODE* newNode = (TREE_NODE*)calloc(1, sizeof(TREE_NODE)); assert(newNode != NULL); newNode->t_data = item; return newNode; } TREE_NODE* makeEvalTree(char* exp) { int len = strlen(exp); for (int i = 0; i < len; i++) { char data = exp[i]; if (data >= '0' && data <= '9') { int j = i + 1; int push_val = data - '0'; while (exp[j] >= '0' && exp[j] <= '9') { push_val *= 10; push_val += (exp[j] - '0'); } i = j; TREE_NODE* node = makeNode(push_val); node->isdigit = true; t_push(node); } else if (data == '+' || data == '-' || data == '*' || data == '/' || data == '%') { TREE_NODE* right = t_pop(); TREE_NODE* left = t_pop(); TREE_NODE* root = makeNode(data); root->left = left; root->right = right; t_push(root); } } return t_pop(); } void LVR(TREE_NODE* root) { if (root == NULL) { return; } else { LVR(root->left); LVR(root->right); if (root->isdigit) { printf("%d ", root->t_data); } else { printf("%c ", root->t_data); } } } int Eval(TREE_NODE* root) { if (root->left == NULL && root->right == NULL) { return root->t_data; } else { int x = Eval(root->left); int y = Eval(root->right); char op = (char)(root->t_data); switch (op) { case '+': return x + y; case '-': return x - y; case '*': return x * y; case '/': return x / y; case '%': return x % y; } } }
'C > 알고리즘' 카테고리의 다른 글
[C] AVL TREE (0) | 2023.12.10 |
---|---|
[C] Prim Alogrithm (0) | 2023.10.06 |
[C]트리 레벨 순회 - Level Order Traverse (0) | 2023.09.22 |
원형 연결 리스트 (0) | 2023.09.20 |
큐 응용 : 미로문제(Maze problem) (0) | 2023.09.19 |