계산기(문자열 수식 계산하기)
계산기의 문자열 계산식을 스택을 사용하여 해결할 수 있다.
1. 중위표기식을 후위표기식으로 변환하기
- 입력받은 중위표기식에서 토큰을 읽음
- 토큰이 피연산자이면 토큰을 출력
- 토큰이 연산자(괄호포함)일 경우
- 토큰이 스택의 top에 저장된 연산자보다 우선순위가 높으면 push
- 그렇지 않다면 토큰의 우선순위가 더 높을때까지 pop한 후 토큰의 연산자를 push
- top에 연산자가 없으면 push
- 토큰이 오른쪽 괄호 ')' 일 경우
- Stack top에 왼쪽 괄호 '(' 가 올 때까지 pop
- pop한 연산자를 출력
- 중위표기식에 더 읽을 것이 없다면 중지, 더 읽을 것이 있다면 1부터 반복
- Stack 남아있는 연산자를 모두 pop하여 출력
1-1. 중위표기식을 후위표기식으로 변환하는 알고리즘 구현하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; public class Stack_중위표기법변환 { // 스택 내에서 연산자 우선순위 맵 private static Map<Character, Integer> operPriorityMap; private static Stack<Character> operStack ; static { operPriorityMap = new HashMap<>(); operPriorityMap.put('(', 0); operPriorityMap.put('+', 1); operPriorityMap.put('-', 1); operPriorityMap.put('*', 2); operPriorityMap.put('/', 2); operStack = new Stack<>(); } public static void main(String[] args) { String input = "(6+5*(2-8)/2)"; List<Character> output = new ArrayList<>(); char[] inputChars = input.toCharArray(); for(int i=0; i<inputChars.length; i++) { char c = inputChars[i]; // 연산자 if(isOperation(c)) { // 연산자 스택이 비었다면 push if(operStack.isEmpty()) { operStack.push(c); } // 연산자 우선순위 비교 else { // 닫는 괄호일경우 여는 괄호 '(' 가 나올떄까지 pop if(c == ')') { char top = ' '; do { top = operStack.pop(); if(top != '(') { output.add(top); } } while(top != '('); } // 여는 괄호는 무조건 push else if(c == '(') { operStack.push(c); } // 그외 연산자인 경우 else { int tokenPriority = operPriorityMap.get(c); char top = operStack.peek(); int topPriority = operPriorityMap.get(top); // 토큰의 우선순위가 더 높다면 스택에 추가 if(tokenPriority > topPriority) { operStack.push(c); } // 토큰의 우선순위가 더 높을때까지 pop 한 후 push else { top = operStack.pop(); output.add(top); do { if(operStack.isEmpty()) { operStack.push(c); } else { top = operStack.peek(); topPriority = operPriorityMap.get(top); if(tokenPriority > topPriority) { operStack.push(c); } else { top = operStack.pop(); output.add(top); } } } while(tokenPriority <= topPriority); } } } } // 피연산자 else { output.add(c); } } // 6528-*2/+ for(int i=0; i<output.size(); i++) { System.out.printf("%c", output.get(i)); } } /** * 연산자 여부 확인 * @param c * @return */ public static boolean isOperation(char c) { if(')' == c) { return true; } return operPriorityMap.containsKey(c); } } | cs |
2. 후위표기법의 수식을 Stack을 이용하여 계산하기
위에서 구한 후의표기법을 이용하여 계산을 수행해보자. 알고리즘은 아래와 같다.
- 피연산자를 만나면 Stack에 push
- 연산자를 만나면 필요한 만큼의 피연자를 Stack에서 pop하여 계산하고 연산 결과를 다시 Stack에 push함
피연산자의 숫자는 pop한 순서와 반대임 - 수식이 끝나면 마지막으로 Stack을 pop하여 출력
2-1. 후위표기법으로 계산 구현하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; public class Stack_후위표기법계산 { private static Stack<Integer> numberStack = new Stack<>(); public static void main(String[] args) { String input = "6528-*2/+"; char[] inputChars = input.toCharArray(); for(int i=0; i<inputChars.length; i++) { char c = inputChars[i]; // 피연산자이면 스택에 추가 if(!isOperation(c)) { int num = Integer.parseInt(new Character(c).toString()); numberStack.push(num); } else { int num1 = numberStack.pop(); int num2 = numberStack.pop(); int result = 0 ; switch(c) { case '+' : result = num2 + num1; break; case '-' : result = num2 - num1; break; case '*' : result = num2 * num1; break; case '/' : result = num2 / num1; break; } numberStack.push(result); } } int result = numberStack.pop(); System.out.println(result); // -9 } /** * 연산자 여부 확인 * @param c * @return */ public static boolean isOperation(char c) { char[] operations = {'(', ')', '*', '+', '-', '/'}; for(int i=0; i<operations.length; i++) { if(c == operations[i]) { return true; } } return false; } } | cs |
백트래킹(Backtracking)
해를 찾는 도중에 막히면(해가 아니면) 되돌아가서 다시 해를 찾아가는 기법이다. 이를 이용하여 최적화(Optimization) 문제와 결정(Desision)문제를 해결할 수 있다. 3
미로찾기 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Stack; public class Stack백트래킹_미로찾기 { private static Stack<Pos> posStack = new Stack<>(); private static Map<Pos, Boolean> visitHistory = new HashMap<>(); // 0은 벽, 1은 통로, 2는 출구 private static int[][] mazeArr = { {1, 0, 0, 0}, {1, 1, 1, 0}, {0, 1, 0, 0}, {0, 1, 1, 1}, {0, 2, 0, 0} }; // 시계방향으로 이동가능한지 확인을 위함 private static int[][] directionArr = { {1, 0}, // 우 {0, 1}, // 하 {-1, 0}, // 좌 {0, -1} // 상 }; public static void main(String[] args) { Pos now = new Pos(0, 0); posStack.push(now); do { System.out.println(now); visitHistory.put(now, true); Pos movablePos = move(now); if(movablePos != null) { posStack.push(movablePos); } else { movablePos = posStack.pop(); } now = movablePos; } while(mazeArr[now.getY()][now.getX()] != 2); } /** * 이동할 수 있는 위치 반환. 이동할 수 없다면 null 반환 * @param pos * @return */ private static Pos move(Pos pos) { int x = pos.getX(); int y = pos.getY(); // 현재위치에서 시계방향으로 이동할 수 있는 곳을 체크함 for(int i=0; i<4; i++) { int[] direction = directionArr[i]; int moveX = direction[0]; int moveY = direction[1]; // 미로 범위안에서 이동가능 체크 if((x + moveX) >= 0 && (x + moveX) <= 3 && (y + moveY) >= 0 && (y + moveY) <= 4) { if(mazeArr[y+moveY][x+moveX] == 0) { continue; } // 1(통로) 아니면 2(도착지) Pos movePos = new Pos(x+moveX, y+moveY); boolean isVisit = posStack.contains(movePos); if(!isVisit) { Boolean hasVisitHisotry = visitHistory.get(movePos); if(hasVisitHisotry == null) { return movePos; } } } } return null; } } class Pos { private int x; private int y; public Pos(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } @Override public boolean equals(Object obj) { if(obj == null || !(obj instanceof Pos)) { return false; } Pos pos = (Pos)obj; if(pos.getX() == x && pos.getY() == y) { return true; } return false; } public int hashCode() { return Objects.hash(x,y); } public String toString() { StringBuffer bf = new StringBuffer(); bf.append("x: "+x+","); bf.append("y: "+y); return bf.toString(); } } | cs |
'알고리즘 > SW Expert Academy' 카테고리의 다른 글
백트래킹 - PowerSet (0) | 2019.05.15 |
---|---|
백트래킹 - NQueen (0) | 2019.05.15 |
Stack(2) - DP(동적 계획법), DFS(깊이우선탐색) (0) | 2019.03.23 |
Stack(1) - 스택과 memorization (0) | 2019.03.19 |
문자열 (String) (0) | 2019.02.16 |