{ Java Algorithms } Stack
Stack
- 한 쪽 끝에서만 자료를 넣고 뺄 수 있는 LIFO (Last In First Out) 형식의 자료 구조
- Undo, store the previous states of your work
- Example
- A pile of books placed in a vertical order
- Stack runs perfectly for Depth First Search and Expression Evaluation Algorithm
- It is used for the actions
- To backtrack to the previous task/state, e.g., in a recursive code
- To store a partially completed task, e.g., when you are exploring two different paths on a Graph from a point while calculating the smallest path to the target.
Operations
- 스택(Stack)는 LIFO(Last In First Out) 를 따른다. 즉, 가장 최근에 스택에 추가한 항목이 가장 먼저 제거될 항목이다.
- pop(): 스택에서 가장 위에 있는 항목을 제거한다.
- push(item): item 하나를 스택의 가장 윗 부분에 추가한다.
- datatype top(): 스택의 가장 위에 있는 항목을 반환한다.
- isEmpty(): 스택이 비어 있을 때에 true를 반환한다.
- isFull: Returns true if the stack is full and false otherwise
Implementaion
public class Stack <V> {
private int maxSize;
private int top;
private V array[];
/*
Java does not allow generic type arrays. So we have used an
array of Object type and type-casted it to the generic type V.
This type-casting is unsafe and produces a warning.
*/
@SuppressWarnings("unchecked")
public Stack(int max_size) {
this.maxSize = max_size;
this.top = -1; // when stack is empty (initially)
array = (V[]) new Object[max_size];// type casting Object[] to V[]
}
// returns the maximum size capacity
public int getMaxSize() {
return maxSize;
}
// returns true if Stack is empty
public boolean isEmpty(){
return top == -1;
}
// returns true if Stack is full
public boolean isFull(){
return top == maxSize -1;
}
// returns the value at top of Stack
public V top(){
if(isEmpty())
return null;
return array[top];
}
}
Queue
- Similar to Stack, Queue is the another linear data structure that stores elements in a sequential manner.
- Stack stores elements LIFO / Queue stores elements FIFO (First In First Out)
- Queue is quite more tricky than Stack, we have to track both on elements (front and end) in an array
- Example
- Line of the people to get a ticket in front of the booth
- Store packets on routers: packets - 데이터 전송에서 송신측과 수신측에 의하여 하나의 단위로 취급되어 전송되는 집합체
- Prioritize something over another
- 다른 기기들 사이에서 공유되는 리소스 (Web Servers OR Control Units)
Operations
- enqueue: inserts element to the ‘end’ of the queue
- dequeue: removes an element from the ‘start’ of the queue
- top: returns the ‘first’ element of the queue
- isEmpty: checks if the queue is empty
- isFull: checks if the queue is full
Implementation
// 5 data members
public class Queue<V> {
private int maxSize;
private V[] array;
private int front;
private int back;
private int currentSize;
@SuppressWarning("unchecked")
public Queue(int maxSize) {
this.maxSize = maxSize;
array = (V[]) new Object[maxSize]
front = 0;
back = -1;
currentSize = 0;
}
public int getMaxSize() {
return maxSize;
}
public int getCurrentSize() {
return currentSize;
}
// Helper Functions
public boolean isEmpty() {
return currentSize == 0;
}
public boolean isFull() {
return currentSize == maxSize;
}
public V top() {
return array[front];
}
public void enqueue(V value) {
if(isFull()) return;
// to keep the index in range
back = (back + 1) % maxSize;
array[back] = value;
currentSize++;
}
public void dequeue(V value) {
if(isEmpty()) return null;
V temp = array[front];
front = (front + 1) % maxSize;
currentSize--;
return temp;
}
}
Types of Queue
Linear Queue: we discussed til now
Circular Queue
- Both ends are connected to form a circle
- Used for..
- Simulation of objects
- Event handling (do something when a particular event occurs)
Priority Queue
- The most prioritized element is stored on the front on queue
- The least prioritized element is stored on the last on queue
- Operating System: which programs should be operated first
Exercise 1
- Implement Two Stacks using one Array
// The first stack starts from the first element at index 0
// the second starts from the last element.
// Solution - Stacks on opposite ends
class TwoStacks<V> {
private int maxSize
private int top1, top2;
private V[] array;
@SuppressWarnings("unchecked")
public TwoStacks(int max_size) {
this.maxSize = max_size;
this.top1 = -1;
this.top2 = max_size;
array = (V[]) new Object[max_size];//type casting Object[] to V[]
}
// insert at top of first stack
public void push1(V value) {
if(top1 < top2 -1) {
array[++top1] = value;
}
}
// insert at top of second stack
public void push2(V value) {
if(top1 < top2 -1) {
array[--top2] = value;
}
}
// remove and return value from top of first stack
public V pop1() {
if(top1 > -1) {
return array[top1--];
}
return null;
}
// remove and return value from top of second stack
public V pop2() {
if(top2 < maxSize) {
return array[top2++];
}
return null;
}
}
class CheckTwoStacks {
public static void main(String args[]) {
TwoStacks<Integer> tS = new TwoStacks<Integer>(5);
tS.push1(11); // 11
tS.push1(3); // 11 3
tS.push1(7); // 11 3 7
tS.push2(1); // 11 3 7 1
tS.push2(9); // 11 3 7 9 1
// 11 3 7 (stack1) 9 1 (stack2)
// 스택 1 은 왼쪽에서 오른쪽으로 push
// 스택 2 는 오른쪽에서 왼쪽으로 push
// pop 은 opposite side
System.out.println(tS.pop1()); // 7
System.out.println(tS.pop2()); // 9
System.out.println(tS.pop2()); // 1
System.out.println(tS.pop2()); // null
System.out.println(tS.pop1()); // 3
}
Exercise 2
- Reversing the First k Elements of a Queue
void reverseK(Queue<V> queue, int k)
// input
Queue = {1,2,3,4,5,6,7,8,9,10}
k = 5
// output
result = {5,4,3,2,1,6,7,8,9,10}
if (queue.isEmpty() || k <= 0) return;
// declare a new stack
Stack<V> stack = new Stack<>(k)
// Push first k elements in queue into a stack
while(!stack.isFull())
stack.push(queue.dequeue()) // 1 2 3 4 5(top)
// Pop stack elements and enqueue at the end of queue
while(!stack.isEmpty())
queue.enqueue(stack.pop()) // 6 7 8 9 10 5 4 3 2 1
int size = queue.getCurrentSize();
// append them at end of the queue
for(int i = 0; i < size - k; i++)
queue.enqueue(queue.dequeue()); // 5 4 3 2 1 6 7 8 9 10
Exercise 3
- Implement queue using stacks
// implement enqueue() and dequeue()
// Using two stacks, stack1 stores the origin values
// stack2 helps stack1 to store the opposite values
Stack<V> stack1;
Stack<V> stack2;
public QueueWithStack(int maxSize) {
stack1 = new Stack<>(maxSize);
stack2 = new Stack<>(maxSize);
}
public boolean isEmpty(){
return stack1.isEmpty();
}
public void enqueue(V value){
stack1.push(value);
}
// Whenever a value is dequeued, all elements are transferred to stack2 and then back to stack1 (O(N))
public V dequeue(){
// pop all elements in stack1 and push them into stack2 (reversed)
while (!stack1.isEmpty()){
stack2.push(stack1.pop());
}
// pop from stack2 and return it
V result = stack2.pop();
// pop all elements back in stack1
while (!stack2.isEmpty()){
stack1.push(stack2.pop());
}
return result;
}
// All elements in stack1 are transferred to stack1.. (O(N)?) Why O(1)?
public V dequeue(){
// if stack2 is empty, we pop all the elements from stack1 and push them to the stack2
if (stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
// pop and return
return stack2.pop(); // O(1)
}
else{
// simply return the top of stack2
return stack2.pop(); // O(1)
}
}
Exercise 4
- Sort the Values in a Stack
void sortStack(Stack<Integer> stack)
// input
stack = {23,60,12,42,4,97,2}
// output
result = {2,4,12,23,42,60,97}
// Solution 1. Using a Temporary Stack
public static void sortStack(Stack<Integer> stack) {
// 1. use a second tempStack.
Stack<Integer> tempStack = new Stack<>(stack.getMaxSize());
// 2. pop value from mainStack.
while(!stack.isEmpty()) {
int value = stack.pop();
// 3. if the value is greater or equal to the top of tempStack, then push the value in tempStack
if(!tempStack.isEmpty() && value >= tempStack.top()) {
tempStack.push(value);
}
else {
// else pop all values from tempStack and push them in mainStack
while(!tempStack.isEmpty() && tempStack.top() > value)
stack.push(tempStack.pop());
// in the end push value in tempStack and repeat from step 2 until mainStack is not empty
tempStack.push(value);
}
}
// 4. when mainStack will be empty, tempStack will have sorted values in descending order.
// 5. now transfer values from tempStack to mainStack to make values sorted in ascending order.
while(!tempStack.isEmpty()) {
stack.push(tempStack.pop());
}
}
// Solution 2. Recursive Sort
// Time complexity is same, but you do not need to temp stack
public static void insert(Stack<Integer> stack, int value) {
if(stack.isEmpty()|| value < stack.top())
stack.push(value);
else{
int temp = stack.pop();
insert(stack,value);
stack.push(temp);
}
}
public static Stack<Integer> sortStack(Stack<Integer> stack) {
if(!stack.isEmpty()) {
int value = stack.pop();
sortStack(stack);
insert(stack,value);
}
return stack;
}
Exercise 5
- Evaluate Postfix Expressions using Stacks
- Infix and Postfix Expressions
- infix : operators like + and * appear between the operands
- postfix : operators appear after the operands / 변형방법: 괄호로 묶어서 괄호를 하나씩 지우면서 연산자를 뒤로 보냄
// input
expression = "921*-8-4+" // 9 - 2 * 1 - 8 + 4
// output
result = 3
public static int evaluatePostFix(String expression) {
Stack<Integer> stack = new Stack<>(expression.length());
// 1. scan expression character by character,
for(int i = 0; i < expression.length(); i++) {
char character = expression.charAt(i);
// 2. if character is operator, pop two elements from stack
// then perform the operation and put the result back in stack
if (!Character.isDigit(character)) {
int a = stack.pop();
int b = stack.pop();
switch (character) {
case '+' :
stack.push(b + a);
break;
case '-' :
stack.push(b - a);
break;
case '*' :
stack.push(b * a);
break;
case '/' :
stack.push(b / a);
break;
}
// 3. if character is a number, push it in stack
} else stack.push(Character.getNumericValue(character));
}
// 4. at the end, Stack will contain result of whole expression.
return stack.pop();
}
Exercise 6
- Valid Parentheses (Leetcode 20)
/**
* Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', * determine if the input string is valid.
* An input string is valid if:
* Open brackets must be closed by the same type of brackets.
* Open brackets must be closed in the correct order.
* Every close bracket has a corresponding open bracket of the same type.
*/
// 이 문제는 괄호로 이뤄진 문자열이 input 으로 주어지고
// this question is about checking of the given input is opening and closing properly in order
// if the type of open branket and closing blanket is same, return true, otherwise false is returned
// 열린 괄호가 순서대로 닫히는지 그리고 닫히는 괄호와 열리는 괄호가 같은 타입인지 체크하고
// 그렇다면 true, 그렇지 않으면 false 를 리턴하는 문제입니다
// [ {,(,[,],),} ]
// 이러한 문자열이 주어진다면 문자열을 character array 로 만들고
// loop 을 돌려서 배열의 첫번째 요소와 마지막 요소를 각각 순서대로 비교할 수 있는 data structure 를 생각해 봤을 때
// LIFO - 마지막에 들어온 요소가 첫번째로 나가는 stack 을 생각 해 볼수 있습니다
// { ( [ .. 먼저 열리는 괄호를 stack 에 푸쉬를 하고
// 다음 character 가 같은 유형의 닫히는 괄호가 나오면
// pop 맨 마지막에 들어온 열린 괄호를 없애고 ... 반복하게 되면 stack 에 들어온 열린 괄호들은 다 없어질 것
// isEmpty() 를 리턴하여 체크.. 결과 도출
public static boolean isValid(String s) {
// initialize the stack
Stack<Character> stack = new Stack<>();
// convert string into char array
char[] c = s.toCharArray();
// traverse each char
for(int i = 0; i < c.length; i++) {
// the first should be open brankets
if(c[i] == '(' || c[i] == '[' || c[i] == '{') {
// push the char
stack.push(c[i]);
// check if the closing brackets appear after the corresponing open brankets
} else if(c[i] == ')' && !stack.isEmpty() && stack.peek() == '(' ) {
stack.pop();
} else if(c[i] == ']' && !stack.isEmpty() && stack.peek() == '[' ) {
stack.pop();
} else if(c[i] == '}' && !stack.isEmpty() && stack.peek() == '{' ) {
stack.pop();
} else {
return false;
}
}
return stack.isEmpty();
}
Exercise 7
- Next Greater Element using Stack
// An array containing the next greater element of each element from the input array.
// For the maximum value in the array, the next greater value is -1.
int[] nextGreaterElement(int[] arr);
// input
arr = {4,6,3,2,8,1}
// output
result = {6,8,8,8,-1,-1}
// using stack
int[] result = new int[nums.length];
Stack<Integer> stack = new Stack<>();
// iterate through the input array in reverse order
for(int i = nums.length -1; i >= 0; i--) {
// pop from the stack until we get the greater element on top of the stack
while(!stack.empty() && nums[i] >= stack.peak()) {
stack.pop();
}
// update the value of the current index in the result array
// to the value of the top of the stack
result[i] = stack.empty() ? -1 : stack.peek();
stack.push(nums[i]);
}
return result;