[그림 1] 스택의 개념과 원리
[그림 2] 스택에서 행하는 작업의 예
stack[] <- 스택의 원소들이 저장되는 배열
topIndex <- 스택 탑 원소 자리의 인덱스
push(x) <- 스택의 맨위에 있는 원소x를 삽입한다.
pop() <- 스택의 맨위에 있는 원소를 알려주고 삭제한다.
top() <- 스택의 맨위에 있는 원소를 알려준다.
isEmpty() <- 스택이 비었는지 알려준다.
popAll() <- 스택을 정리한다.
topIndex++ //-> 탑 원소 자리의 인덱스를 늘려준다.
stack[topIndex] = x //스택의 topIndex자리에 새 원소를 저장하는 것
원소를 삽입하는 코드의 핵심은 먼저 topIndex를 1 증가시킨 다음 그 자리에 새 원소를 저장한다.
만약 topIndex = stack.length-1인 경우 배열이 꽉 차있어서 더 이상 들어갈 공간이 없다.
알고리즘은 다음과 같다.
push(x):
if(isFull()) //만약 꽉 차있다면?
// 에러처리
else // 꽉 차있지 않다면?
topIndex++
stack[topIndex] = x
topItem = stack[topIndex]
topIndex--
retrun topItem
원소를 삭제하는 코드의 핵심은 먼저 topIndex의 원소를 리턴하면서 topIndex를 1 감소시키는 것이다.
만약 topIndex가 -1인 경우 스택이 비어있는 상태이다.
스택에서 원소를 삭제하는 알고리즘은 다음과 같다.
pop():
if(isEmpty()) //만약 비어있다면?
// 에러처리
else // 비어있지 않다면?
topItem = stack[topIndex]
topIndex--
retrun topItem
top():
if(isEmpty()) // 만약 비어있다면?
// 에러처리
else
return stack[topIndex] //topIndex의 내용을 반환한다.
isFull():
//topIndex가 stack의 길이-1만큼 있는 경우 (배열은 0부터 인덱스가 시작한다.)
if(topIndex = stack.length-1)
return true //꽉 차있다.
else
return false //비어있다.
isEmpty():
if(topIndex = -1)
return true
else
return false
popAll():
topindex = -1
stack = newObject[stack.length] //새로운 배열로 교체
// StackInterface.java
package stack
public interface StackInterface<E>{
public void push(E newItem);
public E pop();
public E top();
public boolean isEmpty();
public void popAll();
}
//ArrayStack.java
package stack;
public class ArrayStack<E> implements StackInterface<E> {
private E stack[];
private int topIndex; // 스택 탑 인덱스
private static final int DEFAULT_CAPACITY = 64;
private final E ERROR = null; //임의의 에러 값
}
public ArrayStack() { //생성자 1
stack = (E()) new Object[DEFAULT_CAPACITY];
topIndex = -1;
}
public ArrayStack(int n) { //생성자 2
stack = (E()) new Object[n];
topIndex = -1;
}
// 스택에 원소 x 삽입하기
public void push(E newItem) {
if(isFull()) {/* 에러 처리 */}
else stack[++topIndex] = newItem;
}
//스택에서 원소 삭제하기
public E pop() {
if(isEmpty()) return ERROR;
else return stack[topIndex]
}
// 스택 탑 원소 알려주기
public E top() {
if(isEmpty()) return ERROR;
else return stack[topIndex];
}
// 스택이 꽉 찼는지 확인하기
public boolean isEmpty() {
return (topIndex < 0);
}
// 스택이 비었는지 확인하기
public boolean isFull() {
return (topIndex == stack.length-1)
}
// 스택 비우기
public void popAll() {
stack = (E[]) new Object[stack.length];
topIndex = -1;
}
ArrayStack<Integer> s = new ArrayStack<>(10);
s.push(300);
s.push(150);
s.pop();
여기서 ArrayStack 클래스의 생성자는 1번과 2번 총 2개이다.
1번의 경우는 배열의 크기가 주어지지 않은 경우
즉, 파라미터가 없이 호출되면 기본으로 주어진 상수 크기의 배열을 할당 받고,
위 사용 예의 경우 지정된 크기(10)만큼 배열을 할당 받는다.
위 클래스 구현에서는 popAll()에서 새로운 배열이 스택 역할을 하게 했다.
불필요할지 모르나 이건 후에 새로운 피드를 통해 따로 내용을 다루고자 한다.
//LinkedStack.java
public class LinkedStack<E> implements StackInterface<E> {
private Node<E> topNode;
private final E ERROR = null; // 임의의 에러값
public LinkedStack() {
topNode = null;
}
// 스택에 원소 x 삽입하기
public void push(E newItem) {
topNode = new Node<>(newItem,topNode);
}
// 스택에서 원소 삭제하기
public E pop() {
if (isEmpty()) return ERROR;
else {
Node<E> temp = topNode;
topNode = topNode.next;
return temp.item;
}
}
// 스택에서 탑 원소 알려주기
public E top() {
if (isEmpty()) return ERROR;
else return topNode.item;
}
//스택이 비었는지 확인하기
public boolean isEmpty() {
return (topNode == null);
}
//스택 비우기
public void popAll() {
topNode = null;
}
}
package stack;
public class ReverseString {
public static void main(String[] args) {
String input = "Test Seq 12345";
String t = reverse(input);
System.out.println("Input string: " + input);
System.out.println("Reversed string: " + t);
}
private static String reverse(String s) {
ArrayStack<Character> st = new ArrayStack<>(s.length());
for (int i = 0; i < s.length(); i++)
st.push(s.charAt(i););
String output = "";
while (!st.isEmpty())
output = output + st.pop();
return output;
}
}
package stack;
public class PostfixEval {
public static void main(String[] args) {
String postfix = "700 3 47 + 6 * - 4 /";
System.out.println("Input string: " + postfix);
int answer = evaluate(postfix);
System.out.println("Answer: " + answer);
}
public static int evaluate(String postfix) {
int A,B
LinkedStack<Integer> s = new LinkedStack<>();
boolean digitPreviously = false;
for (int i = 0; i < postfix.length(); i++;) {
char ch = postfix.charAt(i)
if(Character.isDigit(ch)) {
if (digitPreviously = true;) {
int tmp = s.pop();
tmp = 10*tmp + (ch - '0');
s.push(tmp);
} else s.push(ch - '0')
digitPreviously = true;
} else if(isOperator(ch)) {
A = s.pop();
B = s.pop();
int val = operation(A,B,ch);
s.push(val)
digitPreviously = false;
} else digitPreviously = false;
}
return s.pop();
}
public static int operation(char ch, int a, int b) {
int val = 0;
switch (ch) {
case '*':
val = b*a;
break;
case '/':
val = b/a;
break;
case '+'
val = b+a;
break;
case '-':
val = b-a;
break;
}
}
public static boolean isOperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
}
그림 1, 2는 쉽게 배우는 자료구조 with 자바에서 참조했습니다.